jmekaelthas 9 years ago
commit fa3ea41a8d
  1. 2
      common.gradle
  2. 1
      gradle.properties
  3. 2
      jme3-android/src/main/java/com/jme3/input/android/AndroidTouchInput.java
  4. 1
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/meshes/Face.java
  5. 3
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/meshes/MeshHelper.java
  6. 10
      jme3-bullet/src/common/java/com/jme3/bullet/control/AbstractPhysicsControl.java
  7. 13
      jme3-bullet/src/common/java/com/jme3/bullet/control/BetterCharacterControl.java
  8. 28
      jme3-bullet/src/common/java/com/jme3/bullet/control/CharacterControl.java
  9. 24
      jme3-bullet/src/common/java/com/jme3/bullet/control/GhostControl.java
  10. 16
      jme3-bullet/src/common/java/com/jme3/bullet/control/KinematicRagdollControl.java
  11. 38
      jme3-bullet/src/common/java/com/jme3/bullet/control/RigidBodyControl.java
  12. 62
      jme3-bullet/src/common/java/com/jme3/bullet/control/VehicleControl.java
  13. 4
      jme3-bullet/src/main/java/com/jme3/bullet/collision/shapes/ConeCollisionShape.java
  14. 32
      jme3-core/src/main/java/com/jme3/animation/AnimControl.java
  15. 31
      jme3-core/src/main/java/com/jme3/animation/Animation.java
  16. 23
      jme3-core/src/main/java/com/jme3/animation/AudioTrack.java
  17. 18
      jme3-core/src/main/java/com/jme3/animation/Bone.java
  18. 3
      jme3-core/src/main/java/com/jme3/animation/ClonableTrack.java
  19. 34
      jme3-core/src/main/java/com/jme3/animation/EffectTrack.java
  20. 13
      jme3-core/src/main/java/com/jme3/animation/Skeleton.java
  21. 28
      jme3-core/src/main/java/com/jme3/animation/SkeletonControl.java
  22. 18
      jme3-core/src/main/java/com/jme3/animation/TrackInfo.java
  23. 15
      jme3-core/src/main/java/com/jme3/app/StatsView.java
  24. 20
      jme3-core/src/main/java/com/jme3/app/state/ScreenshotAppState.java
  25. 2
      jme3-core/src/main/java/com/jme3/asset/AssetKey.java
  26. 38
      jme3-core/src/main/java/com/jme3/audio/AudioNode.java
  27. 30
      jme3-core/src/main/java/com/jme3/cinematic/events/MotionEvent.java
  28. 19
      jme3-core/src/main/java/com/jme3/effect/ParticleEmitter.java
  29. 22
      jme3-core/src/main/java/com/jme3/input/ChaseCamera.java
  30. 35
      jme3-core/src/main/java/com/jme3/material/MatParam.java
  31. 2
      jme3-core/src/main/java/com/jme3/post/FilterPostProcessor.java
  32. 123
      jme3-core/src/main/java/com/jme3/scene/BatchNode.java
  33. 2
      jme3-core/src/main/java/com/jme3/scene/Geometry.java
  34. 2
      jme3-core/src/main/java/com/jme3/scene/GeometryGroupNode.java
  35. 18
      jme3-core/src/main/java/com/jme3/scene/control/AbstractControl.java
  36. 13
      jme3-core/src/main/java/com/jme3/scene/control/BillboardControl.java
  37. 15
      jme3-core/src/main/java/com/jme3/scene/control/CameraControl.java
  38. 15
      jme3-core/src/main/java/com/jme3/scene/control/LightControl.java
  39. 13
      jme3-core/src/main/java/com/jme3/scene/control/LodControl.java
  40. 14
      jme3-core/src/main/java/com/jme3/scene/control/UpdateControl.java
  41. 20
      jme3-core/src/main/java/com/jme3/scene/instancing/InstancedNode.java
  42. 3
      jme3-core/src/main/java/com/jme3/scene/shape/Cylinder.java
  43. 47
      jme3-core/src/main/java/com/jme3/shadow/AbstractShadowFilter.java
  44. 103
      jme3-core/src/main/java/com/jme3/shadow/AbstractShadowRenderer.java
  45. 2
      jme3-core/src/main/java/com/jme3/shadow/DirectionalLightShadowRenderer.java
  46. 2
      jme3-core/src/main/java/com/jme3/system/JmeSystemDelegate.java
  47. 14
      jme3-core/src/main/java/com/jme3/util/BufferUtils.java
  48. 81
      jme3-core/src/main/java/com/jme3/util/clone/CloneFunction.java
  49. 348
      jme3-core/src/main/java/com/jme3/util/clone/Cloner.java
  50. 39
      jme3-core/src/main/java/com/jme3/util/clone/IdentityCloneFunction.java
  51. 99
      jme3-core/src/main/java/com/jme3/util/clone/JmeCloneable.java
  52. 45
      jme3-core/src/main/java/com/jme3/util/clone/ListCloneFunction.java
  53. 97
      jme3-core/src/main/java/com/jme3/util/mikktspace/MikkTSpaceContext.java
  54. 100
      jme3-core/src/main/java/com/jme3/util/mikktspace/MikkTSpaceImpl.java
  55. 1717
      jme3-core/src/main/java/com/jme3/util/mikktspace/MikktspaceTangentGenerator.java
  56. 17
      jme3-core/src/main/resources/Common/MatDefs/Light/Lighting.j3md
  57. 8
      jme3-core/src/main/resources/Common/MatDefs/Misc/Unshaded.j3md
  58. 12
      jme3-core/src/main/resources/Common/MatDefs/Shadow/PostShadow.frag
  59. 8
      jme3-core/src/main/resources/Common/MatDefs/Shadow/PostShadow.j3md
  60. 32
      jme3-core/src/main/resources/Common/MatDefs/Shadow/PostShadow.vert
  61. 80
      jme3-core/src/main/resources/Common/MatDefs/Shadow/PostShadow15.frag
  62. 82
      jme3-core/src/main/resources/Common/MatDefs/Shadow/PostShadow15.vert
  63. 29
      jme3-core/src/main/resources/Common/MatDefs/Shadow/PostShadowFilter.frag
  64. 6
      jme3-core/src/main/resources/Common/MatDefs/Shadow/PostShadowFilter.j3md
  65. 40
      jme3-core/src/main/resources/Common/MatDefs/Shadow/PostShadowFilter15.frag
  66. 203
      jme3-core/src/main/resources/Common/ShaderLib/Shadows.glsllib
  67. 242
      jme3-core/src/main/resources/Common/ShaderLib/Shadows15.glsllib
  68. 23
      jme3-core/src/plugins/java/com/jme3/material/plugins/J3MLoader.java
  69. 2
      jme3-core/src/test/java/com/jme3/material/plugins/J3MLoaderTest.java
  70. 3
      jme3-core/src/test/resources/texture-parameters-newstyle.j3m
  71. 4
      jme3-effects/src/main/resources/Common/MatDefs/SSAO/normal.vert
  72. 4
      jme3-examples/build.gradle
  73. 4
      jme3-examples/src/main/java/jme3test/TestChooser.java
  74. 367
      jme3-examples/src/main/java/jme3test/app/TestCloner.java
  75. 15
      jme3-examples/src/main/java/jme3test/bullet/PhysicsHoverControl.java
  76. 39
      jme3-examples/src/main/java/jme3test/light/TestDirectionalLightShadow.java
  77. 25
      jme3-examples/src/main/java/jme3test/light/TestPointLightShadows.java
  78. 12
      jme3-examples/src/main/java/jme3test/light/TestPssmShadow.java
  79. 2
      jme3-examples/src/main/java/jme3test/light/TestSpotLight.java
  80. 7
      jme3-examples/src/main/java/jme3test/light/TestSpotLightShadows.java
  81. 2
      jme3-examples/src/main/java/jme3test/light/TestSpotLightTerrain.java
  82. 14
      jme3-examples/src/main/java/jme3test/network/TestChatClient.java
  83. 21
      jme3-examples/src/main/java/jme3test/network/TestChatServer.java
  84. 29
      jme3-lwjgl3/src/main/java/com/jme3/system/lwjgl/LwjglContext.java
  85. 26
      jme3-lwjgl3/src/main/java/com/jme3/system/lwjgl/LwjglWindow.java
  86. 1
      jme3-plugins/build.gradle
  87. 78
      jme3-plugins/src/main/java/com/jme3/material/plugin/export/material/J3MExporter.java
  88. 383
      jme3-plugins/src/main/java/com/jme3/material/plugin/export/material/J3MOutputCapsule.java
  89. 81
      jme3-plugins/src/main/java/com/jme3/material/plugin/export/material/J3MRenderStateOutputCapsule.java
  90. 99
      jme3-plugins/src/main/java/com/jme3/material/plugin/export/material/J3MRootOutputCapsule.java
  91. 115
      jme3-plugins/src/test/java/com/jme3/material/plugin/TestMaterialWrite.java
  92. 10
      jme3-terrain/src/main/java/com/jme3/terrain/geomipmap/NormalRecalcControl.java
  93. 25
      jme3-terrain/src/main/java/com/jme3/terrain/geomipmap/TerrainLodControl.java
  94. 8
      jme3-testdata/src/main/resources/Models/Sign Post/Sign Post.j3m
  95. BIN
      jme3-testdata/src/main/resources/Models/Test/CornellBox.j3o
  96. 1
      sdk/BasicGameTemplate/MANIFEST.MF
  97. 0
      sdk/BasicGameTemplate/assets/Interface/.keep
  98. 0
      sdk/BasicGameTemplate/assets/MatDefs/.keep
  99. 0
      sdk/BasicGameTemplate/assets/Materials/.keep
  100. 0
      sdk/BasicGameTemplate/assets/Models/.keep
  101. Some files were not shown because too many files have changed in this diff Show More

@ -8,7 +8,7 @@ apply plugin: 'maven'
group = 'org.jmonkeyengine' group = 'org.jmonkeyengine'
version = jmePomVersion version = jmePomVersion
sourceCompatibility = '1.6' sourceCompatibility = '1.7'
[compileJava, compileTestJava]*.options*.encoding = 'UTF-8' [compileJava, compileTestJava]*.options*.encoding = 'UTF-8'
repositories { repositories {

@ -11,7 +11,6 @@ jmeVersionTagID = 0
buildJavaDoc = true buildJavaDoc = true
# specify if SDK and Native libraries get built # specify if SDK and Native libraries get built
buildSdkProject = true
buildNativeProjects = false buildNativeProjects = false
buildAndroidExamples = false buildAndroidExamples = false

@ -430,7 +430,7 @@ public class AndroidTouchInput implements TouchInput {
return; return;
} }
logger.log(Level.INFO, "event: {0}", event); //logger.log(Level.INFO, "event: {0}", event);
inputEventQueue.add(event); inputEventQueue.add(event);
if (event instanceof TouchEvent) { if (event instanceof TouchEvent) {

@ -146,7 +146,6 @@ public class Face implements Comparator<Integer> {
/** /**
* @return current indexes of the face (if it is already triangulated then more than one index group will be in the result list) * @return current indexes of the face (if it is already triangulated then more than one index group will be in the result list)
*/ */
@SuppressWarnings("unchecked")
public List<List<Integer>> getCurrentIndexes() { public List<List<Integer>> getCurrentIndexes() {
if (triangulatedFaces == null) { if (triangulatedFaces == null) {
return Arrays.asList(indexes.getAll()); return Arrays.asList(indexes.getAll());

@ -286,6 +286,8 @@ public class MeshHelper extends AbstractBlenderHelper {
List<Map<String, Float>> result = new ArrayList<Map<String, Float>>(); List<Map<String, Float>> result = new ArrayList<Map<String, Float>>();
Structure parent = blenderContext.peekParent(); Structure parent = blenderContext.peekParent();
if(parent != null) {
// the mesh might be saved without its parent (it is then unused)
Structure defbase = (Structure) parent.getFieldValue("defbase"); Structure defbase = (Structure) parent.getFieldValue("defbase");
List<String> groupNames = new ArrayList<String>(); List<String> groupNames = new ArrayList<String>();
List<Structure> defs = defbase.evaluateListBase(); List<Structure> defs = defbase.evaluateListBase();
@ -312,6 +314,7 @@ public class MeshHelper extends AbstractBlenderHelper {
result.add(weightsForVertex); result.add(weightsForVertex);
} }
} }
}
return result; return result;
} }

@ -41,6 +41,8 @@ import com.jme3.math.Vector3f;
import com.jme3.renderer.RenderManager; import com.jme3.renderer.RenderManager;
import com.jme3.renderer.ViewPort; import com.jme3.renderer.ViewPort;
import com.jme3.scene.Spatial; import com.jme3.scene.Spatial;
import com.jme3.util.clone.Cloner;
import com.jme3.util.clone.JmeCloneable;
import java.io.IOException; import java.io.IOException;
/** /**
@ -49,7 +51,7 @@ import java.io.IOException;
* *
* @author normenhansen * @author normenhansen
*/ */
public abstract class AbstractPhysicsControl implements PhysicsControl { public abstract class AbstractPhysicsControl implements PhysicsControl, JmeCloneable {
private final Quaternion tmp_inverseWorldRotation = new Quaternion(); private final Quaternion tmp_inverseWorldRotation = new Quaternion();
protected Spatial spatial; protected Spatial spatial;
@ -161,6 +163,12 @@ public abstract class AbstractPhysicsControl implements PhysicsControl {
} }
@Override
public void cloneFields( Cloner cloner, Object original ) {
this.spatial = cloner.clone(spatial);
createSpatialData(this.spatial);
}
public void setSpatial(Spatial spatial) { public void setSpatial(Spatial spatial) {
if (this.spatial != null && this.spatial != spatial) { if (this.spatial != null && this.spatial != spatial) {
removeSpatialData(this.spatial); removeSpatialData(this.spatial);

@ -50,6 +50,8 @@ import com.jme3.renderer.ViewPort;
import com.jme3.scene.Spatial; import com.jme3.scene.Spatial;
import com.jme3.scene.control.Control; import com.jme3.scene.control.Control;
import com.jme3.util.TempVars; import com.jme3.util.TempVars;
import com.jme3.util.clone.Cloner;
import com.jme3.util.clone.JmeCloneable;
import java.io.IOException; import java.io.IOException;
import java.util.List; import java.util.List;
import java.util.logging.Level; import java.util.logging.Level;
@ -68,7 +70,7 @@ import java.util.logging.Logger;
* *
* @author normenhansen * @author normenhansen
*/ */
public class BetterCharacterControl extends AbstractPhysicsControl implements PhysicsTickListener { public class BetterCharacterControl extends AbstractPhysicsControl implements PhysicsTickListener, JmeCloneable {
protected static final Logger logger = Logger.getLogger(BetterCharacterControl.class.getName()); protected static final Logger logger = Logger.getLogger(BetterCharacterControl.class.getName());
protected PhysicsRigidBody rigidBody; protected PhysicsRigidBody rigidBody;
@ -663,12 +665,21 @@ public class BetterCharacterControl extends AbstractPhysicsControl implements Ph
rigidBody.setUserObject(null); rigidBody.setUserObject(null);
} }
@Override
public Control cloneForSpatial(Spatial spatial) { public Control cloneForSpatial(Spatial spatial) {
BetterCharacterControl control = new BetterCharacterControl(radius, height, mass); BetterCharacterControl control = new BetterCharacterControl(radius, height, mass);
control.setJumpForce(jumpForce); control.setJumpForce(jumpForce);
return control; return control;
} }
@Override
public Object jmeClone() {
BetterCharacterControl control = new BetterCharacterControl(radius, height, mass);
control.setJumpForce(jumpForce);
control.spatial = this.spatial;
return control;
}
@Override @Override
public void write(JmeExporter ex) throws IOException { public void write(JmeExporter ex) throws IOException {
super.write(ex); super.write(ex);

@ -44,13 +44,15 @@ import com.jme3.renderer.RenderManager;
import com.jme3.renderer.ViewPort; import com.jme3.renderer.ViewPort;
import com.jme3.scene.Spatial; import com.jme3.scene.Spatial;
import com.jme3.scene.control.Control; import com.jme3.scene.control.Control;
import com.jme3.util.clone.Cloner;
import com.jme3.util.clone.JmeCloneable;
import java.io.IOException; import java.io.IOException;
/** /**
* You might want to try <code>BetterCharacterControl</code> as well. * You might want to try <code>BetterCharacterControl</code> as well.
* @author normenhansen * @author normenhansen
*/ */
public class CharacterControl extends PhysicsCharacter implements PhysicsControl { public class CharacterControl extends PhysicsCharacter implements PhysicsControl, JmeCloneable {
protected Spatial spatial; protected Spatial spatial;
protected boolean enabled = true; protected boolean enabled = true;
@ -87,6 +89,7 @@ public class CharacterControl extends PhysicsCharacter implements PhysicsControl
return spatial.getWorldTranslation(); return spatial.getWorldTranslation();
} }
@Override
public Control cloneForSpatial(Spatial spatial) { public Control cloneForSpatial(Spatial spatial) {
CharacterControl control = new CharacterControl(collisionShape, stepHeight); CharacterControl control = new CharacterControl(collisionShape, stepHeight);
control.setCcdMotionThreshold(getCcdMotionThreshold()); control.setCcdMotionThreshold(getCcdMotionThreshold());
@ -103,6 +106,29 @@ public class CharacterControl extends PhysicsCharacter implements PhysicsControl
return control; return control;
} }
@Override
public Object jmeClone() {
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.spatial = this.spatial;
return control;
}
@Override
public void cloneFields( Cloner cloner, Object original ) {
this.spatial = cloner.clone(spatial);
}
public void setSpatial(Spatial spatial) { public void setSpatial(Spatial spatial) {
this.spatial = spatial; this.spatial = spatial;
setUserObject(spatial); setUserObject(spatial);

@ -44,6 +44,8 @@ import com.jme3.renderer.RenderManager;
import com.jme3.renderer.ViewPort; import com.jme3.renderer.ViewPort;
import com.jme3.scene.Spatial; import com.jme3.scene.Spatial;
import com.jme3.scene.control.Control; import com.jme3.scene.control.Control;
import com.jme3.util.clone.Cloner;
import com.jme3.util.clone.JmeCloneable;
import java.io.IOException; import java.io.IOException;
/** /**
@ -51,7 +53,7 @@ import java.io.IOException;
* overlaps with other physics objects (e.g. aggro radius). * overlaps with other physics objects (e.g. aggro radius).
* @author normenhansen * @author normenhansen
*/ */
public class GhostControl extends PhysicsGhostObject implements PhysicsControl { public class GhostControl extends PhysicsGhostObject implements PhysicsControl, JmeCloneable {
protected Spatial spatial; protected Spatial spatial;
protected boolean enabled = true; protected boolean enabled = true;
@ -93,6 +95,7 @@ public class GhostControl extends PhysicsGhostObject implements PhysicsControl {
return spatial.getWorldRotation(); return spatial.getWorldRotation();
} }
@Override
public Control cloneForSpatial(Spatial spatial) { public Control cloneForSpatial(Spatial spatial) {
GhostControl control = new GhostControl(collisionShape); GhostControl control = new GhostControl(collisionShape);
control.setCcdMotionThreshold(getCcdMotionThreshold()); control.setCcdMotionThreshold(getCcdMotionThreshold());
@ -105,6 +108,25 @@ public class GhostControl extends PhysicsGhostObject implements PhysicsControl {
return control; return control;
} }
@Override
public Object jmeClone() {
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.spatial = this.spatial;
return control;
}
@Override
public void cloneFields( Cloner cloner, Object original ) {
this.spatial = cloner.clone(spatial);
}
public void setSpatial(Spatial spatial) { public void setSpatial(Spatial spatial) {
this.spatial = spatial; this.spatial = spatial;
setUserObject(spatial); setUserObject(spatial);

@ -61,6 +61,8 @@ import com.jme3.scene.Node;
import com.jme3.scene.Spatial; import com.jme3.scene.Spatial;
import com.jme3.scene.control.Control; import com.jme3.scene.control.Control;
import com.jme3.util.TempVars; import com.jme3.util.TempVars;
import com.jme3.util.clone.Cloner;
import com.jme3.util.clone.JmeCloneable;
import java.io.IOException; import java.io.IOException;
import java.util.*; import java.util.*;
import java.util.logging.Level; import java.util.logging.Level;
@ -92,7 +94,7 @@ import java.util.logging.Logger;
* *
* @author Normen Hansen and Rémy Bouquet (Nehon) * @author Normen Hansen and Rémy Bouquet (Nehon)
*/ */
public class KinematicRagdollControl extends AbstractPhysicsControl implements PhysicsCollisionListener { public class KinematicRagdollControl extends AbstractPhysicsControl implements PhysicsCollisionListener, JmeCloneable {
protected static final Logger logger = Logger.getLogger(KinematicRagdollControl.class.getName()); protected static final Logger logger = Logger.getLogger(KinematicRagdollControl.class.getName());
protected List<RagdollCollisionListener> listeners; protected List<RagdollCollisionListener> listeners;
@ -910,6 +912,7 @@ public class KinematicRagdollControl extends AbstractPhysicsControl implements P
public void render(RenderManager rm, ViewPort vp) { public void render(RenderManager rm, ViewPort vp) {
} }
@Override
public Control cloneForSpatial(Spatial spatial) { public Control cloneForSpatial(Spatial spatial) {
KinematicRagdollControl control = new KinematicRagdollControl(preset, weightThreshold); KinematicRagdollControl control = new KinematicRagdollControl(preset, weightThreshold);
control.setMode(mode); control.setMode(mode);
@ -919,6 +922,17 @@ public class KinematicRagdollControl extends AbstractPhysicsControl implements P
return control; return control;
} }
@Override
public Object jmeClone() {
KinematicRagdollControl control = new KinematicRagdollControl(preset, weightThreshold);
control.setMode(mode);
control.setRootMass(rootMass);
control.setWeightThreshold(weightThreshold);
control.setApplyPhysicsLocal(applyLocal);
control.spatial = this.spatial;
return control;
}
public Vector3f setIKTarget(Bone bone, Vector3f worldPos, int chainLength) { public Vector3f setIKTarget(Bone bone, Vector3f worldPos, int chainLength) {
Vector3f target = worldPos.subtract(targetModel.getWorldTranslation()); Vector3f target = worldPos.subtract(targetModel.getWorldTranslation());
ikTargets.put(bone.getName(), target); ikTargets.put(bone.getName(), target);

@ -51,13 +51,15 @@ import com.jme3.scene.Spatial;
import com.jme3.scene.control.Control; import com.jme3.scene.control.Control;
import com.jme3.scene.shape.Box; import com.jme3.scene.shape.Box;
import com.jme3.scene.shape.Sphere; import com.jme3.scene.shape.Sphere;
import com.jme3.util.clone.Cloner;
import com.jme3.util.clone.JmeCloneable;
import java.io.IOException; import java.io.IOException;
/** /**
* *
* @author normenhansen * @author normenhansen
*/ */
public class RigidBodyControl extends PhysicsRigidBody implements PhysicsControl { public class RigidBodyControl extends PhysicsRigidBody implements PhysicsControl, JmeCloneable {
protected Spatial spatial; protected Spatial spatial;
protected boolean enabled = true; protected boolean enabled = true;
@ -89,6 +91,7 @@ public class RigidBodyControl extends PhysicsRigidBody implements PhysicsControl
super(shape, mass); super(shape, mass);
} }
@Override
public Control cloneForSpatial(Spatial spatial) { public Control cloneForSpatial(Spatial spatial) {
RigidBodyControl control = new RigidBodyControl(collisionShape, mass); RigidBodyControl control = new RigidBodyControl(collisionShape, mass);
control.setAngularFactor(getAngularFactor()); control.setAngularFactor(getAngularFactor());
@ -115,6 +118,39 @@ public class RigidBodyControl extends PhysicsRigidBody implements PhysicsControl
return control; return control;
} }
@Override
public Object jmeClone() {
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.spatial = this.spatial;
return control;
}
@Override
public void cloneFields( Cloner cloner, Object original ) {
this.spatial = cloner.clone(spatial);
}
public void setSpatial(Spatial spatial) { public void setSpatial(Spatial spatial) {
this.spatial = spatial; this.spatial = spatial;
setUserObject(spatial); setUserObject(spatial);

@ -46,6 +46,8 @@ import com.jme3.renderer.ViewPort;
import com.jme3.scene.Node; import com.jme3.scene.Node;
import com.jme3.scene.Spatial; import com.jme3.scene.Spatial;
import com.jme3.scene.control.Control; import com.jme3.scene.control.Control;
import com.jme3.util.clone.Cloner;
import com.jme3.util.clone.JmeCloneable;
import java.io.IOException; import java.io.IOException;
import java.util.Iterator; import java.util.Iterator;
@ -53,7 +55,7 @@ import java.util.Iterator;
* *
* @author normenhansen * @author normenhansen
*/ */
public class VehicleControl extends PhysicsVehicle implements PhysicsControl { public class VehicleControl extends PhysicsVehicle implements PhysicsControl, JmeCloneable {
protected Spatial spatial; protected Spatial spatial;
protected boolean enabled = true; protected boolean enabled = true;
@ -106,6 +108,7 @@ public class VehicleControl extends PhysicsVehicle implements PhysicsControl {
return spatial.getWorldRotation(); return spatial.getWorldRotation();
} }
@Override
public Control cloneForSpatial(Spatial spatial) { public Control cloneForSpatial(Spatial spatial) {
VehicleControl control = new VehicleControl(collisionShape, mass); VehicleControl control = new VehicleControl(collisionShape, mass);
control.setAngularFactor(getAngularFactor()); control.setAngularFactor(getAngularFactor());
@ -155,6 +158,63 @@ public class VehicleControl extends PhysicsVehicle implements PhysicsControl {
return control; return control;
} }
@Override
public Object jmeClone() {
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());
// Copy the wheel spatial reference directly for now. They'll
// get fixed up in the cloneFields() method
newWheel.setWheelSpatial(wheel.getWheelSpatial());
}
control.setApplyPhysicsLocal(isApplyPhysicsLocal());
control.spatial = spatial;
return control;
}
@Override
public void cloneFields( Cloner cloner, Object original ) {
this.spatial = cloner.clone(spatial);
for( VehicleWheel wheel : wheels ) {
Spatial spatial = cloner.clone(wheel.getWheelSpatial());
wheel.setWheelSpatial(spatial);
}
}
public void setSpatial(Spatial spatial) { public void setSpatial(Spatial spatial) {
this.spatial = spatial; this.spatial = spatial;
setUserObject(spatial); setUserObject(spatial);

@ -71,6 +71,10 @@ public class ConeCollisionShape extends CollisionShape {
return radius; return radius;
} }
public float getHeight() {
return height;
}
public void write(JmeExporter ex) throws IOException { public void write(JmeExporter ex) throws IOException {
super.write(ex); super.write(ex);
OutputCapsule capsule = ex.getCapsule(this); OutputCapsule capsule = ex.getCapsule(this);

@ -38,11 +38,14 @@ import com.jme3.scene.Mesh;
import com.jme3.scene.Spatial; import com.jme3.scene.Spatial;
import com.jme3.scene.control.AbstractControl; import com.jme3.scene.control.AbstractControl;
import com.jme3.scene.control.Control; import com.jme3.scene.control.Control;
import com.jme3.util.clone.Cloner;
import com.jme3.util.clone.JmeCloneable;
import com.jme3.util.TempVars; import com.jme3.util.TempVars;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
/** /**
@ -65,7 +68,7 @@ import java.util.Map.Entry;
* *
* @author Kirill Vainer * @author Kirill Vainer
*/ */
public final class AnimControl extends AbstractControl implements Cloneable { public final class AnimControl extends AbstractControl implements Cloneable, JmeCloneable {
/** /**
* Skeleton object must contain corresponding data for the targets' weight buffers. * Skeleton object must contain corresponding data for the targets' weight buffers.
@ -108,6 +111,7 @@ public final class AnimControl extends AbstractControl implements Cloneable {
/** /**
* Internal use only. * Internal use only.
*/ */
@Override
public Control cloneForSpatial(Spatial spatial) { public Control cloneForSpatial(Spatial spatial) {
try { try {
AnimControl clone = (AnimControl) super.clone(); AnimControl clone = (AnimControl) super.clone();
@ -130,6 +134,32 @@ public final class AnimControl extends AbstractControl implements Cloneable {
} }
} }
@Override
public Object jmeClone() {
AnimControl clone = (AnimControl) super.jmeClone();
clone.channels = new ArrayList<AnimChannel>();
clone.listeners = new ArrayList<AnimEventListener>();
return clone;
}
@Override
public void cloneFields( Cloner cloner, Object original ) {
super.cloneFields(cloner, original);
this.skeleton = cloner.clone(skeleton);
// Note cloneForSpatial() never actually cloned the animation map... just its reference
HashMap<String, Animation> newMap = new HashMap<>();
// animationMap is cloned, but only ClonableTracks will be cloned as they need a reference to a cloned spatial
for( Map.Entry<String, Animation> e : animationMap.entrySet() ) {
newMap.put(e.getKey(), cloner.clone(e.getValue()));
}
this.animationMap = newMap;
}
/** /**
* @param animations Set the animations that this <code>AnimControl</code> * @param animations Set the animations that this <code>AnimControl</code>
* will be capable of playing. The animations should be compatible * will be capable of playing. The animations should be compatible

@ -35,6 +35,8 @@ import com.jme3.export.*;
import com.jme3.scene.Spatial; import com.jme3.scene.Spatial;
import com.jme3.util.SafeArrayList; import com.jme3.util.SafeArrayList;
import com.jme3.util.TempVars; import com.jme3.util.TempVars;
import com.jme3.util.clone.Cloner;
import com.jme3.util.clone.JmeCloneable;
import java.io.IOException; import java.io.IOException;
/** /**
@ -42,7 +44,7 @@ import java.io.IOException;
* *
* @author Kirill Vainer, Marcin Roguski (Kaelthas) * @author Kirill Vainer, Marcin Roguski (Kaelthas)
*/ */
public class Animation implements Savable, Cloneable { public class Animation implements Savable, Cloneable, JmeCloneable {
/** /**
* The name of the animation. * The name of the animation.
@ -190,6 +192,33 @@ public class Animation implements Savable, Cloneable {
} }
} }
@Override
public Object jmeClone() {
try {
return super.clone();
} catch( CloneNotSupportedException e ) {
throw new RuntimeException("Error cloning", e);
}
}
@Override
public void cloneFields( Cloner cloner, Object original ) {
// There is some logic here that I'm copying but I'm not sure if
// it's a mistake or not. If a track is not a CloneableTrack then it
// isn't cloned at all... even though they all implement clone() methods. -pspeed
SafeArrayList<Track> newTracks = new SafeArrayList<>(Track.class);
for( Track track : tracks ) {
if( track instanceof ClonableTrack ) {
newTracks.add(cloner.clone(track));
} else {
// this is the part that seems fishy
newTracks.add(track);
}
}
this.tracks = newTracks;
}
@Override @Override
public String toString() { public String toString() {
return getClass().getSimpleName() + "[name=" + name + ", length=" + length + ']'; return getClass().getSimpleName() + "[name=" + name + ", length=" + length + ']';

@ -39,6 +39,8 @@ import com.jme3.export.OutputCapsule;
import com.jme3.scene.Node; import com.jme3.scene.Node;
import com.jme3.scene.Spatial; import com.jme3.scene.Spatial;
import com.jme3.util.TempVars; import com.jme3.util.TempVars;
import com.jme3.util.clone.Cloner;
import com.jme3.util.clone.JmeCloneable;
import java.io.IOException; import java.io.IOException;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
@ -174,6 +176,7 @@ public class AudioTrack implements ClonableTrack {
* @param spatial the Spatial holding the AnimControl * @param spatial the Spatial holding the AnimControl
* @return the cloned Track with proper reference * @return the cloned Track with proper reference
*/ */
@Override
public Track cloneForSpatial(Spatial spatial) { public Track cloneForSpatial(Spatial spatial) {
AudioTrack audioTrack = new AudioTrack(); AudioTrack audioTrack = new AudioTrack();
audioTrack.length = this.length; audioTrack.length = this.length;
@ -192,6 +195,26 @@ public class AudioTrack implements ClonableTrack {
return audioTrack; return audioTrack;
} }
@Override
public Object jmeClone() {
try {
return super.clone();
} catch( CloneNotSupportedException e ) {
throw new RuntimeException("Error cloning", e);
}
}
@Override
public void cloneFields( Cloner cloner, Object original ) {
// Duplicating the old cloned state from cloneForSpatial()
this.initialized = false;
this.started = false;
this.played = false;
this.audio = cloner.clone(audio);
}
/** /**
* recursive function responsible for finding the newly cloned AudioNode * recursive function responsible for finding the newly cloned AudioNode
* *

@ -139,11 +139,7 @@ public final class Bone implements Savable {
/** /**
* Special-purpose copy constructor. * Special-purpose copy constructor.
* <p> * <p>
* Only copies the name and bind pose from the original. * Only copies the name, user control state and bind pose transforms 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> * <p>
* The rest of the data is <em>NOT</em> copied, as it will be * The rest of the data is <em>NOT</em> copied, as it will be
* generated automatically when the bone is animated. * generated automatically when the bone is animated.
@ -155,13 +151,13 @@ public final class Bone implements Savable {
userControl = source.userControl; userControl = source.userControl;
bindPos = source.bindPos; bindPos = source.bindPos.clone();
bindRot = source.bindRot; bindRot = source.bindRot.clone();
bindScale = source.bindScale; bindScale = source.bindScale.clone();
modelBindInversePos = source.modelBindInversePos; modelBindInversePos = source.modelBindInversePos.clone();
modelBindInverseRot = source.modelBindInverseRot; modelBindInverseRot = source.modelBindInverseRot.clone();
modelBindInverseScale = source.modelBindInverseScale; modelBindInverseScale = source.modelBindInverseScale.clone();
// parent and children will be assigned manually.. // parent and children will be assigned manually..
} }

@ -32,6 +32,7 @@
package com.jme3.animation; package com.jme3.animation;
import com.jme3.scene.Spatial; import com.jme3.scene.Spatial;
import com.jme3.util.clone.JmeCloneable;
/** /**
* An interface that allow to clone a Track for a given Spatial. * An interface that allow to clone a Track for a given Spatial.
@ -43,7 +44,7 @@ import com.jme3.scene.Spatial;
* *
* @author Nehon * @author Nehon
*/ */
public interface ClonableTrack extends Track { public interface ClonableTrack extends Track, JmeCloneable {
/** /**
* Allows to clone the track for a given Spatial. * Allows to clone the track for a given Spatial.

@ -44,6 +44,8 @@ import com.jme3.scene.Spatial.CullHint;
import com.jme3.scene.control.AbstractControl; import com.jme3.scene.control.AbstractControl;
import com.jme3.scene.control.Control; import com.jme3.scene.control.Control;
import com.jme3.util.TempVars; import com.jme3.util.TempVars;
import com.jme3.util.clone.Cloner;
import com.jme3.util.clone.JmeCloneable;
import java.io.IOException; import java.io.IOException;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
@ -116,6 +118,22 @@ public class EffectTrack implements ClonableTrack {
} }
} }
@Override
public Object jmeClone() {
KillParticleControl c = new KillParticleControl();
//this control should be removed as it shouldn't have been persisted in the first place
//In the quest to find the less hackish solution to achieve this,
//making it remove itself from the spatial in the first update loop when loaded was the less bad.
c.remove = true;
c.spatial = spatial;
return c;
}
@Override
public void cloneFields( Cloner cloner, Object original ) {
this.spatial = cloner.clone(spatial);
}
@Override @Override
protected void controlRender(RenderManager rm, ViewPort vp) { protected void controlRender(RenderManager rm, ViewPort vp) {
} }
@ -263,6 +281,7 @@ public class EffectTrack implements ClonableTrack {
* @param spatial the Spatial holding the AnimControl * @param spatial the Spatial holding the AnimControl
* @return the cloned Track with proper reference * @return the cloned Track with proper reference
*/ */
@Override
public Track cloneForSpatial(Spatial spatial) { public Track cloneForSpatial(Spatial spatial) {
EffectTrack effectTrack = new EffectTrack(); EffectTrack effectTrack = new EffectTrack();
effectTrack.particlesPerSeconds = this.particlesPerSeconds; effectTrack.particlesPerSeconds = this.particlesPerSeconds;
@ -283,6 +302,21 @@ public class EffectTrack implements ClonableTrack {
return effectTrack; return effectTrack;
} }
@Override
public Object jmeClone() {
try {
return super.clone();
} catch( CloneNotSupportedException e ) {
throw new RuntimeException("Error cloning", e);
}
}
@Override
public void cloneFields( Cloner cloner, Object original ) {
this.emitter = cloner.clone(emitter);
}
/** /**
* recursive function responsible for finding the newly cloned Emitter * recursive function responsible for finding the newly cloned Emitter
* *

@ -34,6 +34,8 @@ package com.jme3.animation;
import com.jme3.export.*; import com.jme3.export.*;
import com.jme3.math.Matrix4f; import com.jme3.math.Matrix4f;
import com.jme3.util.TempVars; import com.jme3.util.TempVars;
import com.jme3.util.clone.JmeCloneable;
import com.jme3.util.clone.Cloner;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -45,7 +47,7 @@ import java.util.List;
* *
* @author Kirill Vainer * @author Kirill Vainer
*/ */
public final class Skeleton implements Savable { public final class Skeleton implements Savable, JmeCloneable {
private Bone[] rootBones; private Bone[] rootBones;
private Bone[] boneList; private Bone[] boneList;
@ -118,6 +120,15 @@ public final class Skeleton implements Savable {
public Skeleton() { public Skeleton() {
} }
@Override
public Object jmeClone() {
return new Skeleton(this);
}
@Override
public void cloneFields( Cloner cloner, Object original ) {
}
private void createSkinningMatrices() { private void createSkinningMatrices() {
skinningMatrixes = new Matrix4f[boneList.length]; skinningMatrixes = new Matrix4f[boneList.length];
for (int i = 0; i < skinningMatrixes.length; i++) { for (int i = 0; i < skinningMatrixes.length; i++) {

@ -46,6 +46,8 @@ import com.jme3.scene.control.Control;
import com.jme3.shader.VarType; import com.jme3.shader.VarType;
import com.jme3.util.SafeArrayList; import com.jme3.util.SafeArrayList;
import com.jme3.util.TempVars; import com.jme3.util.TempVars;
import com.jme3.util.clone.Cloner;
import com.jme3.util.clone.JmeCloneable;
import java.io.IOException; import java.io.IOException;
import java.nio.Buffer; import java.nio.Buffer;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
@ -63,7 +65,7 @@ import java.util.logging.Logger;
* *
* @author Rémy Bouquet Based on AnimControl by Kirill Vainer * @author Rémy Bouquet Based on AnimControl by Kirill Vainer
*/ */
public class SkeletonControl extends AbstractControl implements Cloneable { public class SkeletonControl extends AbstractControl implements Cloneable, JmeCloneable {
/** /**
* The skeleton of the model. * The skeleton of the model.
@ -345,6 +347,7 @@ public class SkeletonControl extends AbstractControl implements Cloneable {
} }
} }
@Override
public Control cloneForSpatial(Spatial spatial) { public Control cloneForSpatial(Spatial spatial) {
Node clonedNode = (Node) spatial; Node clonedNode = (Node) spatial;
SkeletonControl clone = new SkeletonControl(); SkeletonControl clone = new SkeletonControl();
@ -385,6 +388,29 @@ public class SkeletonControl extends AbstractControl implements Cloneable {
return clone; return clone;
} }
@Override
public Object jmeClone() {
return super.jmeClone();
}
@Override
public void cloneFields( Cloner cloner, Object original ) {
super.cloneFields(cloner, original);
this.skeleton = cloner.clone(skeleton);
// If the targets were cloned then this will clone them. If the targets
// were shared then this will share them.
this.targets = cloner.clone(targets);
// Not automatic set cloning yet
Set<Material> newMaterials = new HashSet<Material>();
for( Material m : this.materials ) {
newMaterials.add(cloner.clone(m));
}
this.materials = newMaterials;
}
/** /**
* *
* @param boneName the name of the bone * @param boneName the name of the bone

@ -36,6 +36,8 @@ import com.jme3.export.JmeExporter;
import com.jme3.export.JmeImporter; import com.jme3.export.JmeImporter;
import com.jme3.export.OutputCapsule; import com.jme3.export.OutputCapsule;
import com.jme3.export.Savable; import com.jme3.export.Savable;
import com.jme3.util.clone.Cloner;
import com.jme3.util.clone.JmeCloneable;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
@ -48,7 +50,7 @@ import java.util.ArrayList;
* *
* @author Nehon * @author Nehon
*/ */
public class TrackInfo implements Savable { public class TrackInfo implements Savable, JmeCloneable {
ArrayList<Track> tracks = new ArrayList<Track>(); ArrayList<Track> tracks = new ArrayList<Track>();
@ -72,4 +74,18 @@ public class TrackInfo implements Savable {
public void addTrack(Track track) { public void addTrack(Track track) {
tracks.add(track); tracks.add(track);
} }
@Override
public Object jmeClone() {
try {
return super.clone();
} catch( CloneNotSupportedException e ) {
throw new RuntimeException("Error cloning", e);
}
}
@Override
public void cloneFields( Cloner cloner, Object original ) {
this.tracks = cloner.clone(tracks);
}
} }

@ -41,6 +41,8 @@ import com.jme3.renderer.queue.RenderQueue.Bucket;
import com.jme3.scene.Node; import com.jme3.scene.Node;
import com.jme3.scene.Spatial; import com.jme3.scene.Spatial;
import com.jme3.scene.control.Control; import com.jme3.scene.control.Control;
import com.jme3.util.clone.Cloner;
import com.jme3.util.clone.JmeCloneable;
/** /**
* The <code>StatsView</code> provides a heads-up display (HUD) of various * The <code>StatsView</code> provides a heads-up display (HUD) of various
@ -58,7 +60,7 @@ import com.jme3.scene.control.Control;
* rootNode.attachChild(statsView);<br/> * rootNode.attachChild(statsView);<br/>
* </code> * </code>
*/ */
public class StatsView extends Node implements Control { public class StatsView extends Node implements Control, JmeCloneable {
private BitmapText statText; private BitmapText statText;
private Statistics statistics; private Statistics statistics;
@ -115,10 +117,21 @@ public class StatsView extends Node implements Control {
//statistics.clearFrame(); //statistics.clearFrame();
} }
@Override
public Control cloneForSpatial(Spatial spatial) { public Control cloneForSpatial(Spatial spatial) {
return (Control) spatial; return (Control) spatial;
} }
@Override
public Object jmeClone() {
throw new UnsupportedOperationException("Not yet implemented.");
}
@Override
public void cloneFields( Cloner cloner, Object original ) {
throw new UnsupportedOperationException("Not yet implemented.");
}
public void setSpatial(Spatial spatial) { public void setSpatial(Spatial spatial) {
} }

@ -249,21 +249,23 @@ public class ScreenshotAppState extends AbstractAppState implements ActionListen
} }
logger.log(Level.FINE, "Saving ScreenShot to: {0}", file.getAbsolutePath()); logger.log(Level.FINE, "Saving ScreenShot to: {0}", file.getAbsolutePath());
OutputStream outStream = null;
try { try {
outStream = new FileOutputStream(file); writeImageFile(file);
JmeSystem.writeImageFile(outStream, "png", outBuf, width, height);
} catch (IOException ex) {
logger.log(Level.SEVERE, "Error while saving screenshot", ex);
} finally {
if (outStream != null){
try {
outStream.close();
} catch (IOException ex) { } catch (IOException ex) {
logger.log(Level.SEVERE, "Error while saving screenshot", ex); logger.log(Level.SEVERE, "Error while saving screenshot", ex);
} }
} }
} }
/**
* Called by postFrame() once the screen has been captured to outBuf.
*/
protected void writeImageFile( File file ) throws IOException {
OutputStream outStream = new FileOutputStream(file);
try {
JmeSystem.writeImageFile(outStream, "png", outBuf, width, height);
} finally {
outStream.close();
} }
} }
} }

@ -156,7 +156,7 @@ public class AssetKey<T> implements Savable, Cloneable {
list.removeLast(); list.removeLast();
} else { } else {
list.add(".."); list.add("..");
Logger.getLogger(AssetKey.class.getName()).log(Level.SEVERE, "Asset path is outside assetmanager root"); Logger.getLogger(AssetKey.class.getName()).log(Level.SEVERE, "Asset path \"{0}\" is outside assetmanager root", path);
} }
} else { } else {
list.add(string); list.add(string);

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2009-2012 jMonkeyEngine * Copyright (c) 2009-2012, 2016 jMonkeyEngine
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
@ -33,6 +33,7 @@ package com.jme3.audio;
import com.jme3.asset.AssetManager; import com.jme3.asset.AssetManager;
import com.jme3.asset.AssetNotFoundException; import com.jme3.asset.AssetNotFoundException;
import com.jme3.audio.AudioData.DataType;
import com.jme3.export.InputCapsule; import com.jme3.export.InputCapsule;
import com.jme3.export.JmeExporter; import com.jme3.export.JmeExporter;
import com.jme3.export.JmeImporter; import com.jme3.export.JmeImporter;
@ -128,6 +129,17 @@ public class AudioNode extends Node implements AudioSource {
setAudioData(audioData, audioKey); setAudioData(audioData, 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 type The type. If <code>{@link com.jme3.audio.AudioData.DataType}.Stream</code>, the audio will be streamed gradually from disk,
* otherwise it will be buffered (<code>{@link com.jme3.audio.AudioData.DataType}.Buffer</code>)
*/
public AudioNode(AssetManager assetManager, String name, DataType type) {
this(assetManager, name, type == DataType.Stream, true);
}
/** /**
* Creates a new <code>AudioNode</code> with the given audio file. * Creates a new <code>AudioNode</code> with the given audio file.
* *
@ -139,6 +151,8 @@ public class AudioNode extends Node implements AudioSource {
* the stream cache is used. When enabled, the audio stream will * the stream cache is used. When enabled, the audio stream will
* be read entirely but not decoded, allowing features such as * be read entirely but not decoded, allowing features such as
* seeking, looping and determining duration. * seeking, looping and determining duration.
*
* @deprecated Use {@link AudioNode#AudioNode(com.jme3.asset.AssetManager, java.lang.String, com.jme3.audio.AudioData.DataType)} instead
*/ */
public AudioNode(AssetManager assetManager, String name, boolean stream, boolean streamCache) { public AudioNode(AssetManager assetManager, String name, boolean stream, boolean streamCache) {
this.audioKey = new AudioKey(name, stream, streamCache); this.audioKey = new AudioKey(name, stream, streamCache);
@ -152,9 +166,11 @@ public class AudioNode extends Node implements AudioSource {
* @param name The filename of the audio file * @param name The filename of the audio file
* @param stream If true, the audio will be streamed gradually from disk, * @param stream If true, the audio will be streamed gradually from disk,
* otherwise, it will be buffered. * otherwise, it will be buffered.
*
* @deprecated Use {@link AudioNode#AudioNode(com.jme3.asset.AssetManager, java.lang.String, com.jme3.audio.AudioData.DataType)} instead
*/ */
public AudioNode(AssetManager assetManager, String name, boolean stream) { public AudioNode(AssetManager assetManager, String name, boolean stream) {
this(assetManager, name, stream, false); this(assetManager, name, stream, true); // Always streamCached
} }
/** /**
@ -167,7 +183,7 @@ public class AudioNode extends Node implements AudioSource {
* @deprecated AudioRenderer parameter is ignored. * @deprecated AudioRenderer parameter is ignored.
*/ */
public AudioNode(AudioRenderer audioRenderer, AssetManager assetManager, String name) { public AudioNode(AudioRenderer audioRenderer, AssetManager assetManager, String name) {
this(assetManager, name, false); this(assetManager, name, DataType.Buffer);
} }
/** /**
@ -175,9 +191,10 @@ public class AudioNode extends Node implements AudioSource {
* *
* @param assetManager The asset manager to use to load the audio file * @param assetManager The asset manager to use to load the audio file
* @param name The filename of the audio file * @param name The filename of the audio file
* @deprecated Use {@link AudioNode#AudioNode(com.jme3.asset.AssetManager, java.lang.String, com.jme3.audio.AudioData.DataType) } instead
*/ */
public AudioNode(AssetManager assetManager, String name) { public AudioNode(AssetManager assetManager, String name) {
this(assetManager, name, false); this(assetManager, name, DataType.Buffer);
} }
protected AudioRenderer getRenderer() { protected AudioRenderer getRenderer() {
@ -310,6 +327,19 @@ public class AudioNode extends Node implements AudioSource {
this.status = status; this.status = status;
} }
/**
* Get the Type of the underlying AudioData to see if it's streamed or buffered.
* This is a shortcut to getAudioData().getType()
* <b>Warning</b>: Can return null!
* @return The {@link com.jme3.audio.AudioData.DataType} of the audio node.
*/
public DataType getType() {
if (data == null)
return null;
else
return data.getDataType();
}
/** /**
* @return True if the audio will keep looping after it is done playing, * @return True if the audio will keep looping after it is done playing,
* otherwise, false. * otherwise, false.

@ -47,6 +47,8 @@ import com.jme3.renderer.RenderManager;
import com.jme3.renderer.ViewPort; import com.jme3.renderer.ViewPort;
import com.jme3.scene.Spatial; import com.jme3.scene.Spatial;
import com.jme3.scene.control.Control; import com.jme3.scene.control.Control;
import com.jme3.util.clone.Cloner;
import com.jme3.util.clone.JmeCloneable;
import java.io.IOException; import java.io.IOException;
/** /**
@ -56,7 +58,7 @@ import java.io.IOException;
* *
* @author Nehon * @author Nehon
*/ */
public class MotionEvent extends AbstractCinematicEvent implements Control { public class MotionEvent extends AbstractCinematicEvent implements Control, JmeCloneable {
protected Spatial spatial; protected Spatial spatial;
protected int currentWayPoint; protected int currentWayPoint;
@ -274,6 +276,7 @@ public class MotionEvent extends AbstractCinematicEvent implements Control {
* @param spatial * @param spatial
* @return * @return
*/ */
@Override
public Control cloneForSpatial(Spatial spatial) { public Control cloneForSpatial(Spatial spatial) {
MotionEvent control = new MotionEvent(spatial, path); MotionEvent control = new MotionEvent(spatial, path);
control.playState = playState; control.playState = playState;
@ -291,6 +294,31 @@ public class MotionEvent extends AbstractCinematicEvent implements Control {
return control; return control;
} }
@Override
public Object jmeClone() {
MotionEvent control = new MotionEvent();
control.path = 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;
control.spatial = spatial;
return control;
}
@Override
public void cloneFields( Cloner cloner, Object original ) {
this.spatial = cloner.clone(spatial);
}
@Override @Override
public void onPlay() { public void onPlay() {
traveledDistance = 0; traveledDistance = 0;

@ -54,6 +54,8 @@ import com.jme3.scene.Geometry;
import com.jme3.scene.Spatial; import com.jme3.scene.Spatial;
import com.jme3.scene.control.Control; import com.jme3.scene.control.Control;
import com.jme3.util.TempVars; import com.jme3.util.TempVars;
import com.jme3.util.clone.Cloner;
import com.jme3.util.clone.JmeCloneable;
import java.io.IOException; import java.io.IOException;
/** /**
@ -108,7 +110,7 @@ public class ParticleEmitter extends Geometry {
private transient Vector3f temp = new Vector3f(); private transient Vector3f temp = new Vector3f();
private transient Vector3f lastPos; private transient Vector3f lastPos;
public static class ParticleEmitterControl implements Control { public static class ParticleEmitterControl implements Control, JmeCloneable {
ParticleEmitter parentEmitter; ParticleEmitter parentEmitter;
@ -119,11 +121,26 @@ public class ParticleEmitter extends Geometry {
this.parentEmitter = parentEmitter; this.parentEmitter = parentEmitter;
} }
@Override
public Control cloneForSpatial(Spatial spatial) { public Control cloneForSpatial(Spatial spatial) {
return this; // WARNING: Sets wrong control on spatial. Will be return this; // WARNING: Sets wrong control on spatial. Will be
// fixed automatically by ParticleEmitter.clone() method. // fixed automatically by ParticleEmitter.clone() method.
} }
@Override
public Object jmeClone() {
try {
return super.clone();
} catch( CloneNotSupportedException e ) {
throw new RuntimeException("Error cloning", e);
}
}
@Override
public void cloneFields( Cloner cloner, Object original ) {
this.parentEmitter = cloner.clone(parentEmitter);
}
public void setSpatial(Spatial spatial) { public void setSpatial(Spatial spatial) {
} }

@ -43,13 +43,15 @@ import com.jme3.renderer.RenderManager;
import com.jme3.renderer.ViewPort; import com.jme3.renderer.ViewPort;
import com.jme3.scene.Spatial; import com.jme3.scene.Spatial;
import com.jme3.scene.control.Control; import com.jme3.scene.control.Control;
import com.jme3.util.clone.Cloner;
import com.jme3.util.clone.JmeCloneable;
import java.io.IOException; import java.io.IOException;
/** /**
* A camera that follows a spatial and can turn around it by dragging the mouse * A camera that follows a spatial and can turn around it by dragging the mouse
* @author nehon * @author nehon
*/ */
public class ChaseCamera implements ActionListener, AnalogListener, Control { public class ChaseCamera implements ActionListener, AnalogListener, Control, JmeCloneable {
protected Spatial target = null; protected Spatial target = null;
protected float minVerticalRotation = 0.00f; protected float minVerticalRotation = 0.00f;
@ -567,6 +569,7 @@ public class ChaseCamera implements ActionListener, AnalogListener, Control {
* @param spatial * @param spatial
* @return * @return
*/ */
@Override
public Control cloneForSpatial(Spatial spatial) { public Control cloneForSpatial(Spatial spatial) {
ChaseCamera cc = new ChaseCamera(cam, spatial, inputManager); ChaseCamera cc = new ChaseCamera(cam, spatial, inputManager);
cc.setMaxDistance(getMaxDistance()); cc.setMaxDistance(getMaxDistance());
@ -574,6 +577,23 @@ public class ChaseCamera implements ActionListener, AnalogListener, Control {
return cc; return cc;
} }
@Override
public Object jmeClone() {
ChaseCamera cc = new ChaseCamera(cam, inputManager);
cc.target = target;
cc.setMaxDistance(getMaxDistance());
cc.setMinDistance(getMinDistance());
return cc;
}
@Override
public void cloneFields( Cloner cloner, Object original ) {
this.target = cloner.clone(target);
computePosition();
prevPos = new Vector3f(target.getWorldTranslation());
cam.setLocation(pos);
}
/** /**
* Sets the spacial for the camera control, should only be used internally * Sets the spacial for the camera control, should only be used internally
* @param spatial * @param spatial

@ -248,16 +248,45 @@ When arrays can be inserted in J3M files
if (texKey.isFlipY()) { if (texKey.isFlipY()) {
ret += "Flip "; ret += "Flip ";
} }
if (texVal.getWrap(Texture.WrapAxis.S) == WrapMode.Repeat) {
ret += "Repeat "; //Wrap mode
ret += getWrapMode(texVal, Texture.WrapAxis.S);
ret += getWrapMode(texVal, Texture.WrapAxis.T);
ret += getWrapMode(texVal, Texture.WrapAxis.R);
//Min and Mag filter
Texture.MinFilter def = Texture.MinFilter.BilinearNoMipMaps;
if(texVal.getImage().hasMipmaps() || texKey.isGenerateMips()){
def = Texture.MinFilter.Trilinear;
}
if(texVal.getMinFilter() != def){
ret += "Min" + texVal.getMinFilter().name()+ " ";
} }
return ret + texKey.getName(); if(texVal.getMagFilter() != Texture.MagFilter.Bilinear){
ret += "Mag" + texVal.getMagFilter().name()+ " ";
}
return ret + "\"" + texKey.getName() + "\"";
default: default:
return null; // parameter type not supported in J3M return null; // parameter type not supported in J3M
} }
} }
private String getWrapMode(Texture texVal, Texture.WrapAxis axis) {
WrapMode mode = WrapMode.EdgeClamp;
try{
mode = texVal.getWrap(axis);
}catch (IllegalArgumentException e){
//this axis doesn't exist on the texture
return "";
}
if(mode != WrapMode.EdgeClamp){
return"Wrap"+ mode.name() + "_" + axis.name() + " ";
}
return "";
}
@Override @Override
public MatParam clone() { public MatParam clone() {
try { try {

@ -402,7 +402,9 @@ public class FilterPostProcessor implements SceneProcessor, Savable {
viewPort.setOutputFrameBuffer(outputBuffer); viewPort.setOutputFrameBuffer(outputBuffer);
viewPort = null; viewPort = null;
if(renderFrameBuffer != null){
renderFrameBuffer.dispose(); renderFrameBuffer.dispose();
}
if(depthTexture!=null){ if(depthTexture!=null){
depthTexture.getImage().dispose(); depthTexture.getImage().dispose();
} }

@ -31,14 +31,6 @@
*/ */
package com.jme3.scene; package com.jme3.scene;
import com.jme3.export.*;
import com.jme3.material.Material;
import com.jme3.math.Matrix4f;
import com.jme3.math.Vector3f;
import com.jme3.scene.mesh.IndexBuffer;
import com.jme3.util.SafeArrayList;
import com.jme3.util.TempVars;
import java.io.IOException;
import java.nio.Buffer; import java.nio.Buffer;
import java.nio.FloatBuffer; import java.nio.FloatBuffer;
import java.util.ArrayList; import java.util.ArrayList;
@ -48,13 +40,22 @@ import java.util.Map;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
import com.jme3.collision.Collidable;
import com.jme3.collision.CollisionResults;
import com.jme3.material.Material;
import com.jme3.math.Matrix4f;
import com.jme3.math.Vector3f;
import com.jme3.scene.mesh.IndexBuffer;
import com.jme3.util.SafeArrayList;
import com.jme3.util.TempVars;
/** /**
* BatchNode holds geometries that are a batched version of all the geometries that are in its sub scenegraph. * BatchNode holds geometries that are a batched version of all the geometries that are in its sub scenegraph.
* There is one geometry per different material in the sub tree. * There is one geometry per different material in the sub tree.
* The geometries are directly attached to the node in the scene graph. * The 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 the geometries have been attached to the sub scene graph and their material set * Usage is like any other node except you have to call the {@link #batch()} method once all the geometries have been attached to the sub scene graph and their material set
* (see todo more automagic for further enhancements) * (see todo more automagic for further enhancements)
* All the geometries that have been batched are set to {@link CullHint#Always} to not render them. * All the geometries that have been batched are set to not be rendered - {@link CullHint} is left intact.
* The sub geometries can be transformed as usual, their transforms are used to update the mesh of the geometryBatch. * 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 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 just be rendered as normal geometries. * Sub geoms can be added after the batch() method has been called but won't be batched and will just be rendered as normal geometries.
@ -72,7 +73,7 @@ public class BatchNode extends GeometryGroupNode {
*/ */
protected SafeArrayList<Batch> batches = new SafeArrayList<Batch>(Batch.class); protected SafeArrayList<Batch> batches = new SafeArrayList<Batch>(Batch.class);
/** /**
* a map storing he batches by geometry to quickly acces the batch when updating * a map for storing the batches by geometry to quickly access the batch when updating
*/ */
protected Map<Geometry, Batch> batchesByGeom = new HashMap<Geometry, Batch>(); protected Map<Geometry, Batch> batchesByGeom = new HashMap<Geometry, Batch>();
/** /**
@ -115,11 +116,10 @@ public class BatchNode extends GeometryGroupNode {
} }
@Override @Override
public void onGeoemtryUnassociated(Geometry geom) { public void onGeometryUnassociated(Geometry geom) {
setNeedsFullRebatch(true); setNeedsFullRebatch(true);
} }
protected Matrix4f getTransformMatrix(Geometry g){ protected Matrix4f getTransformMatrix(Geometry g){
return g.cachedWorldMat; return g.cachedWorldMat;
} }
@ -177,7 +177,7 @@ public class BatchNode extends GeometryGroupNode {
Map<Material, List<Geometry>> matMap = new HashMap<Material, List<Geometry>>(); Map<Material, List<Geometry>> matMap = new HashMap<Material, List<Geometry>>();
int nbGeoms = 0; int nbGeoms = 0;
gatherGeomerties(matMap, this, needsFullRebatch); gatherGeometries(matMap, this, needsFullRebatch);
if (needsFullRebatch) { if (needsFullRebatch) {
for (Batch batch : batches.getArray()) { for (Batch batch : batches.getArray()) {
batch.geometry.removeFromParent(); batch.geometry.removeFromParent();
@ -271,7 +271,7 @@ public class BatchNode extends GeometryGroupNode {
} }
private void gatherGeomerties(Map<Material, List<Geometry>> map, Spatial n, boolean rebatch) { private void gatherGeometries(Map<Material, List<Geometry>> map, Spatial n, boolean rebatch) {
if (n instanceof Geometry) { if (n instanceof Geometry) {
@ -304,7 +304,7 @@ public class BatchNode extends GeometryGroupNode {
if (child instanceof BatchNode) { if (child instanceof BatchNode) {
continue; continue;
} }
gatherGeomerties(map, child, rebatch); gatherGeometries(map, child, rebatch);
} }
} }
@ -319,7 +319,7 @@ public class BatchNode extends GeometryGroupNode {
return null; return null;
} }
private boolean isBatch(Spatial s) { public final boolean isBatch(Spatial s) {
for (Batch batch : batches.getArray()) { for (Batch batch : batches.getArray()) {
if (batch.geometry == s) { if (batch.geometry == s) {
return true; return true;
@ -336,9 +336,6 @@ public class BatchNode extends GeometryGroupNode {
*/ */
@Override @Override
public void setMaterial(Material material) { 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"); throw new UnsupportedOperationException("Unsupported for now, please set the material on the geoms before batching");
} }
@ -356,74 +353,7 @@ public class BatchNode extends GeometryGroupNode {
Batch b = batches.iterator().next(); Batch b = batches.iterator().next();
return b.geometry.getMaterial(); return b.geometry.getMaterial();
} }
return null;//material; return null;
}
// /**
// * 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);
// }
} }
/** /**
@ -510,8 +440,7 @@ public class BatchNode extends GeometryGroupNode {
outMesh.setMode(mode); outMesh.setMode(mode);
outMesh.setLineWidth(lineWidth); outMesh.setLineWidth(lineWidth);
if (totalVerts >= 65536) { if (totalVerts >= 65536) {
// make sure we create an UnsignedInt buffer so // make sure we create an UnsignedInt buffer so we can fit all of the meshes
// we can fit all of the meshes
formatForBuf[VertexBuffer.Type.Index.ordinal()] = VertexBuffer.Format.UnsignedInt; formatForBuf[VertexBuffer.Type.Index.ordinal()] = VertexBuffer.Format.UnsignedInt;
} else { } else {
formatForBuf[VertexBuffer.Type.Index.ordinal()] = VertexBuffer.Format.UnsignedShort; formatForBuf[VertexBuffer.Type.Index.ordinal()] = VertexBuffer.Format.UnsignedShort;
@ -733,7 +662,6 @@ public class BatchNode extends GeometryGroupNode {
} }
protected class Batch { protected class Batch {
/** /**
* update the batchesByGeom map for this batch with the given List of geometries * update the batchesByGeom map for this batch with the given List of geometries
* @param list * @param list
@ -746,6 +674,10 @@ public class BatchNode extends GeometryGroupNode {
} }
} }
Geometry geometry; Geometry geometry;
public final Geometry getGeometry() {
return geometry;
}
} }
protected void setNeedsFullRebatch(boolean needsFullRebatch) { protected void setNeedsFullRebatch(boolean needsFullRebatch) {
@ -771,4 +703,15 @@ public class BatchNode extends GeometryGroupNode {
} }
return clone; return clone;
} }
@Override
public int collideWith(Collidable other, CollisionResults results) {
int total = 0;
for (Spatial child : children.getArray()){
if (!isBatch(child)) {
total += child.collideWith(other, results);
}
}
return total;
}
} }

@ -344,7 +344,7 @@ public class Geometry extends Spatial {
if (groupNode != null) { if (groupNode != null) {
// Once the geometry is removed // Once the geometry is removed
// from the parent, the group node needs to be updated. // from the parent, the group node needs to be updated.
groupNode.onGeoemtryUnassociated(this); groupNode.onGeometryUnassociated(this);
groupNode = null; groupNode = null;
// change the default to -1 to make error detection easier // change the default to -1 to make error detection easier

@ -83,5 +83,5 @@ public abstract class GeometryGroupNode extends Node {
* *
* @param geom The Geometry which is being unassociated. * @param geom The Geometry which is being unassociated.
*/ */
public abstract void onGeoemtryUnassociated(Geometry geom); public abstract void onGeometryUnassociated(Geometry geom);
} }

@ -38,6 +38,8 @@ import com.jme3.export.OutputCapsule;
import com.jme3.renderer.RenderManager; import com.jme3.renderer.RenderManager;
import com.jme3.renderer.ViewPort; import com.jme3.renderer.ViewPort;
import com.jme3.scene.Spatial; import com.jme3.scene.Spatial;
import com.jme3.util.clone.Cloner;
import com.jme3.util.clone.JmeCloneable;
import java.io.IOException; import java.io.IOException;
/** /**
@ -45,7 +47,7 @@ import java.io.IOException;
* *
* @author Kirill Vainer * @author Kirill Vainer
*/ */
public abstract class AbstractControl implements Control { public abstract class AbstractControl implements Control, JmeCloneable {
protected boolean enabled = true; protected boolean enabled = true;
protected Spatial spatial; protected Spatial spatial;
@ -105,6 +107,20 @@ public abstract class AbstractControl implements Control {
} }
} }
@Override
public Object jmeClone() {
try {
return super.clone();
} catch( CloneNotSupportedException e ) {
throw new RuntimeException( "Can't clone control for spatial", e );
}
}
@Override
public void cloneFields( Cloner cloner, Object original ) {
this.spatial = cloner.clone(spatial);
}
public void update(float tpf) { public void update(float tpf) {
if (!enabled) if (!enabled)
return; return;

@ -86,12 +86,13 @@ public class BillboardControl extends AbstractControl {
alignment = Alignment.Screen; alignment = Alignment.Screen;
} }
public Control cloneForSpatial(Spatial spatial) { // default implementation from AbstractControl is equivalent
BillboardControl control = new BillboardControl(); //public Control cloneForSpatial(Spatial spatial) {
control.alignment = this.alignment; // BillboardControl control = new BillboardControl();
control.setSpatial(spatial); // control.alignment = this.alignment;
return control; // control.setSpatial(spatial);
} // return control;
//}
@Override @Override
protected void controlUpdate(float tpf) { protected void controlUpdate(float tpf) {

@ -136,13 +136,14 @@ public class CameraControl extends AbstractControl {
// nothing to do // nothing to do
} }
@Override // default implementation from AbstractControl is equivalent
public Control cloneForSpatial(Spatial newSpatial) { //@Override
CameraControl control = new CameraControl(camera, controlDir); //public Control cloneForSpatial(Spatial newSpatial) {
control.setSpatial(newSpatial); // CameraControl control = new CameraControl(camera, controlDir);
control.setEnabled(isEnabled()); // control.setSpatial(newSpatial);
return control; // control.setEnabled(isEnabled());
} // return control;
//}
private static final String CONTROL_DIR_NAME = "controlDir"; private static final String CONTROL_DIR_NAME = "controlDir";
private static final String CAMERA_NAME = "camera"; private static final String CAMERA_NAME = "camera";

@ -167,13 +167,14 @@ public class LightControl extends AbstractControl {
// nothing to do // nothing to do
} }
@Override // default implementation from AbstractControl is equivalent
public Control cloneForSpatial(Spatial newSpatial) { //@Override
LightControl control = new LightControl(light, controlDir); //public Control cloneForSpatial(Spatial newSpatial) {
control.setSpatial(newSpatial); // LightControl control = new LightControl(light, controlDir);
control.setEnabled(isEnabled()); // control.setSpatial(newSpatial);
return control; // control.setEnabled(isEnabled());
} // return control;
//}
private static final String CONTROL_DIR_NAME = "controlDir"; private static final String CONTROL_DIR_NAME = "controlDir";
private static final String LIGHT_NAME = "light"; private static final String LIGHT_NAME = "light";

@ -43,6 +43,8 @@ import com.jme3.renderer.ViewPort;
import com.jme3.scene.Geometry; import com.jme3.scene.Geometry;
import com.jme3.scene.Mesh; import com.jme3.scene.Mesh;
import com.jme3.scene.Spatial; import com.jme3.scene.Spatial;
import com.jme3.util.clone.Cloner;
import com.jme3.util.clone.JmeCloneable;
import java.io.IOException; import java.io.IOException;
/** /**
@ -56,7 +58,7 @@ import java.io.IOException;
* and will update the spatial's LOD if the camera has moved by a specified * and will update the spatial's LOD if the camera has moved by a specified
* amount. * amount.
*/ */
public class LodControl extends AbstractControl implements Cloneable { public class LodControl extends AbstractControl implements Cloneable, JmeCloneable {
private float trisPerPixel = 1f; private float trisPerPixel = 1f;
private float distTolerance = 1f; private float distTolerance = 1f;
@ -142,6 +144,15 @@ public class LodControl extends AbstractControl implements Cloneable {
return clone; return clone;
} }
@Override
public Object jmeClone() {
LodControl clone = (LodControl)super.jmeClone();
clone.lastDistance = 0;
clone.lastLevel = 0;
clone.numTris = numTris != null ? numTris.clone() : null;
return clone;
}
@Override @Override
protected void controlUpdate(float tpf) { protected void controlUpdate(float tpf) {
} }

@ -35,6 +35,8 @@ import com.jme3.app.AppTask;
import com.jme3.renderer.RenderManager; import com.jme3.renderer.RenderManager;
import com.jme3.renderer.ViewPort; import com.jme3.renderer.ViewPort;
import com.jme3.scene.Spatial; import com.jme3.scene.Spatial;
import com.jme3.util.clone.Cloner;
import com.jme3.util.clone.JmeCloneable;
import java.util.concurrent.Callable; import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.Future; import java.util.concurrent.Future;
@ -85,6 +87,7 @@ public class UpdateControl extends AbstractControl {
} }
@Override
public Control cloneForSpatial(Spatial newSpatial) { public Control cloneForSpatial(Spatial newSpatial) {
UpdateControl control = new UpdateControl(); UpdateControl control = new UpdateControl();
control.setSpatial(newSpatial); control.setSpatial(newSpatial);
@ -93,4 +96,15 @@ public class UpdateControl extends AbstractControl {
return control; return control;
} }
@Override
public Object jmeClone() {
UpdateControl clone = (UpdateControl)super.jmeClone();
// This is kind of questionable since the tasks aren't cloned and have
// no reference to the new spatial or anything. They'll get run again
// but it's not clear to me why that would be desired. I'm doing it
// because the old cloneForSpatial() code does. FIXME? -pspeed
clone.taskQueue.addAll(taskQueue);
return clone;
}
} }

@ -44,6 +44,8 @@ import com.jme3.scene.control.Control;
import com.jme3.export.JmeExporter; import com.jme3.export.JmeExporter;
import com.jme3.export.JmeImporter; import com.jme3.export.JmeImporter;
import com.jme3.material.MatParam; import com.jme3.material.MatParam;
import com.jme3.util.clone.Cloner;
import com.jme3.util.clone.JmeCloneable;
import java.io.IOException; import java.io.IOException;
import java.util.HashMap; import java.util.HashMap;
@ -106,7 +108,7 @@ public class InstancedNode extends GeometryGroupNode {
} }
} }
private static class InstancedNodeControl implements Control { private static class InstancedNodeControl implements Control, JmeCloneable {
private InstancedNode node; private InstancedNode node;
@ -124,6 +126,20 @@ public class InstancedNode extends GeometryGroupNode {
// fixed automatically by InstancedNode.clone() method. // fixed automatically by InstancedNode.clone() method.
} }
@Override
public Object jmeClone() {
try {
return super.clone();
} catch( CloneNotSupportedException e ) {
throw new RuntimeException("Error cloning control", e);
}
}
@Override
public void cloneFields( Cloner cloner, Object original ) {
this.node = cloner.clone(node);
}
public void setSpatial(Spatial spatial){ public void setSpatial(Spatial spatial){
} }
@ -329,7 +345,7 @@ public class InstancedNode extends GeometryGroupNode {
} }
@Override @Override
public void onGeoemtryUnassociated(Geometry geom) { public void onGeometryUnassociated(Geometry geom) {
removeFromInstancedGeometry(geom); removeFromInstancedGeometry(geom);
} }
} }

@ -211,7 +211,7 @@ public class Cylinder extends Mesh {
*/ */
public void updateGeometry(int axisSamples, int radialSamples, public void updateGeometry(int axisSamples, int radialSamples,
float radius, float radius2, float height, boolean closed, boolean inverted) { float radius, float radius2, float height, boolean closed, boolean inverted) {
this.axisSamples = axisSamples + (closed ? 2 : 0); this.axisSamples = axisSamples;
this.radialSamples = radialSamples; this.radialSamples = radialSamples;
this.radius = radius; this.radius = radius;
this.radius2 = radius2; this.radius2 = radius2;
@ -222,6 +222,7 @@ public class Cylinder extends Mesh {
// VertexBuffer pvb = getBuffer(Type.Position); // VertexBuffer pvb = getBuffer(Type.Position);
// VertexBuffer nvb = getBuffer(Type.Normal); // VertexBuffer nvb = getBuffer(Type.Normal);
// VertexBuffer tvb = getBuffer(Type.TexCoord); // VertexBuffer tvb = getBuffer(Type.TexCoord);
axisSamples += (closed ? 2 : 0);
// Vertices // Vertices
int vertCount = axisSamples * (radialSamples + 1) + (closed ? 2 : 0); int vertCount = axisSamples * (radialSamples + 1) + (closed ? 2 : 0);

@ -37,6 +37,7 @@ import com.jme3.export.JmeExporter;
import com.jme3.export.JmeImporter; import com.jme3.export.JmeImporter;
import com.jme3.export.OutputCapsule; import com.jme3.export.OutputCapsule;
import com.jme3.material.Material; import com.jme3.material.Material;
import com.jme3.material.RenderState;
import com.jme3.math.Matrix4f; import com.jme3.math.Matrix4f;
import com.jme3.math.Vector4f; import com.jme3.math.Vector4f;
import com.jme3.post.Filter; import com.jme3.post.Filter;
@ -44,6 +45,7 @@ import com.jme3.renderer.RenderManager;
import com.jme3.renderer.ViewPort; import com.jme3.renderer.ViewPort;
import com.jme3.renderer.queue.RenderQueue; import com.jme3.renderer.queue.RenderQueue;
import com.jme3.texture.FrameBuffer; import com.jme3.texture.FrameBuffer;
import java.io.IOException; import java.io.IOException;
/** /**
@ -74,6 +76,9 @@ public abstract class AbstractShadowFilter<T extends AbstractShadowRenderer> ext
material = new Material(manager, "Common/MatDefs/Shadow/PostShadowFilter.j3md"); material = new Material(manager, "Common/MatDefs/Shadow/PostShadowFilter.j3md");
this.shadowRenderer = shadowRenderer; this.shadowRenderer = shadowRenderer;
this.shadowRenderer.setPostShadowMaterial(material); this.shadowRenderer.setPostShadowMaterial(material);
//this is legacy setting for shadows with backface shadows
this.shadowRenderer.setRenderBackFacesShadows(true);
} }
@Override @Override
@ -126,7 +131,7 @@ public abstract class AbstractShadowFilter<T extends AbstractShadowRenderer> ext
/** /**
* How far the shadows are rendered in the view * How far the shadows are rendered in the view
* *
* @see setShadowZExtend(float zFar) * @see #setShadowZExtend(float zFar)
* @return shadowZExtend * @return shadowZExtend
*/ */
public float getShadowZExtend() { public float getShadowZExtend() {
@ -248,6 +253,46 @@ public abstract class AbstractShadowFilter<T extends AbstractShadowRenderer> ext
shadowRenderer.setEdgeFilteringMode(filterMode); shadowRenderer.setEdgeFilteringMode(filterMode);
} }
/**
*
* !! WARNING !! this parameter is defaulted to true for the ShadowFilter.
* Setting it to true, may produce edges artifacts on shadows. *
*
* Set to true if you want back faces shadows on geometries.
* Note that back faces shadows will be blended over dark lighten areas and may produce overly dark lighting.
*
* Setting this parameter will override this parameter for ALL materials in the scene.
* This also will automatically adjust the faceCullMode and the PolyOffset of the pre shadow pass.
* You can modify them by using {@link #getPreShadowForcedRenderState()}
*
* If you want to set it differently for each material in the scene you have to use the ShadowRenderer instead
* of the shadow filter.
*
* @param renderBackFacesShadows true or false.
*/
public void setRenderBackFacesShadows(Boolean renderBackFacesShadows) {
shadowRenderer.setRenderBackFacesShadows(renderBackFacesShadows);
}
/**
* if this filter renders back faces shadows
* @return true if this filter renders back faces shadows
*/
public boolean isRenderBackFacesShadows() {
return shadowRenderer.isRenderBackFacesShadows();
}
/**
* returns the pre shadows pass render state.
* use it to adjust the RenderState parameters of the pre shadow pass.
* Note that this will be overriden if the preShadow technique in the material has a ForcedRenderState
* @return the pre shadow render state.
*/
public RenderState getPreShadowForcedRenderState() {
return shadowRenderer.getPreShadowForcedRenderState();
}
/** /**
* returns the the edge filtering mode * returns the the edge filtering mode
* *

@ -31,10 +31,6 @@
*/ */
package com.jme3.shadow; package com.jme3.shadow;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import com.jme3.asset.AssetManager; import com.jme3.asset.AssetManager;
import com.jme3.export.InputCapsule; import com.jme3.export.InputCapsule;
import com.jme3.export.JmeExporter; import com.jme3.export.JmeExporter;
@ -42,6 +38,7 @@ import com.jme3.export.JmeImporter;
import com.jme3.export.OutputCapsule; import com.jme3.export.OutputCapsule;
import com.jme3.export.Savable; import com.jme3.export.Savable;
import com.jme3.material.Material; import com.jme3.material.Material;
import com.jme3.material.RenderState;
import com.jme3.math.ColorRGBA; import com.jme3.math.ColorRGBA;
import com.jme3.math.Matrix4f; import com.jme3.math.Matrix4f;
import com.jme3.math.Vector2f; import com.jme3.math.Vector2f;
@ -67,6 +64,10 @@ import com.jme3.texture.Texture.ShadowCompareMode;
import com.jme3.texture.Texture2D; import com.jme3.texture.Texture2D;
import com.jme3.ui.Picture; import com.jme3.ui.Picture;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/** /**
* abstract shadow renderer that holds commons feature to have for a shadow * abstract shadow renderer that holds commons feature to have for a shadow
* renderer * renderer
@ -92,6 +93,9 @@ public abstract class AbstractShadowRenderer implements SceneProcessor, Savable
protected EdgeFilteringMode edgeFilteringMode = EdgeFilteringMode.Bilinear; protected EdgeFilteringMode edgeFilteringMode = EdgeFilteringMode.Bilinear;
protected CompareMode shadowCompareMode = CompareMode.Hardware; protected CompareMode shadowCompareMode = CompareMode.Hardware;
protected Picture[] dispPic; protected Picture[] dispPic;
protected RenderState forcedRenderState = new RenderState();
protected Boolean renderBackFacesShadows;
/** /**
* true if the fallback material should be used, otherwise false * true if the fallback material should be used, otherwise false
*/ */
@ -181,6 +185,14 @@ public abstract class AbstractShadowRenderer implements SceneProcessor, Savable
setShadowCompareMode(shadowCompareMode); setShadowCompareMode(shadowCompareMode);
setEdgeFilteringMode(edgeFilteringMode); setEdgeFilteringMode(edgeFilteringMode);
setShadowIntensity(shadowIntensity); setShadowIntensity(shadowIntensity);
initForcedRenderState();
}
protected void initForcedRenderState() {
forcedRenderState.setFaceCullMode(RenderState.FaceCullMode.Front);
forcedRenderState.setColorWrite(false);
forcedRenderState.setDepthWrite(true);
forcedRenderState.setDepthTest(true);
} }
/** /**
@ -356,9 +368,7 @@ public abstract class AbstractShadowRenderer implements SceneProcessor, Savable
* rendered in the shadow map * rendered in the shadow map
* *
* @param shadowMapIndex the index of the shadow map being rendered * @param shadowMapIndex the index of the shadow map being rendered
* @param sceneOccluders the occluders of the whole scene * @param shadowMapOccluders the list of occluders
* @param sceneReceivers the receivers of the whole scene
* @param shadowMapOcculders
* @return * @return
*/ */
protected abstract GeometryList getOccludersToRender(int shadowMapIndex, GeometryList shadowMapOccluders); protected abstract GeometryList getOccludersToRender(int shadowMapIndex, GeometryList shadowMapOccluders);
@ -425,9 +435,11 @@ public abstract class AbstractShadowRenderer implements SceneProcessor, Savable
renderManager.getRenderer().setFrameBuffer(shadowFB[shadowMapIndex]); renderManager.getRenderer().setFrameBuffer(shadowFB[shadowMapIndex]);
renderManager.getRenderer().clearBuffers(true, true, true); renderManager.getRenderer().clearBuffers(true, true, true);
renderManager.setForcedRenderState(forcedRenderState);
// render shadow casters to shadow map // render shadow casters to shadow map
viewPort.getQueue().renderShadowQueue(shadowMapOccluders, renderManager, shadowCam, true); viewPort.getQueue().renderShadowQueue(shadowMapOccluders, renderManager, shadowCam, true);
renderManager.setForcedRenderState(null);
} }
boolean debugfrustums = false; boolean debugfrustums = false;
@ -535,18 +547,7 @@ public abstract class AbstractShadowRenderer implements SceneProcessor, Savable
private void setMatParams(GeometryList l) { private void setMatParams(GeometryList l) {
//iteration throught all the geometries of the list to gather the materials //iteration throught all the geometries of the list to gather the materials
matCache.clear(); buildMatCache(l);
for (int i = 0; i < l.size(); i++) {
Material mat = l.get(i).getMaterial();
//checking if the material has the post technique and adding it to the material cache
if (mat.getMaterialDef().getTechniqueDef(postTechniqueName) != null) {
if (!matCache.contains(mat)) {
matCache.add(mat);
}
} else {
needsfallBackMaterial = true;
}
}
//iterating through the mat cache and setting the parameters //iterating through the mat cache and setting the parameters
for (Material mat : matCache) { for (Material mat : matCache) {
@ -566,6 +567,10 @@ public abstract class AbstractShadowRenderer implements SceneProcessor, Savable
if (fadeInfo != null) { if (fadeInfo != null) {
mat.setVector2("FadeInfo", fadeInfo); mat.setVector2("FadeInfo", fadeInfo);
} }
if(renderBackFacesShadows != null){
mat.setBoolean("BackfaceShadows", renderBackFacesShadows);
}
setMaterialParameters(mat); setMaterialParameters(mat);
} }
@ -577,6 +582,21 @@ public abstract class AbstractShadowRenderer implements SceneProcessor, Savable
} }
private void buildMatCache(GeometryList l) {
matCache.clear();
for (int i = 0; i < l.size(); i++) {
Material mat = l.get(i).getMaterial();
//checking if the material has the post technique and adding it to the material cache
if (mat.getMaterialDef().getTechniqueDef(postTechniqueName) != null) {
if (!matCache.contains(mat)) {
matCache.add(mat);
}
} else {
needsfallBackMaterial = true;
}
}
}
/** /**
* for internal use only * for internal use only
*/ */
@ -589,6 +609,9 @@ public abstract class AbstractShadowRenderer implements SceneProcessor, Savable
if (fadeInfo != null) { if (fadeInfo != null) {
postshadowMat.setVector2("FadeInfo", fadeInfo); postshadowMat.setVector2("FadeInfo", fadeInfo);
} }
if(renderBackFacesShadows != null){
postshadowMat.setBoolean("BackfaceShadows", renderBackFacesShadows);
}
} }
/** /**
@ -730,6 +753,48 @@ public abstract class AbstractShadowRenderer implements SceneProcessor, Savable
@Deprecated @Deprecated
public void setFlushQueues(boolean flushQueues) {} public void setFlushQueues(boolean flushQueues) {}
/**
* returns the pre shadows pass render state.
* use it to adjust the RenderState parameters of the pre shadow pass.
* Note that this will be overriden if the preShadow technique in the material has a ForcedRenderState
* @return the pre shadow render state.
*/
public RenderState getPreShadowForcedRenderState() {
return forcedRenderState;
}
/**
* Set to true if you want back faces shadows on geometries.
* Note that back faces shadows will be blended over dark lighten areas and may produce overly dark lighting.
*
* Also note that setting this parameter will override this parameter for ALL materials in the scene.
* You can alternatively change this parameter on a single material using {@link Material#setBoolean(String, boolean)}
*
* This also will automatically adjust the faceCullMode and the PolyOffset of the pre shadow pass.
* You can modify them by using {@link #getPreShadowForcedRenderState()}
*
* @param renderBackFacesShadows true or false.
*/
public void setRenderBackFacesShadows(Boolean renderBackFacesShadows) {
this.renderBackFacesShadows = renderBackFacesShadows;
if(renderBackFacesShadows) {
getPreShadowForcedRenderState().setPolyOffset(5, 3);
getPreShadowForcedRenderState().setFaceCullMode(RenderState.FaceCullMode.Back);
}else{
getPreShadowForcedRenderState().setPolyOffset(0, 0);
getPreShadowForcedRenderState().setFaceCullMode(RenderState.FaceCullMode.Front);
}
}
/**
* if this processor renders back faces shadows
* @return true if this processor renders back faces shadows
*/
public boolean isRenderBackFacesShadows() {
return renderBackFacesShadows != null?renderBackFacesShadows:false;
}
/** /**
* De-serialize this instance, for example when loading from a J3O file. * De-serialize this instance, for example when loading from a J3O file.
* *

@ -215,6 +215,7 @@ public class DirectionalLightShadowRenderer extends AbstractShadowRenderer {
@Override @Override
protected void setMaterialParameters(Material material) { protected void setMaterialParameters(Material material) {
material.setColor("Splits", splits); material.setColor("Splits", splits);
material.setVector3("LightDir", light.getDirection());
if (fadeInfo != null) { if (fadeInfo != null) {
material.setVector2("FadeInfo", fadeInfo); material.setVector2("FadeInfo", fadeInfo);
} }
@ -224,6 +225,7 @@ public class DirectionalLightShadowRenderer extends AbstractShadowRenderer {
protected void clearMaterialParameters(Material material) { protected void clearMaterialParameters(Material material) {
material.clearParam("Splits"); material.clearParam("Splits");
material.clearParam("FadeInfo"); material.clearParam("FadeInfo");
material.clearParam("LightDir");
} }
/** /**

@ -157,6 +157,8 @@ public abstract class JmeSystemDelegate {
return false; return false;
} else if (arch.equals("aarch64")) { } else if (arch.equals("aarch64")) {
return true; return true;
} else if (arch.equals("armv7") || arch.equals("armv7l")) {
return false;
} else if (arch.equals("arm")) { } else if (arch.equals("arm")) {
return false; return false;
} else { } else {

@ -51,7 +51,6 @@ import java.nio.IntBuffer;
import java.nio.LongBuffer; import java.nio.LongBuffer;
import java.nio.ShortBuffer; import java.nio.ShortBuffer;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
@ -1268,7 +1267,6 @@ public final class BufferUtils {
System.out.println(store.toString()); System.out.println(store.toString());
} }
} }
private static final AtomicBoolean loadedMethods = new AtomicBoolean(false);
private static Method cleanerMethod = null; private static Method cleanerMethod = null;
private static Method cleanMethod = null; private static Method cleanMethod = null;
private static Method viewedBufferMethod = null; private static Method viewedBufferMethod = null;
@ -1288,14 +1286,7 @@ public final class BufferUtils {
} }
} }
private static void loadCleanerMethods() { static {
// If its already true, exit, if not, set it to true.
if (BufferUtils.loadedMethods.getAndSet(true)) {
return;
}
// This could potentially be called many times if used from multiple
// threads
synchronized (BufferUtils.loadedMethods) {
// Oracle JRE / OpenJDK // Oracle JRE / OpenJDK
cleanerMethod = loadMethod("sun.nio.ch.DirectBuffer", "cleaner"); cleanerMethod = loadMethod("sun.nio.ch.DirectBuffer", "cleaner");
cleanMethod = loadMethod("sun.misc.Cleaner", "clean"); cleanMethod = loadMethod("sun.misc.Cleaner", "clean");
@ -1314,7 +1305,6 @@ public final class BufferUtils {
} catch (SecurityException ex) { } catch (SecurityException ex) {
} }
} }
}
/** /**
* Direct buffers are garbage collected by using a phantom reference and a * Direct buffers are garbage collected by using a phantom reference and a
@ -1333,8 +1323,6 @@ public final class BufferUtils {
return; return;
} }
BufferUtils.loadCleanerMethods();
try { try {
if (freeMethod != null) { if (freeMethod != null) {
freeMethod.invoke(toBeDestroyed); freeMethod.invoke(toBeDestroyed);

@ -0,0 +1,81 @@
/*
* Copyright (c) 2016 jMonkeyEngine
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR 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.clone;
/**
* Provides custom cloning for a particular object type. Once
* registered with the Cloner, this function object will be called twice
* for any cloned object that matches the class for which it was registered.
* It will first call cloneObject() to shallow clone the object and then call
* cloneFields() to deep clone the object's values.
*
* <p>This two step process is important because this is what allows
* circular references in the cloned object graph.</p>
*
* @author Paul Speed
*/
public interface CloneFunction<T> {
/**
* Performs a shallow clone of the specified object. This is similar
* to the JmeCloneable.clone() method in semantics and is the first part
* of a two part cloning process. Once the shallow clone is created, it
* is cached and CloneFunction.cloneFields() is called. In this way,
* the CloneFunction interface can completely take over the JmeCloneable
* style cloning for an object that doesn't otherwise implement that interface.
*
* @param cloner The cloner performing the cloning operation.
* @param original The original object that needs to be cloned.
*/
public T cloneObject( Cloner cloner, T original );
/**
* Performs a deep clone of the specified clone's fields. This is similar
* to the JmeCloneable.cloneFields() method in semantics and is the second part
* of a two part cloning process. Once the shallow clone is created, it
* is cached and CloneFunction.cloneFields() is called. In this way,
* the CloneFunction interface can completely take over the JmeCloneable
* style cloning for an object that doesn't otherwise implement that interface.
*
* @param cloner The cloner performing the cloning operation.
* @param clone The clone previously returned from cloneObject().
* @param original The original object that was cloned. This is provided for
* the very special case where field cloning needs to refer to
* the original object. Mostly the necessary fields should already
* be on the clone.
*/
public void cloneFields( Cloner cloner, T clone, T original );
}

@ -0,0 +1,348 @@
/*
* Copyright (c) 2016 jMonkeyEngine
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR 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.clone;
import java.lang.reflect.Array;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* A deep clone utility that provides similar object-graph-preserving
* qualities to typical serialization schemes. An internal registry
* of cloned objects is kept to be used by other objects in the deep
* clone process that implement JmeCloneable.
*
* <p>By default, objects that do not implement JmeCloneable will
* be treated like normal Java Cloneable objects. If the object does
* not implement the JmeCloneable or the regular JDK Cloneable interfaces
* AND has no special handling defined then an IllegalArgumentException
* will be thrown.</p>
*
* <p>Enhanced object cloning is done in a two step process. First,
* the object is cloned using the normal Java clone() method and stored
* in the clone registry. After that, if it implements JmeCloneable then
* its cloneFields() method is called to deep clone any of the fields.
* This two step process has a few benefits. First, it means that objects
* can easily have a regular shallow clone implementation just like any
* normal Java objects. Second, the deep cloning of fields happens after
* creation wich means that the clone is available to future field cloning
* to resolve circular references.</p>
*
* <p>Similar to Java serialization, the handling of specific object
* types can be customized. This allows certain objects to be cloned gracefully
* even if they aren't normally Cloneable. This can also be used as a
* sort of filter to keep certain types of objects from being cloned.
* (For example, adding the IdentityCloneFunction for Mesh.class would cause
* all mesh instances to be shared with the original object graph.)</p>
*
* <p>By default, the Cloner registers serveral default clone functions
* as follows:</p>
* <ul>
* <li>java.util.ArrayList: ListCloneFunction
* <li>java.util.LinkedList: ListCloneFunction
* <li>java.util.concurrent.CopyOnWriteArrayList: ListCloneFunction
* <li>java.util.Vector: ListCloneFunction
* <li>java.util.Stack: ListCloneFunction
* <li>com.jme3.util.SafeArrayList: ListCloneFunction
* </ul>
*
* <p>Usage:</p>
* <pre>
* // Example 1: using an instantiated, reusable cloner.
* Cloner cloner = new Cloner();
* Foo fooClone = cloner.clone(foo);
* cloner.clearIndex(); // prepare it for reuse
* Foo fooClone2 = cloner.clone(foo);
*
* // Example 2: using the utility method that self-instantiates a temporary cloner.
* Foo fooClone = Cloner.deepClone(foo);
*
* </pre>
*
* @author Paul Speed
*/
public class Cloner {
/**
* Keeps track of the objects that have been cloned so far.
*/
private IdentityHashMap<Object, Object> index = new IdentityHashMap<Object, Object>();
/**
* Custom functions for cloning objects.
*/
private Map<Class, CloneFunction> functions = new HashMap<Class, CloneFunction>();
/**
* Cache the clone methods once for all cloners.
*/
private static final Map<Class, Method> methodCache = new ConcurrentHashMap<>();
/**
* Creates a new cloner with only default clone functions and an empty
* object index.
*/
public Cloner() {
// Register some standard types
ListCloneFunction listFunction = new ListCloneFunction();
functions.put(java.util.ArrayList.class, listFunction);
functions.put(java.util.LinkedList.class, listFunction);
functions.put(java.util.concurrent.CopyOnWriteArrayList.class, listFunction);
functions.put(java.util.Vector.class, listFunction);
functions.put(java.util.Stack.class, listFunction);
functions.put(com.jme3.util.SafeArrayList.class, listFunction);
}
/**
* Convenience utility function that creates a new Cloner, uses it to
* deep clone the object, and then returns the result.
*/
public static <T> T deepClone( T object ) {
return new Cloner().clone(object);
}
/**
* Deeps clones the specified object, reusing previous clones when possible.
*
* <p>Object cloning priority works as follows:</p>
* <ul>
* <li>If the object has already been cloned then its clone is returned.
* <li>If there is a custom CloneFunction then it is called to clone the object.
* <li>If the object implements Cloneable then its clone() method is called, arrays are
* deep cloned with entries passing through clone().
* <li>If the object implements JmeCloneable then its cloneFields() method is called on the
* clone.
* <li>Else an IllegalArgumentException is thrown.
* </ul>
*
* Note: objects returned by this method may not have yet had their cloneField()
* method called.
*/
public <T> T clone( T object ) {
return clone(object, true);
}
/**
* Internal method to work around a Java generics typing issue by
* isolating the 'bad' case into a method with suppressed warnings.
*/
@SuppressWarnings("unchecked")
private <T> Class<T> objectClass( T object ) {
// This should be 100% allowed without a cast but Java generics
// is not that smart sometimes.
// Wrapping it in a method at least isolates the warning suppression
return (Class<T>)object.getClass();
}
/**
* Deeps clones the specified object, reusing previous clones when possible.
*
* <p>Object cloning priority works as follows:</p>
* <ul>
* <li>If the object has already been cloned then its clone is returned.
* <li>If useFunctions is true and there is a custom CloneFunction then it is
* called to clone the object.
* <li>If the object implements Cloneable then its clone() method is called, arrays are
* deep cloned with entries passing through clone().
* <li>If the object implements JmeCloneable then its cloneFields() method is called on the
* clone.
* <li>Else an IllegalArgumentException is thrown.
* </ul>
*
* <p>The abililty to selectively use clone functions is useful when
* being called from a clone function.</p>
*
* Note: objects returned by this method may not have yet had their cloneField()
* method called.
*/
public <T> T clone( T object, boolean useFunctions ) {
if( object == null ) {
return null;
}
Class<T> type = objectClass(object);
// Check the index to see if we already have it
Object clone = index.get(object);
if( clone != null ) {
return type.cast(clone);
}
// See if there is a custom function... that trumps everything.
CloneFunction<T> f = getCloneFunction(type);
if( f != null ) {
T result = f.cloneObject(this, object);
// Store the object in the identity map so that any circular references
// are resolvable.
index.put(object, result);
// Now call the function again to deep clone the fields
f.cloneFields(this, result, object);
return result;
}
if( object.getClass().isArray() ) {
// Perform an array clone
clone = arrayClone(object);
// Array clone already indexes the clone
} else if( object instanceof JmeCloneable ) {
// Use the two-step cloning semantics
clone = ((JmeCloneable)object).jmeClone();
// Store the object in the identity map so that any circular references
// are resolvable
index.put(object, clone);
((JmeCloneable)clone).cloneFields(this, object);
} else if( object instanceof Cloneable ) {
// Perform a regular Java shallow clone
try {
clone = javaClone(object);
} catch( CloneNotSupportedException e ) {
throw new IllegalArgumentException("Object is not cloneable, type:" + type, e);
}
// Store the object in the identity map so that any circular references
// are resolvable
index.put(object, clone);
} else {
throw new IllegalArgumentException("Object is not cloneable, type:" + type);
}
return type.cast(clone);
}
/**
* Sets a custom CloneFunction for the exact Java type. Note: no inheritence
* checks are performed so a function must be registered for each specific type
* that it handles. By default ListCloneFunction is registered for
* ArrayList, LinkedList, CopyOnWriteArrayList, Vector, Stack, and JME's SafeArrayList.
*/
public <T> void setCloneFunction( Class<T> type, CloneFunction<T> function ) {
if( function == null ) {
functions.remove(type);
} else {
functions.put(type, function);
}
}
/**
* Returns a previously registered clone function for the specified type or null
* if there is no custom clone function for the type.
*/
@SuppressWarnings("unchecked")
public <T> CloneFunction<T> getCloneFunction( Class<T> type ) {
return (CloneFunction<T>)functions.get(type);
}
/**
* Clears the object index allowing the cloner to be reused for a brand new
* cloning operation.
*/
public void clearIndex() {
index.clear();
}
/**
* Performs a raw shallow Java clone using reflection. This call does NOT
* check against the clone index and so will return new objects every time
* it is called. That's because these are shallow clones and have not (and may
* not ever, depending on the caller) get resolved.
*
* <p>This method is provided as a convenient way for CloneFunctions to call
* clone() and objects without necessarily knowing their real type.</p>
*/
public <T> T javaClone( T object ) throws CloneNotSupportedException {
Method m = methodCache.get(object.getClass());
if( m == null ) {
try {
// Lookup the method and cache it
m = object.getClass().getMethod("clone");
} catch( NoSuchMethodException e ) {
throw new CloneNotSupportedException("No public clone method found for:" + object.getClass());
}
methodCache.put(object.getClass(), m);
// Note: yes we might cache the method twice... but so what?
}
try {
Class<? extends T> type = objectClass(object);
return type.cast(m.invoke(object));
} catch( IllegalAccessException | InvocationTargetException e ) {
throw new RuntimeException("Error cloning object of type:" + object.getClass(), e);
}
}
/**
* Clones a primitive array by coping it and clones an object
* array by coping it and then running each of its values through
* Cloner.clone().
*/
protected <T> T arrayClone( T object ) {
// Java doesn't support the cloning of arrays through reflection unless
// you open access to Object's protected clone array... which requires
// elevated privileges. So we will do a work-around that is slightly less
// elegant.
// This should be 100% allowed without a case but Java generics
// is not that smart
Class<T> type = objectClass(object);
Class elementType = type.getComponentType();
int size = Array.getLength(object);
Object clone = Array.newInstance(elementType, size);
// Store the clone for later lookups
index.put(object, clone);
if( elementType.isPrimitive() ) {
// Then our job is a bit easier
System.arraycopy(object, 0, clone, 0, size);
} else {
// Else it's an object array so we'll clone it and its children
for( int i = 0; i < size; i++ ) {
Object element = clone(Array.get(object, i));
Array.set(clone, i, element);
}
}
return type.cast(clone);
}
}

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2009-2010 jMonkeyEngine * Copyright (c) 2016 jMonkeyEngine
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
@ -30,34 +30,29 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package com.jme3.gde.terraineditor; package com.jme3.util.clone;
import com.jme3.terrain.heightmap.AbstractHeightMap;
/** /**
* Very simple heightmap class that is a heightmap of floats that is size*size * A CloneFunction implementation that simply returns the
* in size, and has height values of zero. * the passed object without cloning it. This is useful for
* forcing some object types (like Meshes) to be shared between
* the original and cloned object graph.
* *
* @author bowens * @author Paul Speed
*/ */
public class FlatHeightmap extends AbstractHeightMap { public class IdentityCloneFunction<T> implements CloneFunction<T> {
private int size; /**
private float[] heightmapData; * Returns the object directly.
*/
public FlatHeightmap(int size) { public T cloneObject( Cloner cloner, T object ) {
this.size = size; return object;
}
@Override
public boolean load() {
heightmapData = new float[size*size];
return true;
} }
@Override /**
public float[] getHeightMap() { * Does nothing.
return heightmapData; */
public void cloneFields( Cloner cloner, T clone, T object ) {
} }
} }

@ -0,0 +1,99 @@
/*
* Copyright (c) 2016 jMonkeyEngine
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR 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.clone;
/**
* Indicates an object that wishes to more actively participate in the
* two-part deep copying process provided by the Cloner. Objects implementing
* this interface can access the already cloned object graph to resolve
* their local dependencies in a way that will be equivalent to the
* original object graph. In other words, if two objects in the graph
* share the same target reference then the cloned version will share
* the cloned reference.
*
* <p>For example, if an object wishes to deep clone one of its fields
* then it will call cloner.clone(object) instead of object.clone().
* The cloner will keep track of any clones already created for 'object'
* and return that instead of a new clone.</p>
*
* <p>Cloning of a JmeCloneable object is done in two parts. First,
* the standard Java clone() method is called to create a shallow clone
* of the object. Second, the cloner wil lcall the cloneFields() method
* to let the object deep clone any of its fields that should be cloned.</p>
*
* <p>This two part process is necessary to facilitate circular references.
* When an object calls cloner.clone() during its cloneFields() method, it
* may get only a shallow copy that will be filled in later.</p>
*
* @author Paul Speed
*/
public interface JmeCloneable extends Cloneable {
/**
* Performs a regular shallow clone of the object. Some fields
* may also be cloned but generally only if they will never be
* shared with other objects. (For example, local Vector3fs and so on.)
*
* <p>This method is separate from the regular clone() method
* so that objects might still maintain their own regular java clone()
* semantics (perhaps even using Cloner for those methods). However,
* because Java's clone() has specific features in the sense of Object's
* clone() implementation, it's usually best to have some path for
* subclasses to bypass the public clone() method that might be cloning
* fields and instead get at the superclass protected clone() methods.
* For example, through super.jmeClone() or another protected clone
* method that some base class eventually calls super.clone() in.</p>
*/
public Object jmeClone();
/**
* Implemented to perform deep cloning for this object, resolving
* local cloned references using the specified cloner. The object
* can call cloner.clone(fieldValue) to deep clone any of its fields.
*
* <p>Note: during normal clone operations the original object
* will not be needed as the clone has already had all of the fields
* shallow copied.</p>
*
* @param cloner The cloner that is performing the cloning operation. The
* cloneFields method can call back into the cloner to make
* clones if its subordinate fields.
* @param original The original object from which this object was cloned.
* This is provided for the very rare case that this object needs
* to refer to its original for some reason. In general, all of
* the relevant values should have been transferred during the
* shallow clone and this object need merely clone what it wants.
*/
public void cloneFields( Cloner cloner, Object original );
}

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2003-2012 jMonkeyEngine * Copyright (c) 2016 jMonkeyEngine
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
@ -29,27 +29,42 @@
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package com.jme3.gde.core.util;
import java.lang.reflect.InvocationTargetException; package com.jme3.util.clone;
import org.apache.commons.beanutils.BeanUtils;
import org.openide.util.Exceptions; import java.util.List;
/** /**
* A CloneFunction implementation that deep clones a list by
* creating a new list and cloning its values using the cloner.
* *
* @author normenhansen * @author Paul Speed
*/ */
public class Beans { public class ListCloneFunction<T extends List> implements CloneFunction<T> {
public static boolean copyProperties(Object dest, Object src) { public T cloneObject( Cloner cloner, T object ) {
try { try {
BeanUtils.copyProperties(dest, src); T clone = cloner.javaClone(object);
return true; return clone;
} catch (IllegalAccessException ex) { } catch( CloneNotSupportedException e ) {
Exceptions.printStackTrace(ex); throw new IllegalArgumentException("Clone not supported for type:" + object.getClass(), e);
} catch (InvocationTargetException ex) { }
Exceptions.printStackTrace(ex); }
/**
* Clones the elements of the list.
*/
@SuppressWarnings("unchecked")
public void cloneFields( Cloner cloner, T clone, T object ) {
for( int i = 0; i < clone.size(); i++ ) {
// Need to clone the clones... because T might
// have done something special in its clone method that
// we will have to adhere to. For example, clone may have nulled
// out some things or whatever that might be implementation specific.
// At any rate, if it's a proper clone then the clone will already
// have shallow versions of the elements that we can clone.
clone.set(i, cloner.clone(clone.get(i)));
} }
return false;
} }
} }

@ -0,0 +1,97 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package com.jme3.util.mikktspace;
/**
*
* @author Nehon
*/
public interface MikkTSpaceContext {
/**
* Returns the number of faces (triangles/quads) on the mesh to be
* processed.
*
* @return
*/
public int getNumFaces();
/**
* Returns the number of vertices on face number iFace iFace is a number in
* the range {0, 1, ..., getNumFaces()-1}
*
* @param face
* @return
*/
public int getNumVerticesOfFace(int face);
/**
* returns the position/normal/texcoord of the referenced face of vertex
* number iVert. iVert is in the range {0,1,2} for triangles and {0,1,2,3}
* for quads.
*
* @param posOut
* @param face
* @param vert
*/
public void getPosition(float posOut[], int face, int vert);
public void getNormal(float normOut[], int face, int vert);
public void getTexCoord(float texOut[], int face, int vert);
/**
* The call-backsetTSpaceBasic() is sufficient for basic normal mapping.
* This function is used to return the tangent and sign to the application.
* tangent is a unit length vector. For normal maps it is sufficient to use
* the following simplified version of the bitangent which is generated at
* pixel/vertex level.
*
* bitangent = fSign * cross(vN, tangent);
*
* Note that the results are returned unindexed. It is possible to generate
* a new index list But averaging/overwriting tangent spaces by using an
* already existing index list WILL produce INCRORRECT results. DO NOT! use
* an already existing index list.
*
* @param tangent
* @param sign
* @param face
* @param vert
*/
public void setTSpaceBasic(float tangent[], float sign, int face, int vert);
/**
* This function is used to return tangent space results to the application.
* tangent and biTangent are unit length vectors and fMagS and fMagT are
* their true magnitudes which can be used for relief mapping effects.
*
* biTangent is the "real" bitangent and thus may not be perpendicular to
* tangent. However, both are perpendicular to the vertex normal. For normal
* maps it is sufficient to use the following simplified version of the
* bitangent which is generated at pixel/vertex level.
*
* <pre>
* fSign = bIsOrientationPreserving ? 1.0f : (-1.0f);
* bitangent = fSign * cross(vN, tangent);
* </pre>
*
* Note that the results are returned unindexed. It is possible to generate
* a new index list. But averaging/overwriting tangent spaces by using an
* already existing index list WILL produce INCRORRECT results. DO NOT! use
* an already existing index list.
*
* @param tangent
* @param biTangent
* @param magS
* @param magT
* @param isOrientationPreserving
* @param face
* @param vert
*/
void setTSpace(float tangent[], float biTangent[], float magS, float magT,
boolean isOrientationPreserving, int face, int vert);
}

@ -0,0 +1,100 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package com.jme3.util.mikktspace;
import com.jme3.scene.Mesh;
import com.jme3.scene.VertexBuffer;
import com.jme3.scene.mesh.IndexBuffer;
import com.jme3.util.BufferUtils;
import java.nio.FloatBuffer;
/**
*
* @author Nehon
*/
public class MikkTSpaceImpl implements MikkTSpaceContext {
Mesh mesh;
public MikkTSpaceImpl(Mesh mesh) {
this.mesh = mesh;
VertexBuffer tangentBuffer = mesh.getBuffer(VertexBuffer.Type.Tangent);
if(tangentBuffer == null){
FloatBuffer fb = BufferUtils.createFloatBuffer(mesh.getVertexCount() * 4);
mesh.setBuffer(VertexBuffer.Type.Tangent, 4, fb);
}
//TODO ensure the Tangent buffer exists, else create one.
}
@Override
public int getNumFaces() {
return mesh.getTriangleCount();
}
@Override
public int getNumVerticesOfFace(int face) {
return 3;
}
@Override
public void getPosition(float[] posOut, int face, int vert) {
int vertIndex = getIndex(face, vert);
VertexBuffer position = mesh.getBuffer(VertexBuffer.Type.Position);
FloatBuffer pos = (FloatBuffer) position.getData();
pos.position(vertIndex * 3);
posOut[0] = pos.get();
posOut[1] = pos.get();
posOut[2] = pos.get();
}
@Override
public void getNormal(float[] normOut, int face, int vert) {
int vertIndex = getIndex(face, vert);
VertexBuffer normal = mesh.getBuffer(VertexBuffer.Type.Normal);
FloatBuffer norm = (FloatBuffer) normal.getData();
norm.position(vertIndex * 3);
normOut[0] = norm.get();
normOut[1] = norm.get();
normOut[2] = norm.get();
}
@Override
public void getTexCoord(float[] texOut, int face, int vert) {
int vertIndex = getIndex(face, vert);
VertexBuffer texCoord = mesh.getBuffer(VertexBuffer.Type.TexCoord);
FloatBuffer tex = (FloatBuffer) texCoord.getData();
tex.position(vertIndex * 2);
texOut[0] = tex.get();
texOut[1] = tex.get();
}
@Override
public void setTSpaceBasic(float[] tangent, float sign, int face, int vert) {
int vertIndex = getIndex(face, vert);
VertexBuffer tangentBuffer = mesh.getBuffer(VertexBuffer.Type.Tangent);
FloatBuffer tan = (FloatBuffer) tangentBuffer.getData();
tan.position(vertIndex * 4);
tan.put(tangent);
tan.put(sign);
tan.rewind();
tangentBuffer.setUpdateNeeded();
}
@Override
public void setTSpace(float[] tangent, float[] biTangent, float magS, float magT, boolean isOrientationPreserving, int face, int vert) {
//Do nothing
}
private int getIndex(int face, int vert) {
IndexBuffer index = mesh.getIndexBuffer();
int vertIndex = index.get(face * 3 + vert);
return vertIndex;
}
}

@ -113,6 +113,8 @@ MaterialDef Phong Lighting {
//For instancing //For instancing
Boolean UseInstancing Boolean UseInstancing
Boolean BackfaceShadows: false
} }
Technique { Technique {
@ -213,26 +215,19 @@ MaterialDef Phong Lighting {
INSTANCING : UseInstancing INSTANCING : UseInstancing
} }
ForcedRenderState {
FaceCull Off
DepthTest On
DepthWrite On
PolyOffset 5 3
ColorWrite Off
}
} }
Technique PostShadow15{ Technique PostShadow15{
VertexShader GLSL150: Common/MatDefs/Shadow/PostShadow15.vert VertexShader GLSL150: Common/MatDefs/Shadow/PostShadow.vert
FragmentShader GLSL150: Common/MatDefs/Shadow/PostShadow15.frag FragmentShader GLSL150: Common/MatDefs/Shadow/PostShadow.frag
WorldParameters { WorldParameters {
WorldViewProjectionMatrix WorldViewProjectionMatrix
WorldMatrix WorldMatrix
ViewProjectionMatrix ViewProjectionMatrix
ViewMatrix ViewMatrix
NormalMatrix
} }
Defines { Defines {
@ -247,6 +242,7 @@ MaterialDef Phong Lighting {
POINTLIGHT : LightViewProjectionMatrix5 POINTLIGHT : LightViewProjectionMatrix5
NUM_BONES : NumberOfBones NUM_BONES : NumberOfBones
INSTANCING : UseInstancing INSTANCING : UseInstancing
BACKFACE_SHADOWS: BackfaceShadows
} }
ForcedRenderState { ForcedRenderState {
@ -265,6 +261,7 @@ MaterialDef Phong Lighting {
WorldMatrix WorldMatrix
ViewProjectionMatrix ViewProjectionMatrix
ViewMatrix ViewMatrix
NormalMatrix
} }
Defines { Defines {

@ -51,6 +51,8 @@ MaterialDef Unshaded {
Float PCFEdge Float PCFEdge
Float ShadowMapSize Float ShadowMapSize
Boolean BackfaceShadows: true
} }
Technique { Technique {
@ -147,8 +149,8 @@ MaterialDef Unshaded {
Technique PostShadow15{ Technique PostShadow15{
VertexShader GLSL150: Common/MatDefs/Shadow/PostShadow15.vert VertexShader GLSL150: Common/MatDefs/Shadow/PostShadow.vert
FragmentShader GLSL150: Common/MatDefs/Shadow/PostShadow15.frag FragmentShader GLSL150: Common/MatDefs/Shadow/PostShadow.frag
WorldParameters { WorldParameters {
WorldViewProjectionMatrix WorldViewProjectionMatrix
@ -169,6 +171,7 @@ MaterialDef Unshaded {
POINTLIGHT : LightViewProjectionMatrix5 POINTLIGHT : LightViewProjectionMatrix5
NUM_BONES : NumberOfBones NUM_BONES : NumberOfBones
INSTANCING : UseInstancing INSTANCING : UseInstancing
BACKFACE_SHADOWS: BackfaceShadows
} }
ForcedRenderState { ForcedRenderState {
@ -201,6 +204,7 @@ MaterialDef Unshaded {
POINTLIGHT : LightViewProjectionMatrix5 POINTLIGHT : LightViewProjectionMatrix5
NUM_BONES : NumberOfBones NUM_BONES : NumberOfBones
INSTANCING : UseInstancing INSTANCING : UseInstancing
BACKFACE_SHADOWS: BackfaceShadows
} }
ForcedRenderState { ForcedRenderState {

@ -1,4 +1,5 @@
#import "Common/ShaderLib/Shadows.glsllib" #import "Common/ShaderLib/Shadows.glsllib"
#import "Common/ShaderLib/GLSLCompat.glsllib"
#if defined(PSSM) || defined(FADE) #if defined(PSSM) || defined(FADE)
varying float shadowPosition; varying float shadowPosition;
@ -8,6 +9,9 @@ varying vec4 projCoord0;
varying vec4 projCoord1; varying vec4 projCoord1;
varying vec4 projCoord2; varying vec4 projCoord2;
varying vec4 projCoord3; varying vec4 projCoord3;
#ifndef BACKFACE_SHADOWS
varying float nDotL;
#endif
#ifdef POINTLIGHT #ifdef POINTLIGHT
varying vec4 projCoord4; varying vec4 projCoord4;
@ -45,9 +49,15 @@ void main(){
if(alpha<=m_AlphaDiscardThreshold){ if(alpha<=m_AlphaDiscardThreshold){
discard; discard;
} }
#endif
#ifndef BACKFACE_SHADOWS
if(nDotL > 0.0){
discard;
}
#endif #endif
float shadow = 1.0; float shadow = 1.0;
#ifdef POINTLIGHT #ifdef POINTLIGHT
@ -72,8 +82,8 @@ void main(){
#ifdef FADE #ifdef FADE
shadow = max(0.0,mix(shadow,1.0,(shadowPosition - m_FadeInfo.x) * m_FadeInfo.y)); shadow = max(0.0,mix(shadow,1.0,(shadowPosition - m_FadeInfo.x) * m_FadeInfo.y));
#endif #endif
shadow = shadow * m_ShadowIntensity + (1.0 - m_ShadowIntensity);
shadow = shadow * m_ShadowIntensity + (1.0 - m_ShadowIntensity);
gl_FragColor = vec4(shadow, shadow, shadow, 1.0); gl_FragColor = vec4(shadow, shadow, shadow, 1.0);
} }

@ -30,11 +30,13 @@ MaterialDef Post Shadow {
Float ShadowMapSize Float ShadowMapSize
Boolean BackfaceShadows: false
} }
Technique { Technique {
VertexShader GLSL150: Common/MatDefs/Shadow/PostShadow15.vert VertexShader GLSL150: Common/MatDefs/Shadow/PostShadow.vert
FragmentShader GLSL150: Common/MatDefs/Shadow/PostShadow15.frag FragmentShader GLSL150: Common/MatDefs/Shadow/PostShadow.frag
WorldParameters { WorldParameters {
WorldViewProjectionMatrix WorldViewProjectionMatrix
@ -49,6 +51,7 @@ MaterialDef Post Shadow {
FADE : FadeInfo FADE : FadeInfo
PSSM : Splits PSSM : Splits
POINTLIGHT : LightViewProjectionMatrix5 POINTLIGHT : LightViewProjectionMatrix5
BACKFACE_SHADOWS: BackfaceShadows
} }
RenderState { RenderState {
@ -75,6 +78,7 @@ MaterialDef Post Shadow {
FADE : FadeInfo FADE : FadeInfo
PSSM : Splits PSSM : Splits
POINTLIGHT : LightViewProjectionMatrix5 POINTLIGHT : LightViewProjectionMatrix5
BACKFACE_SHADOWS: BackfaceShadows
} }
RenderState { RenderState {

@ -1,11 +1,12 @@
#import "Common/ShaderLib/Instancing.glsllib" #import "Common/ShaderLib/Instancing.glsllib"
#import "Common/ShaderLib/Skinning.glsllib" #import "Common/ShaderLib/Skinning.glsllib"
#import "Common/ShaderLib/GLSLCompat.glsllib"
uniform mat4 m_LightViewProjectionMatrix0; uniform mat4 m_LightViewProjectionMatrix0;
uniform mat4 m_LightViewProjectionMatrix1; uniform mat4 m_LightViewProjectionMatrix1;
uniform mat4 m_LightViewProjectionMatrix2; uniform mat4 m_LightViewProjectionMatrix2;
uniform mat4 m_LightViewProjectionMatrix3; uniform mat4 m_LightViewProjectionMatrix3;
uniform vec3 m_LightPos;
varying vec4 projCoord0; varying vec4 projCoord0;
varying vec4 projCoord1; varying vec4 projCoord1;
@ -15,12 +16,14 @@ varying vec4 projCoord3;
#ifdef POINTLIGHT #ifdef POINTLIGHT
uniform mat4 m_LightViewProjectionMatrix4; uniform mat4 m_LightViewProjectionMatrix4;
uniform mat4 m_LightViewProjectionMatrix5; uniform mat4 m_LightViewProjectionMatrix5;
uniform vec3 m_LightPos;
varying vec4 projCoord4; varying vec4 projCoord4;
varying vec4 projCoord5; varying vec4 projCoord5;
varying vec4 worldPos; varying vec4 worldPos;
#else #else
#ifndef PSSM
uniform vec3 m_LightDir; uniform vec3 m_LightDir;
#ifndef PSSM
uniform vec3 m_LightPos;
varying float lightDot; varying float lightDot;
#endif #endif
#endif #endif
@ -28,12 +31,15 @@ varying vec4 projCoord3;
#if defined(PSSM) || defined(FADE) #if defined(PSSM) || defined(FADE)
varying float shadowPosition; varying float shadowPosition;
#endif #endif
varying vec3 lightVec;
varying vec2 texCoord; varying vec2 texCoord;
attribute vec3 inPosition; attribute vec3 inPosition;
#ifndef BACKFACE_SHADOWS
attribute vec3 inNormal;
varying float nDotL;
#endif
#ifdef DISCARD_ALPHA #ifdef DISCARD_ALPHA
attribute vec2 inTexCoord; attribute vec2 inTexCoord;
#endif #endif
@ -51,6 +57,7 @@ void main(){
Skinning_Compute(modelSpacePos); Skinning_Compute(modelSpacePos);
#endif #endif
gl_Position = TransformWorldViewProjection(modelSpacePos); gl_Position = TransformWorldViewProjection(modelSpacePos);
vec3 lightDir;
#if defined(PSSM) || defined(FADE) #if defined(PSSM) || defined(FADE)
shadowPosition = gl_Position.z; shadowPosition = gl_Position.z;
@ -60,7 +67,7 @@ void main(){
vec4 worldPos=vec4(0.0); vec4 worldPos=vec4(0.0);
#endif #endif
// get the vertex in world space // get the vertex in world space
worldPos = g_WorldMatrix * modelSpacePos; worldPos = TransformWorld(modelSpacePos);
#ifdef DISCARD_ALPHA #ifdef DISCARD_ALPHA
texCoord = inTexCoord; texCoord = inTexCoord;
@ -75,8 +82,21 @@ void main(){
projCoord5 = biasMat * m_LightViewProjectionMatrix5 * worldPos; projCoord5 = biasMat * m_LightViewProjectionMatrix5 * worldPos;
#else #else
#ifndef PSSM #ifndef PSSM
vec3 lightDir = worldPos.xyz - m_LightPos; //Spot light
lightDir = worldPos.xyz - m_LightPos;
lightDot = dot(m_LightDir,lightDir); lightDot = dot(m_LightDir,lightDir);
#endif #endif
#endif #endif
#ifndef BACKFACE_SHADOWS
vec3 normal = normalize(TransformWorld(vec4(inNormal,0.0))).xyz;
#ifdef POINTLIGHT
lightDir = worldPos.xyz - m_LightPos;
#else
#ifdef PSSM
lightDir = m_LightDir;
#endif
#endif
nDotL = dot(normal, lightDir);
#endif
} }

@ -1,80 +0,0 @@
#import "Common/ShaderLib/Shadows15.glsllib"
out vec4 outFragColor;
#if defined(PSSM) || defined(FADE)
in float shadowPosition;
#endif
in vec4 projCoord0;
in vec4 projCoord1;
in vec4 projCoord2;
in vec4 projCoord3;
#ifdef POINTLIGHT
in vec4 projCoord4;
in vec4 projCoord5;
in vec4 worldPos;
uniform vec3 m_LightPos;
#else
#ifndef PSSM
in float lightDot;
#endif
#endif
#ifdef DISCARD_ALPHA
#ifdef COLOR_MAP
uniform sampler2D m_ColorMap;
#else
uniform sampler2D m_DiffuseMap;
#endif
uniform float m_AlphaDiscardThreshold;
varying vec2 texCoord;
#endif
#ifdef FADE
uniform vec2 m_FadeInfo;
#endif
void main(){
#ifdef DISCARD_ALPHA
#ifdef COLOR_MAP
float alpha = texture2D(m_ColorMap,texCoord).a;
#else
float alpha = texture2D(m_DiffuseMap,texCoord).a;
#endif
if(alpha < m_AlphaDiscardThreshold){
discard;
}
#endif
float shadow = 1.0;
#ifdef POINTLIGHT
shadow = getPointLightShadows(worldPos, m_LightPos,
m_ShadowMap0,m_ShadowMap1,m_ShadowMap2,m_ShadowMap3,m_ShadowMap4,m_ShadowMap5,
projCoord0, projCoord1, projCoord2, projCoord3, projCoord4, projCoord5);
#else
#ifdef PSSM
shadow = getDirectionalLightShadows(m_Splits, shadowPosition,
m_ShadowMap0,m_ShadowMap1,m_ShadowMap2,m_ShadowMap3,
projCoord0, projCoord1, projCoord2, projCoord3);
#else
//spotlight
if(lightDot < 0){
outFragColor = vec4(1.0);
return;
}
shadow = getSpotLightShadows(m_ShadowMap0,projCoord0);
#endif
#endif
#ifdef FADE
shadow = max(0.0,mix(shadow,1.0,(shadowPosition - m_FadeInfo.x) * m_FadeInfo.y));
#endif
shadow = shadow * m_ShadowIntensity + (1.0 - m_ShadowIntensity);
outFragColor = vec4(shadow, shadow, shadow, 1.0);
}

@ -1,82 +0,0 @@
#import "Common/ShaderLib/Instancing.glsllib"
#import "Common/ShaderLib/Skinning.glsllib"
uniform mat4 m_LightViewProjectionMatrix0;
uniform mat4 m_LightViewProjectionMatrix1;
uniform mat4 m_LightViewProjectionMatrix2;
uniform mat4 m_LightViewProjectionMatrix3;
out vec4 projCoord0;
out vec4 projCoord1;
out vec4 projCoord2;
out vec4 projCoord3;
#ifdef POINTLIGHT
uniform mat4 m_LightViewProjectionMatrix4;
uniform mat4 m_LightViewProjectionMatrix5;
out vec4 projCoord4;
out vec4 projCoord5;
out vec4 worldPos;
#else
#ifndef PSSM
uniform vec3 m_LightPos;
uniform vec3 m_LightDir;
out float lightDot;
#endif
#endif
#if defined(PSSM) || defined(FADE)
out float shadowPosition;
#endif
out vec3 lightVec;
out vec2 texCoord;
in vec3 inPosition;
#ifdef DISCARD_ALPHA
in vec2 inTexCoord;
#endif
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(){
vec4 modelSpacePos = vec4(inPosition, 1.0);
#ifdef NUM_BONES
Skinning_Compute(modelSpacePos);
#endif
gl_Position = TransformWorldViewProjection(modelSpacePos);
#if defined(PSSM) || defined(FADE)
shadowPosition = gl_Position.z;
#endif
#ifndef POINTLIGHT
vec4 worldPos=vec4(0.0);
#endif
// get the vertex in world space
worldPos = TransformWorld(modelSpacePos);
#ifdef DISCARD_ALPHA
texCoord = inTexCoord;
#endif
// 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;
#ifdef POINTLIGHT
projCoord4 = biasMat * m_LightViewProjectionMatrix4 * worldPos;
projCoord5 = biasMat * m_LightViewProjectionMatrix5 * worldPos;
#else
#ifndef PSSM
vec3 lightDir = worldPos.xyz - m_LightPos;
lightDot = dot(m_LightDir,lightDir);
#endif
#endif
}

@ -18,6 +18,8 @@ uniform mat4 m_LightViewProjectionMatrix1;
uniform mat4 m_LightViewProjectionMatrix2; uniform mat4 m_LightViewProjectionMatrix2;
uniform mat4 m_LightViewProjectionMatrix3; uniform mat4 m_LightViewProjectionMatrix3;
uniform vec2 g_ResolutionInverse;
#ifdef POINTLIGHT #ifdef POINTLIGHT
uniform vec3 m_LightPos; uniform vec3 m_LightPos;
uniform mat4 m_LightViewProjectionMatrix4; uniform mat4 m_LightViewProjectionMatrix4;
@ -39,6 +41,19 @@ vec3 getPosition(in float depth, in vec2 uv){
return pos.xyz / pos.w; return pos.xyz / pos.w;
} }
vec3 approximateNormal(in vec4 worldPos,in vec2 texCoord){
float step = g_ResolutionInverse.x ;
float stepy = g_ResolutionInverse.y ;
float depth2 = texture2D(m_DepthTexture,texCoord + vec2(step,-stepy)).r;
float depth3 = texture2D(m_DepthTexture,texCoord + vec2(-step,-stepy)).r;
vec4 worldPos2 = vec4(getPosition(depth2,texCoord + vec2(step,-stepy)),1.0);
vec4 worldPos3 = vec4(getPosition(depth3,texCoord + vec2(-step,-stepy)),1.0);
vec3 v1 = (worldPos - worldPos2).xyz;
vec3 v2 = (worldPos3 - worldPos2).xyz;
return normalize(cross(v1, v2));
}
void main(){ void main(){
#if !defined( RENDER_SHADOWS ) #if !defined( RENDER_SHADOWS )
gl_FragColor = texture2D(m_Texture,texCoord); gl_FragColor = texture2D(m_Texture,texCoord);
@ -48,6 +63,7 @@ void main(){
float depth = texture2D(m_DepthTexture,texCoord).r; float depth = texture2D(m_DepthTexture,texCoord).r;
vec4 color = texture2D(m_Texture,texCoord); vec4 color = texture2D(m_Texture,texCoord);
//Discard shadow computation on the sky //Discard shadow computation on the sky
if(depth == 1.0){ if(depth == 1.0){
gl_FragColor = color; gl_FragColor = color;
@ -56,6 +72,19 @@ void main(){
// get the vertex in world space // get the vertex in world space
vec4 worldPos = vec4(getPosition(depth,texCoord),1.0); vec4 worldPos = vec4(getPosition(depth,texCoord),1.0);
vec3 normal = approximateNormal(worldPos, texCoord);
vec3 lightDir;
#ifdef PSSM
lightDir = m_LightDir;
#else
lightDir = worldPos.xyz - m_LightPos;
#endif
float ndotl = dot(normal, lightDir);
if(ndotl > -0.0){
gl_FragColor = color;
return;
}
#if (!defined(POINTLIGHT) && !defined(PSSM)) #if (!defined(POINTLIGHT) && !defined(PSSM))
vec3 lightDir = worldPos.xyz - m_LightPos; vec3 lightDir = worldPos.xyz - m_LightPos;

@ -38,6 +38,7 @@ MaterialDef Post Shadow {
Texture2D Texture Texture2D Texture
Texture2D DepthTexture Texture2D DepthTexture
Boolean BackfaceShadows: true
} }
Technique { Technique {
@ -45,6 +46,7 @@ MaterialDef Post Shadow {
FragmentShader GLSL150: Common/MatDefs/Shadow/PostShadowFilter15.frag FragmentShader GLSL150: Common/MatDefs/Shadow/PostShadowFilter15.frag
WorldParameters { WorldParameters {
ResolutionInverse
} }
Defines { Defines {
@ -59,7 +61,7 @@ MaterialDef Post Shadow {
POINTLIGHT : LightViewProjectionMatrix5 POINTLIGHT : LightViewProjectionMatrix5
//if no shadow map don't render shadows //if no shadow map don't render shadows
RENDER_SHADOWS : ShadowMap0 RENDER_SHADOWS : ShadowMap0
BACKFACE_SHADOWS : BackfaceShadows
} }
} }
@ -69,6 +71,7 @@ MaterialDef Post Shadow {
FragmentShader GLSL100: Common/MatDefs/Shadow/PostShadowFilter.frag FragmentShader GLSL100: Common/MatDefs/Shadow/PostShadowFilter.frag
WorldParameters { WorldParameters {
ResolutionInverse
} }
Defines { Defines {
@ -79,6 +82,7 @@ MaterialDef Post Shadow {
FADE : FadeInfo FADE : FadeInfo
PSSM : Splits PSSM : Splits
POINTLIGHT : LightViewProjectionMatrix5 POINTLIGHT : LightViewProjectionMatrix5
BACKFACE_SHADOWS : BackfaceShadows
} }
} }

@ -1,5 +1,5 @@
#import "Common/ShaderLib/MultiSample.glsllib" #import "Common/ShaderLib/MultiSample.glsllib"
#import "Common/ShaderLib/Shadows15.glsllib" #import "Common/ShaderLib/Shadows.glsllib"
uniform COLORTEXTURE m_Texture; uniform COLORTEXTURE m_Texture;
@ -20,14 +20,16 @@ uniform mat4 m_LightViewProjectionMatrix1;
uniform mat4 m_LightViewProjectionMatrix2; uniform mat4 m_LightViewProjectionMatrix2;
uniform mat4 m_LightViewProjectionMatrix3; uniform mat4 m_LightViewProjectionMatrix3;
uniform vec2 g_ResolutionInverse;
#ifdef POINTLIGHT #ifdef POINTLIGHT
uniform vec3 m_LightPos; uniform vec3 m_LightPos;
uniform mat4 m_LightViewProjectionMatrix4; uniform mat4 m_LightViewProjectionMatrix4;
uniform mat4 m_LightViewProjectionMatrix5; uniform mat4 m_LightViewProjectionMatrix5;
#else #else
uniform vec3 m_LightDir;
#ifndef PSSM #ifndef PSSM
uniform vec3 m_LightPos; uniform vec3 m_LightPos;
uniform vec3 m_LightDir;
#endif #endif
#endif #endif
@ -41,6 +43,23 @@ vec3 getPosition(in float depth, in vec2 uv){
return pos.xyz / pos.w; return pos.xyz / pos.w;
} }
#ifndef BACKFACE_SHADOWS
vec3 approximateNormal(in float depth,in vec4 worldPos,in vec2 texCoord, in int numSample){
float step = g_ResolutionInverse.x ;
float stepy = g_ResolutionInverse.y ;
float depth1 = fetchTextureSample(m_DepthTexture,texCoord + vec2(-step,stepy),numSample).r;
float depth2 = fetchTextureSample(m_DepthTexture,texCoord + vec2(step,stepy),numSample).r;
vec3 v1, v2;
vec4 worldPos1 = vec4(getPosition(depth1,texCoord + vec2(-step,stepy)),1.0);
vec4 worldPos2 = vec4(getPosition(depth2,texCoord + vec2(step,stepy)),1.0);
v1 = normalize((worldPos1 - worldPos)).xyz;
v2 = normalize((worldPos2 - worldPos)).xyz;
return normalize(cross(v2, v1));
}
#endif
vec4 main_multiSample(in int numSample){ vec4 main_multiSample(in int numSample){
float depth = fetchTextureSample(m_DepthTexture,texCoord,numSample).r;//getDepth(m_DepthTexture,texCoord).r; float depth = fetchTextureSample(m_DepthTexture,texCoord,numSample).r;//getDepth(m_DepthTexture,texCoord).r;
vec4 color = fetchTextureSample(m_Texture,texCoord,numSample); vec4 color = fetchTextureSample(m_Texture,texCoord,numSample);
@ -53,8 +72,23 @@ vec4 main_multiSample(in int numSample){
// get the vertex in world space // get the vertex in world space
vec4 worldPos = vec4(getPosition(depth,texCoord),1.0); vec4 worldPos = vec4(getPosition(depth,texCoord),1.0);
vec3 lightDir;
#ifdef PSSM
lightDir = m_LightDir;
#else
lightDir = worldPos.xyz - m_LightPos;
#endif
#ifndef BACKFACE_SHADOWS
vec3 normal = approximateNormal(depth, worldPos, texCoord, numSample);
float ndotl = dot(normal, lightDir);
if(ndotl > 0.0){
return color;
}
#endif
#if (!defined(POINTLIGHT) && !defined(PSSM)) #if (!defined(POINTLIGHT) && !defined(PSSM))
vec3 lightDir = worldPos.xyz - m_LightPos;
if( dot(m_LightDir,lightDir)<0){ if( dot(m_LightDir,lightDir)<0){
return color; return color;
} }

@ -1,22 +1,57 @@
#ifdef HARDWARE_SHADOWS #if __VERSION__ >= 130
// Because gpu_shader5 is actually where those
// gather functions are declared to work on shadowmaps
#extension GL_ARB_gpu_shader5 : enable
#define IVEC2 ivec2
#ifdef HARDWARE_SHADOWS
#define SHADOWMAP sampler2DShadow #define SHADOWMAP sampler2DShadow
#define SHADOWCOMPARE(tex,coord) shadow2DProj(tex, coord).r #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 Shadow_Nearest
#define KERNEL 1.0
#elif FILTER_MODE == 1
#ifdef HARDWARE_SHADOWS
#define GETSHADOW Shadow_Nearest
#else
#define GETSHADOW Shadow_DoBilinear_2x2
#endif
#define KERNEL 1.0
#endif
#else #else
#define IVEC2 vec2
#ifdef HARDWARE_SHADOWS
#define SHADOWMAP sampler2DShadow
#define SHADOWCOMPARE(tex,coord) shadow2DProj(tex, coord).r
#else
#define SHADOWMAP sampler2D #define SHADOWMAP sampler2D
#define SHADOWCOMPARE(tex,coord) step(coord.z, texture2DProj(tex, coord).r) #define SHADOWCOMPARE(tex,coord) step(coord.z, texture2DProj(tex, coord).r)
#endif #endif
#if FILTER_MODE == 0 #if FILTER_MODE == 0
#define GETSHADOW Shadow_DoShadowCompare #define GETSHADOW Shadow_DoShadowCompare
#define KERNEL 1.0 #define KERNEL 1.0
#elif FILTER_MODE == 1 #elif FILTER_MODE == 1
#ifdef HARDWARE_SHADOWS #ifdef HARDWARE_SHADOWS
#define GETSHADOW Shadow_DoShadowCompare #define GETSHADOW Shadow_DoShadowCompare
#else #else
#define GETSHADOW Shadow_DoBilinear_2x2 #define GETSHADOW Shadow_DoBilinear_2x2
#endif #endif
#define KERNEL 1.0 #define KERNEL 1.0
#elif FILTER_MODE == 2 #endif
#endif
#if FILTER_MODE == 2
#define GETSHADOW Shadow_DoDither_2x2 #define GETSHADOW Shadow_DoDither_2x2
#define KERNEL 1.0 #define KERNEL 1.0
#elif FILTER_MODE == 3 #elif FILTER_MODE == 3
@ -30,14 +65,13 @@
#define KERNEL 8.0 #define KERNEL 8.0
#endif #endif
uniform SHADOWMAP m_ShadowMap0; uniform SHADOWMAP m_ShadowMap0;
uniform SHADOWMAP m_ShadowMap1; uniform SHADOWMAP m_ShadowMap1;
uniform SHADOWMAP m_ShadowMap2; uniform SHADOWMAP m_ShadowMap2;
uniform SHADOWMAP m_ShadowMap3; uniform SHADOWMAP m_ShadowMap3;
#ifdef POINTLIGHT #ifdef POINTLIGHT
uniform SHADOWMAP m_ShadowMap4; uniform SHADOWMAP m_ShadowMap4;
uniform SHADOWMAP m_ShadowMap5; uniform SHADOWMAP m_ShadowMap5;
#endif #endif
#ifdef PSSM #ifdef PSSM
@ -49,73 +83,91 @@ uniform float m_ShadowIntensity;
const vec2 pixSize2 = vec2(1.0 / SHADOWMAP_SIZE); const vec2 pixSize2 = vec2(1.0 / SHADOWMAP_SIZE);
float shadowBorderScale = 1.0; float shadowBorderScale = 1.0;
float Shadow_DoShadowCompareOffset(SHADOWMAP tex, vec4 projCoord, vec2 offset){ float Shadow_DoShadowCompare(in SHADOWMAP tex,in vec4 projCoord){
vec4 coord = vec4(projCoord.xy + offset.xy * pixSize2 * shadowBorderScale, projCoord.zw);
return SHADOWCOMPARE(tex, coord);
}
float Shadow_DoShadowCompare(SHADOWMAP tex, vec4 projCoord){
return SHADOWCOMPARE(tex, projCoord); return SHADOWCOMPARE(tex, projCoord);
} }
float Shadow_BorderCheck(vec2 coord){ float Shadow_BorderCheck(in vec2 coord){
// Fastest, "hack" method (uses 4-5 instructions) // Fastest, "hack" method (uses 4-5 instructions)
vec4 t = vec4(coord.xy, 0.0, 1.0); vec4 t = vec4(coord.xy, 0.0, 1.0);
t = step(t.wwxy, t.xyzz); t = step(t.wwxy, t.xyzz);
return dot(t,t); return dot(t,t);
} }
float Shadow_Nearest(SHADOWMAP tex, vec4 projCoord){ float Shadow_Nearest(in SHADOWMAP tex,in vec4 projCoord){
float border = Shadow_BorderCheck(projCoord.xy); float border = Shadow_BorderCheck(projCoord.xy);
if (border > 0.0){ if (border > 0.0){
return 1.0; return 1.0;
} }
return Shadow_DoShadowCompare(tex,projCoord); return SHADOWCOMPARE(tex, projCoord);
} }
float Shadow_DoDither_2x2(SHADOWMAP tex, vec4 projCoord){ float Shadow_DoShadowCompareOffset(in SHADOWMAP tex,in vec4 projCoord,in vec2 offset){
vec4 coord = vec4(projCoord.xy + offset.xy * pixSize2 * shadowBorderScale, projCoord.zw);
return SHADOWCOMPARE(tex, coord);
}
float Shadow_DoDither_2x2(in SHADOWMAP tex, in vec4 projCoord){
float border = Shadow_BorderCheck(projCoord.xy); float border = Shadow_BorderCheck(projCoord.xy);
if (border > 0.0) if (border > 0.0)
return 1.0; return 1.0;
float shadow = 0.0; float shadow = 0.0;
vec2 o = mod(floor(gl_FragCoord.xy), 2.0); IVEC2 o = IVEC2(mod(floor(gl_FragCoord.xy), 2.0));
shadow += Shadow_DoShadowCompareOffset(tex,projCoord,vec2(-1.5, 1.5) + o); 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( 0.5, 1.5)+o));
shadow += Shadow_DoShadowCompareOffset(tex,projCoord,vec2(-1.5, -0.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 += Shadow_DoShadowCompareOffset(tex, projCoord, (vec2( 0.5, -0.5)+o));
shadow *= 0.25 ; shadow *= 0.25;
return shadow; return shadow;
} }
float Shadow_DoBilinear_2x2(SHADOWMAP tex, vec4 projCoord){ float Shadow_DoBilinear_2x2(in SHADOWMAP tex, in vec4 projCoord){
float border = Shadow_BorderCheck(projCoord.xy); float border = Shadow_BorderCheck(projCoord.xy);
if (border > 0.0) if (border > 0.0){
return 1.0; return 1.0;
}
vec4 gather = vec4(0.0); vec4 gather = vec4(0.0);
#if __VERSION__ >= 130
#ifdef GL_ARB_gpu_shader5
vec4 coord = vec4(projCoord.xyz / projCoord.www,0.0);
gather = SHADOWGATHER(tex, coord);
#else
gather.x = SHADOWCOMPAREOFFSET(tex, projCoord, ivec2(0, 1));
gather.y = SHADOWCOMPAREOFFSET(tex, projCoord, ivec2(1, 1));
gather.z = SHADOWCOMPAREOFFSET(tex, projCoord, ivec2(1, 0));
gather.w = SHADOWCOMPAREOFFSET(tex, projCoord, ivec2(0, 0));
#endif
#else
gather.x = Shadow_DoShadowCompareOffset(tex, projCoord, vec2(0.0, 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.y = Shadow_DoShadowCompareOffset(tex, projCoord, vec2(1.0, 0.0));
gather.z = Shadow_DoShadowCompareOffset(tex, projCoord, vec2(0.0, 1.0)); gather.z = Shadow_DoShadowCompareOffset(tex, projCoord, vec2(0.0, 1.0));
gather.w = Shadow_DoShadowCompareOffset(tex, projCoord, vec2(1.0, 1.0)); gather.w = Shadow_DoShadowCompareOffset(tex, projCoord, vec2(1.0, 1.0));
#endif
vec2 f = fract( projCoord.xy * SHADOWMAP_SIZE ); vec2 f = fract( projCoord.xy * SHADOWMAP_SIZE );
vec2 mx = mix( gather.xz, gather.yw, f.x ); vec2 mx = mix( gather.wx, gather.zy, f.x );
return mix( mx.x, mx.y, f.y ); return mix( mx.x, mx.y, f.y );
} }
float Shadow_DoPCF(SHADOWMAP tex, vec4 projCoord){ float Shadow_DoPCF(in SHADOWMAP tex,in vec4 projCoord){
float shadow = 0.0; float shadow = 0.0;
float border = Shadow_BorderCheck(projCoord.xy); float border = Shadow_BorderCheck(projCoord.xy);
if (border > 0.0) if (border > 0.0)
return 1.0; return 1.0;
float bound = KERNEL * 0.5 - 0.5; float bound = KERNEL * 0.5 - 0.5;
bound *= PCFEDGE; bound *= PCFEDGE;
for (float y = -bound; y <= bound; y += PCFEDGE){ for (float y = -bound; y <= bound; y += PCFEDGE){
for (float x = -bound; x <= bound; x += PCFEDGE){ for (float x = -bound; x <= bound; x += PCFEDGE){
shadow += clamp(Shadow_DoShadowCompareOffset(tex,projCoord,vec2(x,y)) + #if __VERSION__ < 130
border, shadow += clamp(Shadow_DoShadowCompareOffset(tex,projCoord,vec2(x,y)) + border, 0.0, 1.0);
0.0, 1.0); #else
shadow += Shadow_DoShadowCompareOffset(tex, projCoord, vec2(x,y));
#endif
} }
} }
@ -123,51 +175,51 @@ float Shadow_DoPCF(SHADOWMAP tex, vec4 projCoord){
return shadow; return shadow;
} }
//12 tap poisson disk //12 tap poisson disk
const vec2 poissonDisk0 = vec2(-0.1711046, -0.425016); const vec2 poissonDisk0 = vec2(-0.1711046, -0.425016);
const vec2 poissonDisk1 = vec2(-0.7829809, 0.2162201); const vec2 poissonDisk1 = vec2(-0.7829809, 0.2162201);
const vec2 poissonDisk2 = vec2(-0.2380269, -0.8835521); const vec2 poissonDisk2 = vec2(-0.2380269, -0.8835521);
const vec2 poissonDisk3 = vec2(0.4198045, 0.1687819); const vec2 poissonDisk3 = vec2(0.4198045, 0.1687819);
const vec2 poissonDisk4 = vec2(-0.684418, -0.3186957); const vec2 poissonDisk4 = vec2(-0.684418, -0.3186957);
const vec2 poissonDisk5 = vec2(0.6026866, -0.2587841); const vec2 poissonDisk5 = vec2(0.6026866, -0.2587841);
const vec2 poissonDisk6 = vec2(-0.2412762, 0.3913516); const vec2 poissonDisk6 = vec2(-0.2412762, 0.3913516);
const vec2 poissonDisk7 = vec2(0.4720655, -0.7664126); const vec2 poissonDisk7 = vec2(0.4720655, -0.7664126);
const vec2 poissonDisk8 = vec2(0.9571564, 0.2680693); const vec2 poissonDisk8 = vec2(0.9571564, 0.2680693);
const vec2 poissonDisk9 = vec2(-0.5238616, 0.802707); const vec2 poissonDisk9 = vec2(-0.5238616, 0.802707);
const vec2 poissonDisk10 = vec2(0.5653144, 0.60262); const vec2 poissonDisk10 = vec2(0.5653144, 0.60262);
const vec2 poissonDisk11 = vec2(0.0123658, 0.8627419); const vec2 poissonDisk11 = vec2(0.0123658, 0.8627419);
float Shadow_DoPCFPoisson(SHADOWMAP tex, vec4 projCoord){
float Shadow_DoPCFPoisson(in SHADOWMAP tex, in vec4 projCoord){
float shadow = 0.0; float shadow = 0.0;
float border = Shadow_BorderCheck(projCoord.xy); float border = Shadow_BorderCheck(projCoord.xy);
if (border > 0.0) if (border > 0.0){
return 1.0; return 1.0;
}
vec2 texelSize = vec2( 4.0 * PCFEDGE * shadowBorderScale); vec2 texelSize = pixSize2 * 4.0 * PCFEDGE * shadowBorderScale;
shadow += Shadow_DoShadowCompareOffset(tex, projCoord , poissonDisk0 * texelSize);
shadow += Shadow_DoShadowCompareOffset(tex, projCoord , poissonDisk1 * texelSize);
shadow += Shadow_DoShadowCompareOffset(tex, projCoord , poissonDisk2 * texelSize);
shadow += Shadow_DoShadowCompareOffset(tex, projCoord , poissonDisk3 * texelSize);
shadow += Shadow_DoShadowCompareOffset(tex, projCoord , poissonDisk4 * texelSize);
shadow += Shadow_DoShadowCompareOffset(tex, projCoord , poissonDisk5 * texelSize);
shadow += Shadow_DoShadowCompareOffset(tex, projCoord , poissonDisk6 * texelSize);
shadow += Shadow_DoShadowCompareOffset(tex, projCoord , poissonDisk7 * texelSize);
shadow += Shadow_DoShadowCompareOffset(tex, projCoord , poissonDisk8 * texelSize);
shadow += Shadow_DoShadowCompareOffset(tex, projCoord , poissonDisk9 * texelSize);
shadow += Shadow_DoShadowCompareOffset(tex, projCoord , poissonDisk10 * texelSize);
shadow += Shadow_DoShadowCompareOffset(tex, projCoord , poissonDisk11 * texelSize);
shadow = shadow * 0.08333333333;//this is divided by 12
return shadow;
}
shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy + poissonDisk0 * texelSize, projCoord.zw));
shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy + poissonDisk1 * texelSize, projCoord.zw));
shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy + poissonDisk2 * texelSize, projCoord.zw));
shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy + poissonDisk3 * texelSize, projCoord.zw));
shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy + poissonDisk4 * texelSize, projCoord.zw));
shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy + poissonDisk5 * texelSize, projCoord.zw));
shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy + poissonDisk6 * texelSize, projCoord.zw));
shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy + poissonDisk7 * texelSize, projCoord.zw));
shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy + poissonDisk8 * texelSize, projCoord.zw));
shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy + poissonDisk9 * texelSize, projCoord.zw));
shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy + poissonDisk10 * texelSize, projCoord.zw));
shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy + poissonDisk11 * texelSize, projCoord.zw));
//this is divided by 12
return shadow * 0.08333333333;
}
#ifdef POINTLIGHT #ifdef POINTLIGHT
float getPointLightShadows(vec4 worldPos,vec3 lightPos, float getPointLightShadows(in vec4 worldPos,in vec3 lightPos,
SHADOWMAP shadowMap0,SHADOWMAP shadowMap1,SHADOWMAP shadowMap2,SHADOWMAP shadowMap3,SHADOWMAP shadowMap4,SHADOWMAP shadowMap5, in SHADOWMAP shadowMap0,in SHADOWMAP shadowMap1,in SHADOWMAP shadowMap2,in SHADOWMAP shadowMap3,in SHADOWMAP shadowMap4,in SHADOWMAP shadowMap5,
vec4 projCoord0,vec4 projCoord1,vec4 projCoord2,vec4 projCoord3,vec4 projCoord4,vec4 projCoord5){ in vec4 projCoord0,in vec4 projCoord1,in vec4 projCoord2,in vec4 projCoord3,in vec4 projCoord4,in vec4 projCoord5){
float shadow = 1.0; float shadow = 1.0;
vec3 vect = worldPos.xyz - lightPos; vec3 vect = worldPos.xyz - lightPos;
vec3 absv= abs(vect); vec3 absv= abs(vect);
@ -195,9 +247,9 @@ float Shadow_DoPCFPoisson(SHADOWMAP tex, vec4 projCoord){
} }
#else #else
#ifdef PSSM #ifdef PSSM
float getDirectionalLightShadows(vec4 splits,float shadowPosition, float getDirectionalLightShadows(in vec4 splits,in float shadowPosition,
SHADOWMAP shadowMap0,SHADOWMAP shadowMap1,SHADOWMAP shadowMap2,SHADOWMAP shadowMap3, in SHADOWMAP shadowMap0,in SHADOWMAP shadowMap1,in SHADOWMAP shadowMap2,in SHADOWMAP shadowMap3,
vec4 projCoord0,vec4 projCoord1,vec4 projCoord2,vec4 projCoord3){ in vec4 projCoord0,in vec4 projCoord1,in vec4 projCoord2,in vec4 projCoord3){
float shadow = 1.0; float shadow = 1.0;
if(shadowPosition < splits.x){ if(shadowPosition < splits.x){
shadow = GETSHADOW(shadowMap0, projCoord0 ); shadow = GETSHADOW(shadowMap0, projCoord0 );
@ -214,10 +266,10 @@ float Shadow_DoPCFPoisson(SHADOWMAP tex, vec4 projCoord){
return shadow; return shadow;
} }
#else #else
float getSpotLightShadows(SHADOWMAP shadowMap, vec4 projCoord){ float getSpotLightShadows(in SHADOWMAP shadowMap,in vec4 projCoord){
float shadow = 1.0; float shadow = 1.0;
projCoord /= projCoord.w; projCoord /= projCoord.w;
shadow = GETSHADOW(shadowMap, projCoord); shadow = GETSHADOW(shadowMap,projCoord);
//a small falloff to make the shadow blend nicely into the not lighten //a small falloff to make the shadow blend nicely into the not lighten
//we translate the texture coordinate value to a -1,1 range so the length //we translate the texture coordinate value to a -1,1 range so the length
@ -225,7 +277,6 @@ float Shadow_DoPCFPoisson(SHADOWMAP tex, vec4 projCoord){
projCoord = projCoord * 2.0 - 1.0; projCoord = projCoord * 2.0 - 1.0;
float fallOff = ( length(projCoord.xy) - 0.9 ) / 0.1; float fallOff = ( length(projCoord.xy) - 0.9 ) / 0.1;
return mix(shadow,1.0,clamp(fallOff,0.0,1.0)); return mix(shadow,1.0,clamp(fallOff,0.0,1.0));
} }
#endif #endif
#endif #endif

@ -1,242 +0,0 @@
// 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 Shadow_Nearest
#define KERNEL 1.0
#elif FILTER_MODE == 1
#ifdef HARDWARE_SHADOWS
#define GETSHADOW Shadow_Nearest
#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_DoPCFPoisson
#define KERNEL 4.0
#elif FILTER_MODE == 5
#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;
#ifdef POINTLIGHT
uniform SHADOWMAP m_ShadowMap4;
uniform SHADOWMAP m_ShadowMap5;
#endif
#ifdef PSSM
uniform vec4 m_Splits;
#endif
uniform float m_ShadowIntensity;
const vec2 pixSize2 = vec2(1.0 / SHADOWMAP_SIZE);
float shadowBorderScale = 1.0;
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_Nearest(in SHADOWMAP tex, in vec4 projCoord){
float border = Shadow_BorderCheck(projCoord.xy);
if (border > 0.0){
return 1.0;
}
return SHADOWCOMPARE(tex,projCoord);
}
float Shadow_DoDither_2x2(in SHADOWMAP tex, in vec4 projCoord){
float border = Shadow_BorderCheck(projCoord.xy);
if (border > 0.0)
return 1.0;
vec2 pixSize = pixSize2 * shadowBorderScale;
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;
#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, 1));
gather.y = SHADOWCOMPAREOFFSET(tex, projCoord, ivec2(1, 1));
gather.z = SHADOWCOMPAREOFFSET(tex, projCoord, ivec2(1, 0));
gather.w = SHADOWCOMPAREOFFSET(tex, projCoord, ivec2(0, 0));
#endif
vec2 f = fract( projCoord.xy * SHADOWMAP_SIZE );
vec2 mx = mix( gather.wx, gather.zy, f.x );
return mix( mx.x, mx.y, f.y );
}
float Shadow_DoPCF(in SHADOWMAP tex, in vec4 projCoord){
vec2 pixSize = pixSize2 * shadowBorderScale;
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;
}
//12 tap poisson disk
const vec2 poissonDisk0 = vec2(-0.1711046, -0.425016);
const vec2 poissonDisk1 = vec2(-0.7829809, 0.2162201);
const vec2 poissonDisk2 = vec2(-0.2380269, -0.8835521);
const vec2 poissonDisk3 = vec2(0.4198045, 0.1687819);
const vec2 poissonDisk4 = vec2(-0.684418, -0.3186957);
const vec2 poissonDisk5 = vec2(0.6026866, -0.2587841);
const vec2 poissonDisk6 = vec2(-0.2412762, 0.3913516);
const vec2 poissonDisk7 = vec2(0.4720655, -0.7664126);
const vec2 poissonDisk8 = vec2(0.9571564, 0.2680693);
const vec2 poissonDisk9 = vec2(-0.5238616, 0.802707);
const vec2 poissonDisk10 = vec2(0.5653144, 0.60262);
const vec2 poissonDisk11 = vec2(0.0123658, 0.8627419);
float Shadow_DoPCFPoisson(in SHADOWMAP tex, in vec4 projCoord){
float shadow = 0.0;
float border = Shadow_BorderCheck(projCoord.xy);
if (border > 0.0){
return 1.0;
}
vec2 texelSize = pixSize2 * 4.0 * PCFEDGE * shadowBorderScale;
shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy + poissonDisk0 * texelSize, projCoord.zw));
shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy + poissonDisk1 * texelSize, projCoord.zw));
shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy + poissonDisk2 * texelSize, projCoord.zw));
shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy + poissonDisk3 * texelSize, projCoord.zw));
shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy + poissonDisk4 * texelSize, projCoord.zw));
shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy + poissonDisk5 * texelSize, projCoord.zw));
shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy + poissonDisk6 * texelSize, projCoord.zw));
shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy + poissonDisk7 * texelSize, projCoord.zw));
shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy + poissonDisk8 * texelSize, projCoord.zw));
shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy + poissonDisk9 * texelSize, projCoord.zw));
shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy + poissonDisk10 * texelSize, projCoord.zw));
shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy + poissonDisk11 * texelSize, projCoord.zw));
//this is divided by 12
return shadow * 0.08333333333;
}
#ifdef POINTLIGHT
float getPointLightShadows(in vec4 worldPos,in vec3 lightPos,
in SHADOWMAP shadowMap0,in SHADOWMAP shadowMap1,in SHADOWMAP shadowMap2,in SHADOWMAP shadowMap3,in SHADOWMAP shadowMap4,in SHADOWMAP shadowMap5,
in vec4 projCoord0,in vec4 projCoord1,in vec4 projCoord2,in vec4 projCoord3,in vec4 projCoord4,in vec4 projCoord5){
float shadow = 1.0;
vec3 vect = worldPos.xyz - lightPos;
vec3 absv= abs(vect);
float maxComp = max(absv.x,max(absv.y,absv.z));
if(maxComp == absv.y){
if(vect.y < 0.0){
shadow = GETSHADOW(shadowMap0, projCoord0 / projCoord0.w);
}else{
shadow = GETSHADOW(shadowMap1, projCoord1 / projCoord1.w);
}
}else if(maxComp == absv.z){
if(vect.z < 0.0){
shadow = GETSHADOW(shadowMap2, projCoord2 / projCoord2.w);
}else{
shadow = GETSHADOW(shadowMap3, projCoord3 / projCoord3.w);
}
}else if(maxComp == absv.x){
if(vect.x < 0.0){
shadow = GETSHADOW(shadowMap4, projCoord4 / projCoord4.w);
}else{
shadow = GETSHADOW(shadowMap5, projCoord5 / projCoord5.w);
}
}
return shadow;
}
#else
#ifdef PSSM
float getDirectionalLightShadows(in vec4 splits,in float shadowPosition,
in SHADOWMAP shadowMap0,in SHADOWMAP shadowMap1,in SHADOWMAP shadowMap2,in SHADOWMAP shadowMap3,
in vec4 projCoord0,in vec4 projCoord1,in vec4 projCoord2,in vec4 projCoord3){
float shadow = 1.0;
if(shadowPosition < splits.x){
shadow = GETSHADOW(shadowMap0, projCoord0 );
}else if( shadowPosition < splits.y){
shadowBorderScale = 0.5;
shadow = GETSHADOW(shadowMap1, projCoord1);
}else if( shadowPosition < splits.z){
shadowBorderScale = 0.25;
shadow = GETSHADOW(shadowMap2, projCoord2);
}else if( shadowPosition < splits.w){
shadowBorderScale = 0.125;
shadow = GETSHADOW(shadowMap3, projCoord3);
}
return shadow;
}
#else
float getSpotLightShadows(in SHADOWMAP shadowMap,in vec4 projCoord){
float shadow = 1.0;
projCoord /= projCoord.w;
shadow = GETSHADOW(shadowMap,projCoord);
//a small falloff to make the shadow blend nicely into the not lighten
//we translate the texture coordinate value to a -1,1 range so the length
//of the texture coordinate vector is actually the radius of the lighten area on the ground
projCoord = projCoord * 2.0 - 1.0;
float fallOff = ( length(projCoord.xy) - 0.9 ) / 0.1;
return mix(shadow,1.0,clamp(fallOff,0.0,1.0));
}
#endif
#endif

@ -169,9 +169,23 @@ public class J3MLoader implements AssetLoader {
return matchList; return matchList;
} }
private boolean isTexturePathDeclaredTheTraditionalWay(final int numberOfValues, final int numberOfTextureOptions, final String texturePath) { private boolean isTexturePathDeclaredTheTraditionalWay(final List<TextureOptionValue> optionValues, final String texturePath) {
return (numberOfValues > 1 && (texturePath.startsWith("Flip Repeat ") || texturePath.startsWith("Flip ") || final boolean startsWithOldStyle = texturePath.startsWith("Flip Repeat ") || texturePath.startsWith("Flip ") ||
texturePath.startsWith("Repeat ") || texturePath.startsWith("Repeat Flip "))) || numberOfTextureOptions == 0; texturePath.startsWith("Repeat ") || texturePath.startsWith("Repeat Flip ");
if (!startsWithOldStyle) {
return false;
}
if (optionValues.size() == 1 && (optionValues.get(0).textureOption == TextureOption.Flip || optionValues.get(0).textureOption == TextureOption.Repeat)) {
return true;
} else if (optionValues.size() == 2 && optionValues.get(0).textureOption == TextureOption.Flip && optionValues.get(1).textureOption == TextureOption.Repeat) {
return true;
} else if (optionValues.size() == 2 && optionValues.get(0).textureOption == TextureOption.Repeat && optionValues.get(1).textureOption == TextureOption.Flip) {
return true;
}
return false;
} }
private Texture parseTextureType(final VarType type, final String value) { private Texture parseTextureType(final VarType type, final String value) {
@ -187,7 +201,7 @@ public class J3MLoader implements AssetLoader {
String texturePath = value.trim(); String texturePath = value.trim();
// If there are no valid "new" texture options specified but the path is split into several parts, lets parse the old way. // If there are no valid "new" texture options specified but the path is split into several parts, lets parse the old way.
if (isTexturePathDeclaredTheTraditionalWay(textureValues.size(), textureOptionValues.size(), texturePath)) { if (isTexturePathDeclaredTheTraditionalWay(textureOptionValues, texturePath)) {
boolean flipY = false; boolean flipY = false;
if (texturePath.startsWith("Flip Repeat ") || texturePath.startsWith("Repeat Flip ")) { if (texturePath.startsWith("Flip Repeat ") || texturePath.startsWith("Repeat Flip ")) {
@ -623,6 +637,7 @@ public class J3MLoader implements AssetLoader {
material = new Material(def); material = new Material(def);
material.setKey(key); material.setKey(key);
material.setName(split[0].trim());
// material.setAssetName(fileName); // material.setAssetName(fileName);
}else if (split.length == 1){ }else if (split.length == 1){
if (extending){ if (extending){

@ -80,6 +80,7 @@ public class J3MLoaderTest {
final Texture textureMin = Mockito.mock(Texture.class); final Texture textureMin = Mockito.mock(Texture.class);
final Texture textureMag = Mockito.mock(Texture.class); final Texture textureMag = Mockito.mock(Texture.class);
final Texture textureCombined = Mockito.mock(Texture.class); final Texture textureCombined = Mockito.mock(Texture.class);
final Texture textureLooksLikeOldStyle = Mockito.mock(Texture.class);
final TextureKey textureKeyNoParameters = setupMockForTexture("Empty", "empty.png", false, textureNoParameters); final TextureKey textureKeyNoParameters = setupMockForTexture("Empty", "empty.png", false, textureNoParameters);
final TextureKey textureKeyFlip = setupMockForTexture("Flip", "flip.png", true, textureFlip); final TextureKey textureKeyFlip = setupMockForTexture("Flip", "flip.png", true, textureFlip);
@ -88,6 +89,7 @@ public class J3MLoaderTest {
setupMockForTexture("Min", "min.png", false, textureMin); setupMockForTexture("Min", "min.png", false, textureMin);
setupMockForTexture("Mag", "mag.png", false, textureMag); setupMockForTexture("Mag", "mag.png", false, textureMag);
setupMockForTexture("Combined", "combined.png", true, textureCombined); setupMockForTexture("Combined", "combined.png", true, textureCombined);
setupMockForTexture("LooksLikeOldStyle", "oldstyle.png", true, textureLooksLikeOldStyle);
j3MLoader.load(assetInfo); j3MLoader.load(assetInfo);

@ -6,6 +6,7 @@ Material Test : matdef.j3md {
Min: MinTrilinear "min.png" Min: MinTrilinear "min.png"
Mag: MagBilinear "mag.png" Mag: MagBilinear "mag.png"
RepeatAxis: WrapRepeat_T "repeat-axis.png" RepeatAxis: WrapRepeat_T "repeat-axis.png"
Combined: MagNearest MinBilinearNoMipMaps Flip WrapRepeat "combined.png" Combined: Flip MagNearest MinBilinearNoMipMaps WrapRepeat "combined.png"
LooksLikeOldStyle: Flip WrapRepeat "oldstyle.png"
} }
} }

@ -19,6 +19,6 @@ void main(void)
#ifdef NUM_BONES #ifdef NUM_BONES
Skinning_Compute(modelSpacePos,modelSpaceNormals); Skinning_Compute(modelSpacePos,modelSpaceNormals);
#endif #endif
normal = normalize(g_NormalMatrix * modelSpaceNormals); normal = normalize(TransformNormal(modelSpaceNormals));
gl_Position = g_WorldViewProjectionMatrix * modelSpacePos; gl_Position = TransformWorldViewProjection(modelSpacePos);
} }

@ -7,6 +7,10 @@ if (!hasProperty('mainClass')) {
task run(dependsOn: 'build', type:JavaExec) { task run(dependsOn: 'build', type:JavaExec) {
main = mainClass main = mainClass
classpath = sourceSets.main.runtimeClasspath classpath = sourceSets.main.runtimeClasspath
if (System.properties['os.name'].toLowerCase().contains('mac')) {
jvmArgs "-XstartOnFirstThread"
jvmArgs "-Djava.awt.headless=true"
}
if( assertions == "true" ){ if( assertions == "true" ){
enableAssertions = true; enableAssertions = true;
} }

@ -260,8 +260,8 @@ public class TestChooser extends JDialog {
for (int i = 0; i < appClass.length; i++) { for (int i = 0; i < appClass.length; i++) {
Class<?> clazz = (Class)appClass[i]; Class<?> clazz = (Class)appClass[i];
try { try {
if (Application.class.isAssignableFrom(clazz)) {
Object app = clazz.newInstance(); Object app = clazz.newInstance();
if (app instanceof Application) {
if (app instanceof SimpleApplication) { if (app instanceof SimpleApplication) {
final Method settingMethod = clazz.getMethod("setShowSettings", boolean.class); final Method settingMethod = clazz.getMethod("setShowSettings", boolean.class);
settingMethod.invoke(app, showSetting); settingMethod.invoke(app, showSetting);
@ -283,7 +283,7 @@ public class TestChooser extends JDialog {
} }
} else { } else {
final Method mainMethod = clazz.getMethod("main", (new String[0]).getClass()); final Method mainMethod = clazz.getMethod("main", (new String[0]).getClass());
mainMethod.invoke(app, new Object[]{new String[0]}); mainMethod.invoke(clazz, new Object[]{new String[0]});
} }
// wait for destroy // wait for destroy
System.gc(); System.gc();

@ -0,0 +1,367 @@
/*
* Copyright (c) 2016 jMonkeyEngine
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* 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 java.util.*;
import com.jme3.util.clone.*;
/**
*
*
* @author Paul Speed
*/
public class TestCloner {
public static void main( String... args ) {
System.out.println("Clone test:");
Cloner cloner = new Cloner();
RegularObject ro = new RegularObject(42);
System.out.println("Regular Object:" + ro);
RegularObject roCloneLegacy = ro.clone();
System.out.println("Regular Object Clone:" + roCloneLegacy);
RegularObject roClone = cloner.clone(ro);
System.out.println("cloner: Regular Object Clone:" + roClone);
System.out.println("------------------------------------");
System.out.println();
cloner = new Cloner();
RegularSubclass rsc = new RegularSubclass(69, "test");
System.out.println("Regular subclass:" + rsc);
RegularSubclass rscCloneLegacy = (RegularSubclass)rsc.clone();
System.out.println("Regular subclass Clone:" + rscCloneLegacy);
RegularSubclass rscClone = cloner.clone(rsc);
System.out.println("cloner: Regular subclass Clone:" + rscClone);
System.out.println("------------------------------------");
System.out.println();
cloner = new Cloner();
Parent parent = new Parent("Foo", 34);
System.out.println("Parent:" + parent);
Parent parentCloneLegacy = parent.clone();
System.out.println("Parent Clone:" + parentCloneLegacy);
Parent parentClone = cloner.clone(parent);
System.out.println("cloner: Parent Clone:" + parentClone);
System.out.println("------------------------------------");
System.out.println();
cloner = new Cloner();
GraphNode root = new GraphNode("root");
GraphNode child1 = root.addLink("child1");
GraphNode child2 = root.addLink("child2");
GraphNode shared = child1.addLink("shared");
child2.addLink(shared);
// Add a circular reference to get fancy
shared.addLink(root);
System.out.println("Simple graph:");
root.dump(" ");
GraphNode rootClone = cloner.clone(root);
System.out.println("clone:");
rootClone.dump(" ");
System.out.println("original:");
root.dump(" ");
GraphNode reclone = Cloner.deepClone(root);
System.out.println("reclone:");
reclone.dump(" ");
System.out.println("------------------------------------");
System.out.println();
cloner = new Cloner();
ArrayHolder arrays = new ArrayHolder(5, 3, 7, 3, 7, 2, 1, 4);
System.out.println("Array holder:" + arrays);
ArrayHolder arraysClone = cloner.clone(arrays);
System.out.println("Array holder clone:" + arraysClone);
}
public static class RegularObject implements Cloneable {
protected int i;
public RegularObject( int i ) {
this.i = i;
}
public RegularObject clone() {
try {
return (RegularObject)super.clone();
} catch( CloneNotSupportedException e ) {
throw new RuntimeException(e);
}
}
public String toString() {
return getClass().getSimpleName() + "@" + System.identityHashCode(this)
+ "[i=" + i + "]";
}
}
public static class RegularSubclass extends RegularObject {
protected String name;
public RegularSubclass( int i, String name ) {
super(i);
this.name = name;
}
public String toString() {
return getClass().getSimpleName() + "@" + System.identityHashCode(this)
+ "[i=" + i + ", name=" + name + "]";
}
}
public static class Parent implements Cloneable, JmeCloneable {
private RegularObject ro;
private RegularSubclass rsc;
public Parent( String name, int age ) {
this.ro = new RegularObject(age);
this.rsc = new RegularSubclass(age, name);
}
public Parent clone() {
try {
return (Parent)super.clone();
} catch( CloneNotSupportedException e ) {
throw new RuntimeException(e);
}
}
public Parent jmeClone() {
// Ok to delegate to clone() in this case because no deep
// cloning is done there.
return clone();
}
public void cloneFields( Cloner cloner, Object original ) {
this.ro = cloner.clone(ro);
this.rsc = cloner.clone(rsc);
}
public String toString() {
return getClass().getSimpleName() + "@" + System.identityHashCode(this)
+ "[ro=" + ro + ", rsc=" + rsc + "]";
}
}
public static class GraphNode implements Cloneable, JmeCloneable {
private String name;
private List<GraphNode> links = new ArrayList<>();
public GraphNode( String name ) {
this.name = name;
}
public void dump( String indent ) {
dump(indent, new HashSet<GraphNode>());
}
private void dump( String indent, Set<GraphNode> visited ) {
if( visited.contains(this) ) {
// already been here
System.out.println(indent + this + " ** circular.");
return;
}
System.out.println(indent + this);
visited.add(this);
for( GraphNode n : links ) {
n.dump(indent + " ", visited);
}
visited.remove(this);
}
public GraphNode addLink( String name ) {
GraphNode node = new GraphNode(name);
links.add(node);
return node;
}
public GraphNode addLink( GraphNode node ) {
links.add(node);
return node;
}
public List<GraphNode> getLinks() {
return links;
}
public GraphNode jmeClone() {
try {
return (GraphNode)super.clone();
} catch( CloneNotSupportedException e ) {
throw new RuntimeException(e);
}
}
public void cloneFields( Cloner cloner, Object original ) {
this.links = cloner.clone(links);
}
public String toString() {
return getClass().getSimpleName() + "@" + System.identityHashCode(this)
+ "[name=" + name + "]";
}
}
public static class ArrayHolder implements JmeCloneable {
private int[] intArray;
private int[][] intArray2D;
private Object[] objects;
private RegularObject[] regularObjects;
private String[] strings;
public ArrayHolder( int... values ) {
this.intArray = values;
this.intArray2D = new int[values.length][2];
for( int i = 0; i < values.length; i++ ) {
intArray2D[i][0] = values[i] + 1;
intArray2D[i][1] = values[i] * 2;
}
this.objects = new Object[values.length];
this.regularObjects = new RegularObject[values.length];
this.strings = new String[values.length];
for( int i = 0; i < values.length; i++ ) {
objects[i] = values[i];
regularObjects[i] = new RegularObject(values[i]);
strings[i] = String.valueOf(values[i]);
}
}
public ArrayHolder jmeClone() {
try {
return (ArrayHolder)super.clone();
} catch( CloneNotSupportedException e ) {
throw new RuntimeException(e);
}
}
public void cloneFields( Cloner cloner, Object original ) {
intArray = cloner.clone(intArray);
intArray2D = cloner.clone(intArray2D);
// Boxed types are not cloneable so this will fail
//objects = cloner.clone(objects);
regularObjects = cloner.clone(regularObjects);
// Strings are also not cloneable
//strings = cloner.clone(strings);
}
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("intArray=" + intArray);
for( int i = 0; i < intArray.length; i++ ) {
if( i == 0 ) {
sb.append("[");
} else {
sb.append(", ");
}
sb.append(intArray[i]);
}
sb.append("], ");
sb.append("intArray2D=" + intArray2D);
for( int i = 0; i < intArray2D.length; i++ ) {
if( i == 0 ) {
sb.append("[");
} else {
sb.append(", ");
}
sb.append("intArray2D[" + i + "]=" + intArray2D[i]);
for( int j = 0; j < 2; j++ ) {
if( j == 0 ) {
sb.append("[");
} else {
sb.append(", ");
}
sb.append(intArray2D[i][j]);
}
sb.append("], ");
}
sb.append("], ");
sb.append("objectArray=" + objects);
for( int i = 0; i < objects.length; i++ ) {
if( i == 0 ) {
sb.append("[");
} else {
sb.append(", ");
}
sb.append(objects[i]);
}
sb.append("], ");
sb.append("objectArray=" + regularObjects);
for( int i = 0; i < regularObjects.length; i++ ) {
if( i == 0 ) {
sb.append("[");
} else {
sb.append(", ");
}
sb.append(regularObjects[i]);
}
sb.append("], ");
sb.append("stringArray=" + strings);
for( int i = 0; i < strings.length; i++ ) {
if( i == 0 ) {
sb.append("[");
} else {
sb.append(", ");
}
sb.append(strings[i]);
}
sb.append("]");
return getClass().getSimpleName() + "@" + System.identityHashCode(this)
+ "[" + sb + "]";
}
}
}

@ -46,13 +46,15 @@ import com.jme3.renderer.RenderManager;
import com.jme3.renderer.ViewPort; import com.jme3.renderer.ViewPort;
import com.jme3.scene.Spatial; import com.jme3.scene.Spatial;
import com.jme3.scene.control.Control; import com.jme3.scene.control.Control;
import com.jme3.util.clone.Cloner;
import com.jme3.util.clone.JmeCloneable;
import java.io.IOException; import java.io.IOException;
/** /**
* PhysicsHoverControl uses a RayCast Vehicle with "slippery wheels" to simulate a hovering tank * PhysicsHoverControl uses a RayCast Vehicle with "slippery wheels" to simulate a hovering tank
* @author normenhansen * @author normenhansen
*/ */
public class PhysicsHoverControl extends PhysicsVehicle implements PhysicsControl, PhysicsTickListener { public class PhysicsHoverControl extends PhysicsVehicle implements PhysicsControl, PhysicsTickListener, JmeCloneable {
protected Spatial spatial; protected Spatial spatial;
protected boolean enabled = true; protected boolean enabled = true;
@ -94,10 +96,21 @@ public class PhysicsHoverControl extends PhysicsVehicle implements PhysicsContro
createWheels(); createWheels();
} }
@Override
public Control cloneForSpatial(Spatial spatial) { public Control cloneForSpatial(Spatial spatial) {
throw new UnsupportedOperationException("Not supported yet."); throw new UnsupportedOperationException("Not supported yet.");
} }
@Override
public Object jmeClone() {
throw new UnsupportedOperationException("Not yet implemented.");
}
@Override
public void cloneFields( Cloner cloner, Object original ) {
throw new UnsupportedOperationException("Not yet implemented.");
}
public void setSpatial(Spatial spatial) { public void setSpatial(Spatial spatial) {
this.spatial = spatial; this.spatial = spatial;
setUserObject(spatial); setUserObject(spatial);

@ -40,12 +40,14 @@ import com.jme3.input.controls.KeyTrigger;
import com.jme3.light.AmbientLight; import com.jme3.light.AmbientLight;
import com.jme3.light.DirectionalLight; import com.jme3.light.DirectionalLight;
import com.jme3.material.Material; import com.jme3.material.Material;
import com.jme3.material.RenderState;
import com.jme3.math.ColorRGBA; import com.jme3.math.ColorRGBA;
import com.jme3.math.FastMath; import com.jme3.math.FastMath;
import com.jme3.math.Quaternion; import com.jme3.math.Quaternion;
import com.jme3.math.Vector2f; import com.jme3.math.Vector2f;
import com.jme3.math.Vector3f; import com.jme3.math.Vector3f;
import com.jme3.post.FilterPostProcessor; import com.jme3.post.FilterPostProcessor;
import com.jme3.post.ssao.SSAOFilter;
import com.jme3.renderer.queue.RenderQueue.ShadowMode; import com.jme3.renderer.queue.RenderQueue.ShadowMode;
import com.jme3.scene.Geometry; import com.jme3.scene.Geometry;
import com.jme3.scene.Spatial; import com.jme3.scene.Spatial;
@ -69,6 +71,7 @@ public class TestDirectionalLightShadow extends SimpleApplication implements Act
private Geometry ground; private Geometry ground;
private Material matGroundU; private Material matGroundU;
private Material matGroundL; private Material matGroundL;
private AmbientLight al;
public static void main(String[] args) { public static void main(String[] args) {
TestDirectionalLightShadow app = new TestDirectionalLightShadow(); TestDirectionalLightShadow app = new TestDirectionalLightShadow();
@ -99,7 +102,7 @@ public class TestDirectionalLightShadow extends SimpleApplication implements Act
mat[0] = assetManager.loadMaterial("Common/Materials/RedColor.j3m"); mat[0] = assetManager.loadMaterial("Common/Materials/RedColor.j3m");
mat[1] = assetManager.loadMaterial("Textures/Terrain/Pond/Pond.j3m"); mat[1] = assetManager.loadMaterial("Textures/Terrain/Pond/Pond.j3m");
mat[1].setBoolean("UseMaterialColors", true); mat[1].setBoolean("UseMaterialColors", true);
mat[1].setColor("Ambient", ColorRGBA.White.mult(0.5f)); mat[1].setColor("Ambient", ColorRGBA.White);
mat[1].setColor("Diffuse", ColorRGBA.White.clone()); mat[1].setColor("Diffuse", ColorRGBA.White.clone());
@ -110,9 +113,14 @@ public class TestDirectionalLightShadow extends SimpleApplication implements Act
TangentBinormalGenerator.generate(obj[1]); TangentBinormalGenerator.generate(obj[1]);
TangentBinormalGenerator.generate(obj[0]); TangentBinormalGenerator.generate(obj[0]);
Spatial t = obj[0].clone(false);
t.setLocalScale(10f);
t.setMaterial(mat[1]);
rootNode.attachChild(t);
t.setLocalTranslation(0, 25, 0);
for (int i = 0; i < 60; i++) { for (int i = 0; i < 60; i++) {
Spatial t = obj[FastMath.nextRandomInt(0, obj.length - 1)].clone(false); t = obj[FastMath.nextRandomInt(0, obj.length - 1)].clone(false);
t.setLocalScale(FastMath.nextRandomFloat() * 10f); t.setLocalScale(FastMath.nextRandomFloat() * 10f);
t.setMaterial(mat[FastMath.nextRandomInt(0, mat.length - 1)]); t.setMaterial(mat[FastMath.nextRandomInt(0, mat.length - 1)]);
rootNode.attachChild(t); rootNode.attachChild(t);
@ -142,8 +150,8 @@ public class TestDirectionalLightShadow extends SimpleApplication implements Act
rootNode.addLight(l); rootNode.addLight(l);
AmbientLight al = new AmbientLight(); al = new AmbientLight();
al.setColor(ColorRGBA.White.mult(0.5f)); al.setColor(ColorRGBA.White.mult(0.02f));
rootNode.addLight(al); rootNode.addLight(al);
Spatial sky = SkyFactory.createSky(assetManager, "Scenes/Beach/FullskiesSunset0068.dds", false); Spatial sky = SkyFactory.createSky(assetManager, "Scenes/Beach/FullskiesSunset0068.dds", false);
@ -156,8 +164,11 @@ public class TestDirectionalLightShadow extends SimpleApplication implements Act
@Override @Override
public void simpleInitApp() { public void simpleInitApp() {
// put the camera in a bad position // put the camera in a bad position
cam.setLocation(new Vector3f(65.25412f, 44.38738f, 9.087874f)); // cam.setLocation(new Vector3f(65.25412f, 44.38738f, 9.087874f));
cam.setRotation(new Quaternion(0.078139365f, 0.050241485f, -0.003942559f, 0.9956679f)); // cam.setRotation(new Quaternion(0.078139365f, 0.050241485f, -0.003942559f, 0.9956679f));
cam.setLocation(new Vector3f(3.3720117f, 42.838284f, -83.43792f));
cam.setRotation(new Quaternion(0.13833192f, -0.08969371f, 0.012581267f, 0.9862358f));
flyCam.setMoveSpeed(100); flyCam.setMoveSpeed(100);
@ -166,7 +177,7 @@ public class TestDirectionalLightShadow extends SimpleApplication implements Act
dlsr = new DirectionalLightShadowRenderer(assetManager, SHADOWMAP_SIZE, 3); dlsr = new DirectionalLightShadowRenderer(assetManager, SHADOWMAP_SIZE, 3);
dlsr.setLight(l); dlsr.setLight(l);
dlsr.setLambda(0.55f); dlsr.setLambda(0.55f);
dlsr.setShadowIntensity(0.6f); dlsr.setShadowIntensity(0.8f);
dlsr.setEdgeFilteringMode(EdgeFilteringMode.Nearest); dlsr.setEdgeFilteringMode(EdgeFilteringMode.Nearest);
dlsr.displayDebug(); dlsr.displayDebug();
viewPort.addProcessor(dlsr); viewPort.addProcessor(dlsr);
@ -174,7 +185,7 @@ public class TestDirectionalLightShadow extends SimpleApplication implements Act
dlsf = new DirectionalLightShadowFilter(assetManager, SHADOWMAP_SIZE, 3); dlsf = new DirectionalLightShadowFilter(assetManager, SHADOWMAP_SIZE, 3);
dlsf.setLight(l); dlsf.setLight(l);
dlsf.setLambda(0.55f); dlsf.setLambda(0.55f);
dlsf.setShadowIntensity(0.6f); dlsf.setShadowIntensity(0.8f);
dlsf.setEdgeFilteringMode(EdgeFilteringMode.Nearest); dlsf.setEdgeFilteringMode(EdgeFilteringMode.Nearest);
dlsf.setEnabled(false); dlsf.setEnabled(false);
@ -205,10 +216,11 @@ public class TestDirectionalLightShadow extends SimpleApplication implements Act
inputManager.addMapping("fwd", new KeyTrigger(KeyInput.KEY_PGUP)); inputManager.addMapping("fwd", new KeyTrigger(KeyInput.KEY_PGUP));
inputManager.addMapping("back", new KeyTrigger(KeyInput.KEY_PGDN)); inputManager.addMapping("back", new KeyTrigger(KeyInput.KEY_PGDN));
inputManager.addMapping("pp", new KeyTrigger(KeyInput.KEY_P)); inputManager.addMapping("pp", new KeyTrigger(KeyInput.KEY_P));
inputManager.addMapping("backShadows", new KeyTrigger(KeyInput.KEY_B));
inputManager.addListener(this, "lambdaUp", "lambdaDown", "ThicknessUp", "ThicknessDown", inputManager.addListener(this, "lambdaUp", "lambdaDown", "ThicknessUp", "ThicknessDown",
"switchGroundMat", "debug", "up", "down", "right", "left", "fwd", "back", "pp", "stabilize", "distance"); "switchGroundMat", "debug", "up", "down", "right", "left", "fwd", "back", "pp", "stabilize", "distance", "ShadowUp", "ShadowDown", "backShadows");
ShadowTestUIManager uiMan = new ShadowTestUIManager(assetManager, dlsr, dlsf, guiNode, inputManager, viewPort); ShadowTestUIManager uiMan = new ShadowTestUIManager(assetManager, dlsr, dlsf, guiNode, inputManager, viewPort);
@ -255,12 +267,19 @@ public class TestDirectionalLightShadow extends SimpleApplication implements Act
dlsf.setLambda(dlsr.getLambda() - 0.01f); dlsf.setLambda(dlsr.getLambda() - 0.01f);
System.out.println("Lambda : " + dlsr.getLambda()); System.out.println("Lambda : " + dlsr.getLambda());
} }
if ((name.equals("ShadowUp") || name.equals("ShadowDown")) && keyPressed) {
al.setColor(ColorRGBA.White.mult((1 - dlsr.getShadowIntensity()) * 0.2f));
}
if (name.equals("debug") && keyPressed) { if (name.equals("debug") && keyPressed) {
dlsr.displayFrustum(); dlsr.displayFrustum();
} }
if (name.equals("backShadows") && keyPressed) {
dlsr.setRenderBackFacesShadows(!dlsr.isRenderBackFacesShadows());
dlsf.setRenderBackFacesShadows(!dlsf.isRenderBackFacesShadows());
}
if (name.equals("stabilize") && keyPressed) { if (name.equals("stabilize") && keyPressed) {
dlsr.setEnabledStabilization(!dlsr.isEnabledStabilization()); dlsr.setEnabledStabilization(!dlsr.isEnabledStabilization());
dlsf.setEnabledStabilization(!dlsf.isEnabledStabilization()); dlsf.setEnabledStabilization(!dlsf.isEnabledStabilization());

@ -32,7 +32,10 @@
package jme3test.light; package jme3test.light;
import com.jme3.app.SimpleApplication; import com.jme3.app.SimpleApplication;
import com.jme3.input.controls.ActionListener;
import com.jme3.light.AmbientLight;
import com.jme3.light.PointLight; import com.jme3.light.PointLight;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Quaternion; import com.jme3.math.Quaternion;
import com.jme3.math.Vector3f; import com.jme3.math.Vector3f;
import com.jme3.post.FilterPostProcessor; import com.jme3.post.FilterPostProcessor;
@ -45,7 +48,7 @@ import com.jme3.shadow.EdgeFilteringMode;
import com.jme3.shadow.PointLightShadowFilter; import com.jme3.shadow.PointLightShadowFilter;
import com.jme3.shadow.PointLightShadowRenderer; import com.jme3.shadow.PointLightShadowRenderer;
public class TestPointLightShadows extends SimpleApplication { public class TestPointLightShadows extends SimpleApplication implements ActionListener{
public static final int SHADOWMAP_SIZE = 512; public static final int SHADOWMAP_SIZE = 512;
public static void main(String[] args) { public static void main(String[] args) {
@ -55,13 +58,19 @@ public class TestPointLightShadows extends SimpleApplication {
Node lightNode; Node lightNode;
PointLightShadowRenderer plsr; PointLightShadowRenderer plsr;
PointLightShadowFilter plsf; PointLightShadowFilter plsf;
AmbientLight al;
@Override @Override
public void simpleInitApp() { public void simpleInitApp () {
flyCam.setMoveSpeed(10); flyCam.setMoveSpeed(10);
cam.setLocation(new Vector3f(0.040581334f, 1.7745866f, 6.155161f)); cam.setLocation(new Vector3f(0.040581334f, 1.7745866f, 6.155161f));
cam.setRotation(new Quaternion(4.3868728E-5f, 0.9999293f, -0.011230096f, 0.0039059948f)); cam.setRotation(new Quaternion(4.3868728E-5f, 0.9999293f, -0.011230096f, 0.0039059948f));
al = new AmbientLight(ColorRGBA.White.mult(0.02f));
rootNode.addLight(al);
Node scene = (Node) assetManager.loadModel("Models/Test/CornellBox.j3o"); Node scene = (Node) assetManager.loadModel("Models/Test/CornellBox.j3o");
scene.setShadowMode(RenderQueue.ShadowMode.CastAndReceive); scene.setShadowMode(RenderQueue.ShadowMode.CastAndReceive);
@ -89,6 +98,7 @@ public class TestPointLightShadows extends SimpleApplication {
plsr.setEdgeFilteringMode(EdgeFilteringMode.PCF4); plsr.setEdgeFilteringMode(EdgeFilteringMode.PCF4);
plsr.setShadowZExtend(15); plsr.setShadowZExtend(15);
plsr.setShadowZFadeLength(5); plsr.setShadowZFadeLength(5);
plsr.setShadowIntensity(0.9f);
// plsr.setFlushQueues(false); // plsr.setFlushQueues(false);
//plsr.displayFrustum(); //plsr.displayFrustum();
plsr.displayDebug(); plsr.displayDebug();
@ -99,18 +109,27 @@ public class TestPointLightShadows extends SimpleApplication {
plsf.setLight((PointLight) scene.getLocalLightList().get(0)); plsf.setLight((PointLight) scene.getLocalLightList().get(0));
plsf.setShadowZExtend(15); plsf.setShadowZExtend(15);
plsf.setShadowZFadeLength(5); plsf.setShadowZFadeLength(5);
plsf.setShadowIntensity(0.8f);
plsf.setEdgeFilteringMode(EdgeFilteringMode.PCF4); plsf.setEdgeFilteringMode(EdgeFilteringMode.PCF4);
plsf.setEnabled(false); plsf.setEnabled(false);
FilterPostProcessor fpp = new FilterPostProcessor(assetManager); FilterPostProcessor fpp = new FilterPostProcessor(assetManager);
fpp.addFilter(plsf); fpp.addFilter(plsf);
viewPort.addProcessor(fpp); viewPort.addProcessor(fpp);
inputManager.addListener(this,"ShadowUp","ShadowDown");
ShadowTestUIManager uiMan = new ShadowTestUIManager(assetManager, plsr, plsf, guiNode, inputManager, viewPort); ShadowTestUIManager uiMan = new ShadowTestUIManager(assetManager, plsr, plsf, guiNode, inputManager, viewPort);
} }
@Override @Override
public void simpleUpdate(float tpf) { public void simpleUpdate(float tpf) {
// lightNode.move(FastMath.cos(tpf) * 0.4f, 0, FastMath.sin(tpf) * 0.4f); // lightNode.move(FastMath.cos(tpf) * 0.4f, 0, FastMath.sin(tpf) * 0.4f);
} }
@Override
public void onAction(String name, boolean isPressed, float tpf) {
if ((name.equals("ShadowUp") || name.equals("ShadowDown")) && isPressed) {
al.setColor(ColorRGBA.White.mult((1 - plsr.getShadowIntensity()) * 0.2f));
}
}
} }

@ -67,6 +67,8 @@ import com.jme3.texture.Texture;
import com.jme3.texture.Texture.WrapMode; import com.jme3.texture.Texture.WrapMode;
import com.jme3.util.SkyFactory; import com.jme3.util.SkyFactory;
import com.jme3.util.TangentBinormalGenerator; import com.jme3.util.TangentBinormalGenerator;
import com.jme3.util.clone.Cloner;
import com.jme3.util.clone.JmeCloneable;
import java.io.IOException; import java.io.IOException;
public class TestPssmShadow extends SimpleApplication implements ActionListener { public class TestPssmShadow extends SimpleApplication implements ActionListener {
@ -249,10 +251,20 @@ public class TestPssmShadow extends SimpleApplication implements ActionListener
time = 0; time = 0;
} }
@Override
public Object jmeClone() {
return null;
}
@Override
public void cloneFields( Cloner cloner, Object original ) {
}
@Override @Override
protected void controlRender(RenderManager rm, ViewPort vp) { protected void controlRender(RenderManager rm, ViewPort vp) {
} }
@Override
public Control cloneForSpatial(Spatial spatial) { public Control cloneForSpatial(Spatial spatial) {
return null; return null;
} }

@ -58,7 +58,7 @@ public class TestSpotLight extends SimpleApplication {
Geometry lightMdl; Geometry lightMdl;
public void setupLighting(){ public void setupLighting(){
AmbientLight al=new AmbientLight(); AmbientLight al=new AmbientLight();
al.setColor(ColorRGBA.White.mult(0.8f)); al.setColor(ColorRGBA.White.mult(0.02f));
rootNode.addLight(al); rootNode.addLight(al);
spot=new SpotLight(); spot=new SpotLight();

@ -65,7 +65,7 @@ public class TestSpotLightShadows extends SimpleApplication {
public void setupLighting() { public void setupLighting() {
AmbientLight al = new AmbientLight(); AmbientLight al = new AmbientLight();
al.setColor(ColorRGBA.White.mult(0.3f)); al.setColor(ColorRGBA.White.mult(0.02f));
rootNode.addLight(al); rootNode.addLight(al);
rootNode.setShadowMode(ShadowMode.CastAndReceive); rootNode.setShadowMode(ShadowMode.CastAndReceive);
@ -132,11 +132,6 @@ public class TestSpotLightShadows extends SimpleApplication {
}, "stop"); }, "stop");
inputManager.addMapping("stop", new KeyTrigger(KeyInput.KEY_1)); inputManager.addMapping("stop", new KeyTrigger(KeyInput.KEY_1));
MaterialDebugAppState s = new MaterialDebugAppState();
s.registerBinding("Common/MatDefs/Shadow/PostShadow15.frag", rootNode);
s.registerBinding(new KeyTrigger(KeyInput.KEY_R), rootNode);
stateManager.attach(s);
flyCam.setDragToRotate(true); flyCam.setDragToRotate(true);
} }

@ -95,7 +95,7 @@ public class TestSpotLightTerrain extends SimpleApplication {
rootNode.addLight(sl); rootNode.addLight(sl);
AmbientLight ambLight = new AmbientLight(); AmbientLight ambLight = new AmbientLight();
ambLight.setColor(new ColorRGBA(0.8f, 0.8f, 0.8f, 0.2f)); ambLight.setColor(ColorRGBA.Black);
rootNode.addLight(ambLight); rootNode.addLight(ambLight);
cam.setLocation(new Vector3f(-41.219646f, -84.8363f, -171.67267f)); cam.setLocation(new Vector3f(-41.219646f, -84.8363f, -171.67267f));

@ -41,6 +41,8 @@ import java.awt.Component;
import java.awt.Dimension; import java.awt.Dimension;
import java.awt.event.ActionEvent; import java.awt.event.ActionEvent;
import java.io.IOException; import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.*; import javax.swing.*;
import jme3test.network.TestChatServer.ChatMessage; import jme3test.network.TestChatServer.ChatMessage;
@ -115,6 +117,18 @@ public class TestChatClient extends JFrame {
public static void main(String... args) throws Exception { public static void main(String... args) throws Exception {
// Increate the logging level for networking...
System.out.println("Setting logging to max");
Logger networkLog = Logger.getLogger("com.jme3.network");
networkLog.setLevel(Level.FINEST);
// And we have to tell JUL's handler also
// turn up logging in a very convoluted way
Logger rootLog = Logger.getLogger("");
if( rootLog.getHandlers().length > 0 ) {
rootLog.getHandlers()[0].setLevel(Level.FINEST);
}
// Note: in JME 3.1 this is generally unnecessary as the server will // Note: in JME 3.1 this is generally unnecessary as the server will
// send a message with all server-registered classes. // send a message with all server-registered classes.
// TestChatServer.initializeClasses(); // TestChatServer.initializeClasses();

@ -31,6 +31,9 @@
*/ */
package jme3test.network; package jme3test.network;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.jme3.network.*; import com.jme3.network.*;
import com.jme3.network.serializing.Serializable; import com.jme3.network.serializing.Serializable;
import com.jme3.network.serializing.Serializer; import com.jme3.network.serializing.Serializer;
@ -56,11 +59,15 @@ public class TestChatServer {
private boolean isRunning; private boolean isRunning;
public TestChatServer() throws IOException { public TestChatServer() throws IOException {
initializeClasses();
// Use this to test the client/server name version check // Use this to test the client/server name version check
this.server = Network.createServer(NAME, VERSION, PORT, UDP_PORT); this.server = Network.createServer(NAME, VERSION, PORT, UDP_PORT);
// Initialize our own messages only after the server has been created.
// It registers some additional messages with the serializer by default
// that need to go before custom messages.
initializeClasses();
ChatHandler handler = new ChatHandler(); ChatHandler handler = new ChatHandler();
server.addMessageListener(handler, ChatMessage.class); server.addMessageListener(handler, ChatMessage.class);
@ -122,6 +129,18 @@ public class TestChatServer {
public static void main(String... args) throws Exception { public static void main(String... args) throws Exception {
// Increate the logging level for networking...
System.out.println("Setting logging to max");
Logger networkLog = Logger.getLogger("com.jme3.network");
networkLog.setLevel(Level.FINEST);
// And we have to tell JUL's handler also
// turn up logging in a very convoluted way
Logger rootLog = Logger.getLogger("");
if( rootLog.getHandlers().length > 0 ) {
rootLog.getHandlers()[0].setLevel(Level.FINEST);
}
TestChatServer chatServer = new TestChatServer(); TestChatServer chatServer = new TestChatServer();
chatServer.start(); chatServer.start();

@ -99,24 +99,6 @@ public abstract class LwjglContext implements JmeContext {
return Integer.MAX_VALUE; return Integer.MAX_VALUE;
} }
protected void loadNatives() {
if (JmeSystem.isLowPermissions()) {
return;
}
if ("LWJGL".equals(settings.getAudioRenderer())) {
NativeLibraryLoader.loadNativeLibrary("openal-lwjgl3", true);
}
if (NativeLibraryLoader.isUsingNativeBullet()) {
NativeLibraryLoader.loadNativeLibrary("bulletjme", true);
}
NativeLibraryLoader.loadNativeLibrary("glfw-lwjgl3", true);
NativeLibraryLoader.loadNativeLibrary("jemalloc-lwjgl3", true);
NativeLibraryLoader.loadNativeLibrary("lwjgl3", true);
}
protected int getNumSamplesToUse() { protected int getNumSamplesToUse() {
int samples = 0; int samples = 0;
if (settings.getSamples() > 1) { if (settings.getSamples() > 1) {
@ -134,6 +116,17 @@ public abstract class LwjglContext implements JmeContext {
return samples; return samples;
} }
protected void loadNatives() {
if (JmeSystem.isLowPermissions()) {
return;
}
if (NativeLibraryLoader.isUsingNativeBullet()) {
NativeLibraryLoader.loadNativeLibrary("bulletjme", true);
}
}
protected void initContextFirstTime() { protected void initContextFirstTime() {
final GLCapabilities capabilities = createCapabilities(settings.getRenderer().equals(AppSettings.LWJGL_OPENGL3)); final GLCapabilities capabilities = createCapabilities(settings.getRenderer().equals(AppSettings.LWJGL_OPENGL3));

@ -81,6 +81,8 @@ public abstract class LwjglWindow extends LwjglContext implements Runnable {
private GLFWWindowSizeCallback windowSizeCallback; private GLFWWindowSizeCallback windowSizeCallback;
private GLFWWindowFocusCallback windowFocusCallback; private GLFWWindowFocusCallback windowFocusCallback;
private Thread mainThread;
public LwjglWindow(final JmeContext.Type type) { public LwjglWindow(final JmeContext.Type type) {
if (!JmeContext.Type.Display.equals(type) && !JmeContext.Type.OffscreenSurface.equals(type) && !JmeContext.Type.Canvas.equals(type)) { if (!JmeContext.Type.Display.equals(type) && !JmeContext.Type.OffscreenSurface.equals(type) && !JmeContext.Type.Canvas.equals(type)) {
throw new IllegalArgumentException("Unsupported type '" + type.name() + "' provided"); throw new IllegalArgumentException("Unsupported type '" + type.name() + "' provided");
@ -210,7 +212,6 @@ public abstract class LwjglWindow extends LwjglContext implements Runnable {
@Override @Override
public void invoke(final long window, final int focused) { public void invoke(final long window, final int focused) {
final boolean focus = (focused == GL_TRUE); final boolean focus = (focused == GL_TRUE);
if (wasActive != focus) { if (wasActive != focus) {
if (!wasActive) { if (!wasActive) {
listener.gainFocus(); listener.gainFocus();
@ -241,10 +242,6 @@ public abstract class LwjglWindow extends LwjglContext implements Runnable {
glfwSwapInterval(0); glfwSwapInterval(0);
} }
// Make the window visible
if (Type.Display.equals(type)) {
glfwShowWindow(window);
}
glfwShowWindow(window); glfwShowWindow(window);
@ -286,17 +283,16 @@ public abstract class LwjglWindow extends LwjglContext implements Runnable {
} }
} }
@Override
public void create(boolean waitFor) { public void create(boolean waitFor) {
if (created.get()) { if (created.get()) {
LOGGER.warning("create() called when display is already created!"); LOGGER.warning("create() called when display is already created!");
return; return;
} }
new Thread(this, THREAD_NAME).start(); // NOTE: this is required for Mac OS X!
mainThread = Thread.currentThread();
if (waitFor) { run();
waitFor(true);
}
} }
/** /**
@ -307,6 +303,7 @@ public abstract class LwjglWindow extends LwjglContext implements Runnable {
if (!JmeSystem.isLowPermissions()) { if (!JmeSystem.isLowPermissions()) {
// Enable uncaught exception handler only for current thread // Enable uncaught exception handler only for current thread
Thread.currentThread().setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() { Thread.currentThread().setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
@Override
public void uncaughtException(Thread thread, Throwable thrown) { public void uncaughtException(Thread thread, Throwable thrown) {
listener.handleError("Uncaught exception thrown in " + thread.toString(), thrown); listener.handleError("Uncaught exception thrown in " + thread.toString(), thrown);
if (needClose.get()) { if (needClose.get()) {
@ -318,6 +315,8 @@ public abstract class LwjglWindow extends LwjglContext implements Runnable {
}); });
} }
loadNatives();
timer = new NanoTimer(); timer = new NanoTimer();
// For canvas, this will create a pbuffer, // For canvas, this will create a pbuffer,
@ -434,13 +433,13 @@ public abstract class LwjglWindow extends LwjglContext implements Runnable {
LOGGER.fine("Display destroyed."); LOGGER.fine("Display destroyed.");
} }
@Override
public void run() { public void run() {
if (listener == null) { if (listener == null) {
throw new IllegalStateException("SystemListener is not set on context!" throw new IllegalStateException("SystemListener is not set on context!"
+ "Must set with JmeContext.setSystemListener()."); + "Must set with JmeContext.setSystemListener().");
} }
loadNatives();
LOGGER.log(Level.FINE, "Using LWJGL {0}", Version.getVersion()); LOGGER.log(Level.FINE, "Using LWJGL {0}", Version.getVersion());
if (!initInThread()) { if (!initInThread()) {
@ -497,6 +496,11 @@ public abstract class LwjglWindow extends LwjglContext implements Runnable {
public void destroy(boolean waitFor) { public void destroy(boolean waitFor) {
needClose.set(true); needClose.set(true);
if (mainThread == Thread.currentThread()) {
// Ignore waitFor.
return;
}
if (waitFor) { if (waitFor) {
waitFor(false); waitFor(false);
} }

@ -14,4 +14,5 @@ sourceSets {
dependencies { dependencies {
compile project(':jme3-core') compile project(':jme3-core')
testCompile project(':jme3-desktop')
} }

@ -0,0 +1,78 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package com.jme3.material.plugin.export.material;
import com.jme3.export.JmeExporter;
import com.jme3.export.OutputCapsule;
import com.jme3.export.Savable;
import com.jme3.material.Material;
import com.jme3.material.MaterialDef;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.nio.charset.Charset;
/**
* Saves a Material to a j3m file with proper formatting.
*
* usage is :
* <pre>
* J3MExporter exporter = new J3MExporter();
* exporter.save(material, myFile);
* //or
* exporter.save(material, myOutputStream);
* </pre>
*
* @author tsr
* @author nehon (documentation and safety check)
*/
public class J3MExporter implements JmeExporter {
private final J3MRootOutputCapsule rootCapsule;
/**
* Create a J3MExporter
*/
public J3MExporter() {
rootCapsule = new J3MRootOutputCapsule(this);
}
@Override
public void save(Savable object, OutputStream f) throws IOException {
if (!(object instanceof Material)) {
throw new IllegalArgumentException("J3MExporter can only save com.jme3.material.Material class");
}
OutputStreamWriter out = new OutputStreamWriter(f, Charset.forName("UTF-8"));
rootCapsule.clear();
object.write(this);
rootCapsule.writeToStream(out);
out.flush();
}
@Override
public void save(Savable object, File f) throws IOException {
try (FileOutputStream fos = new FileOutputStream(f)) {
save(object, fos);
}
}
@Override
public OutputCapsule getCapsule(Savable object) {
if ((object instanceof Material) || (object instanceof MaterialDef)) {
return rootCapsule;
}
return rootCapsule.getCapsule(object);
}
}

@ -0,0 +1,383 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package com.jme3.material.plugin.export.material;
import com.jme3.asset.TextureKey;
import com.jme3.export.OutputCapsule;
import com.jme3.export.Savable;
import com.jme3.material.MatParam;
import com.jme3.material.MatParamTexture;
import com.jme3.math.*;
import com.jme3.shader.VarType;
import com.jme3.texture.Texture;
import com.jme3.texture.Texture.WrapMode;
import com.jme3.util.IntMap;
import java.io.IOException;
import java.io.OutputStreamWriter;
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;
/**
*
* @author tsr
*/
public class J3MOutputCapsule implements OutputCapsule {
private final HashMap<String, String> parameters;
protected final J3MExporter exporter;
public J3MOutputCapsule(J3MExporter exporter) {
this.exporter = exporter;
parameters = new HashMap<>();
}
public void writeToStream(OutputStreamWriter out) throws IOException {
for (String key : parameters.keySet()) {
out.write(" ");
writeParameter(out, key, parameters.get(key));
out.write("\n");
}
}
protected void writeParameter(OutputStreamWriter out, String name, String value) throws IOException {
out.write(name);
out.write(" : ");
out.write(value);
}
public void clear() {
parameters.clear();
}
protected void putParameter(String name, String value) {
parameters.put(name, value);
}
@Override
public void write(boolean value, String name, boolean defVal) throws IOException {
if (value == defVal) {
return;
}
putParameter(name, ((value) ? "On" : "Off"));
}
@Override
public void writeStringSavableMap(Map<String, ? extends Savable> map, String name, Map<String, ? extends Savable> defVal) throws IOException {
for (String key : map.keySet()) {
Savable value = map.get(key);
if (defVal == null || !defVal.containsKey(key) || !defVal.get(key).equals(value)) {
putParameter(key, format(value));
}
}
}
protected String format(Savable value) {
if (value instanceof MatParamTexture) {
return formatMatParamTexture((MatParamTexture) value);
}
if (value instanceof MatParam) {
return formatMatParam((MatParam) value);
}
throw new UnsupportedOperationException(value.getClass() + ": Not supported yet.");
}
private String formatMatParam(MatParam param){
VarType type = param.getVarType();
Object val = param.getValue();
switch (type) {
case Boolean:
case Float:
case Int:
return val.toString();
case Vector2:
Vector2f v2 = (Vector2f) val;
return v2.getX() + " " + v2.getY();
case Vector3:
Vector3f v3 = (Vector3f) val;
return v3.getX() + " " + v3.getY() + " " + v3.getZ();
case Vector4:
// can be either ColorRGBA, Vector4f or Quaternion
if (val instanceof Vector4f) {
Vector4f v4 = (Vector4f) val;
return v4.getX() + " " + v4.getY() + " "
+ v4.getZ() + " " + v4.getW();
} else if (val instanceof ColorRGBA) {
ColorRGBA color = (ColorRGBA) val;
return color.getRed() + " " + color.getGreen() + " "
+ color.getBlue() + " " + color.getAlpha();
} else if (val instanceof Quaternion) {
Quaternion quat = (Quaternion) val;
return quat.getX() + " " + quat.getY() + " "
+ quat.getZ() + " " + quat.getW();
} else {
throw new UnsupportedOperationException("Unexpected Vector4 type: " + val);
}
default:
return null; // parameter type not supported in J3M
}
}
protected static String formatMatParamTexture(MatParamTexture param) {
StringBuilder ret = new StringBuilder();
Texture tex = (Texture) param.getValue();
TextureKey key;
if (tex != null) {
key = (TextureKey) tex.getKey();
if (key != null && key.isFlipY()) {
ret.append("Flip ");
}
ret.append(formatWrapMode(tex, Texture.WrapAxis.S));
ret.append(formatWrapMode(tex, Texture.WrapAxis.T));
ret.append(formatWrapMode(tex, Texture.WrapAxis.R));
//Min and Mag filter
Texture.MinFilter def = Texture.MinFilter.BilinearNoMipMaps;
if (tex.getImage().hasMipmaps() || (key != null && key.isGenerateMips())) {
def = Texture.MinFilter.Trilinear;
}
if (tex.getMinFilter() != def) {
ret.append("Min").append(tex.getMinFilter().name()).append(" ");
}
if (tex.getMagFilter() != Texture.MagFilter.Bilinear) {
ret.append("Mag").append(tex.getMagFilter().name()).append(" ");
}
ret.append("\"").append(key.getName()).append("\"");
}
return ret.toString();
}
protected static String formatWrapMode(Texture texVal, Texture.WrapAxis axis) {
WrapMode mode;
try {
mode = texVal.getWrap(axis);
} catch (IllegalArgumentException e) {
//this axis doesn't exist on the texture
return "";
}
if (mode != WrapMode.EdgeClamp) {
return "Wrap" + mode.name() + "_" + axis.name() + " ";
}
return "";
}
@Override
public void write(Enum value, String name, Enum defVal) throws IOException {
if (value == defVal) {
return;
}
putParameter(name, value.toString());
}
@Override
public void write(float value, String name, float defVal) throws IOException {
if (value == defVal) {
return;
}
putParameter(name, Float.toString(value));
}
@Override
public void write(float[] value, String name, float[] defVal) throws IOException {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
@Override
public void write(float[][] value, String name, float[][] defVal) throws IOException {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
@Override
public void write(double value, String name, double defVal) throws IOException {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
@Override
public void write(double[] value, String name, double[] defVal) throws IOException {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
@Override
public void write(double[][] value, String name, double[][] defVal) throws IOException {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
@Override
public void write(long value, String name, long defVal) throws IOException {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
@Override
public void write(long[] value, String name, long[] defVal) throws IOException {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
@Override
public void write(long[][] value, String name, long[][] defVal) throws IOException {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
@Override
public void write(short value, String name, short defVal) throws IOException {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
@Override
public void write(short[] value, String name, short[] defVal) throws IOException {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
@Override
public void write(short[][] value, String name, short[][] defVal) throws IOException {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
@Override
public void write(boolean[] value, String name, boolean[] defVal) throws IOException {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
@Override
public void write(boolean[][] value, String name, boolean[][] defVal) throws IOException {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
@Override
public void write(String value, String name, String defVal) throws IOException {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
@Override
public void write(String[] value, String name, String[] defVal) throws IOException {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
@Override
public void write(String[][] value, String name, String[][] defVal) throws IOException {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
@Override
public void write(BitSet value, String name, BitSet defVal) throws IOException {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
@Override
public void write(Savable object, String name, Savable defVal) throws IOException {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
@Override
public void write(Savable[] objects, String name, Savable[] defVal) throws IOException {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
@Override
public void write(Savable[][] objects, String name, Savable[][] defVal) throws IOException {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
@Override
public void writeSavableArrayList(ArrayList array, String name, ArrayList defVal) throws IOException {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
@Override
public void writeSavableArrayListArray(ArrayList[] array, String name, ArrayList[] defVal) throws IOException {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
@Override
public void writeSavableArrayListArray2D(ArrayList[][] array, String name, ArrayList[][] defVal) throws IOException {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
@Override
public void writeFloatBufferArrayList(ArrayList<FloatBuffer> array, String name, ArrayList<FloatBuffer> defVal) throws IOException {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
@Override
public void writeByteBufferArrayList(ArrayList<ByteBuffer> array, String name, ArrayList<ByteBuffer> defVal) throws IOException {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
@Override
public void writeSavableMap(Map<? extends Savable, ? extends Savable> map, String name, Map<? extends Savable, ? extends Savable> defVal) throws IOException {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
@Override
public void writeIntSavableMap(IntMap<? extends Savable> map, String name, IntMap<? extends Savable> defVal) throws IOException {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
@Override
public void write(FloatBuffer value, String name, FloatBuffer defVal) throws IOException {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
@Override
public void write(IntBuffer value, String name, IntBuffer defVal) throws IOException {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
@Override
public void write(ByteBuffer value, String name, ByteBuffer defVal) throws IOException {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
@Override
public void write(ShortBuffer value, String name, ShortBuffer defVal) throws IOException {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
@Override
public void write(byte value, String name, byte defVal) throws IOException {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
@Override
public void write(byte[] value, String name, byte[] defVal) throws IOException {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
@Override
public void write(byte[][] value, String name, byte[][] defVal) throws IOException {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
@Override
public void write(int value, String name, int defVal) throws IOException {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
@Override
public void write(int[] value, String name, int[] defVal) throws IOException {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
@Override
public void write(int[][] value, String name, int[][] defVal) throws IOException {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
}

@ -0,0 +1,81 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package com.jme3.material.plugin.export.material;
import com.jme3.export.OutputCapsule;
import com.jme3.export.Savable;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.util.HashMap;
/**
*
* @author tsr
*/
public class J3MRenderStateOutputCapsule extends J3MOutputCapsule {
protected final static HashMap<String, String> NAME_MAP;
protected String offsetUnit;
static {
NAME_MAP = new HashMap<>();
NAME_MAP.put( "wireframe", "Wireframe");
NAME_MAP.put( "cullMode", "FaceCull");
NAME_MAP.put( "depthWrite", "DepthWrite");
NAME_MAP.put( "depthTest", "DepthTest");
NAME_MAP.put( "blendMode", "Blend");
NAME_MAP.put( "alphaFallOff", "AlphaTestFalloff");
NAME_MAP.put( "offsetFactor", "PolyOffset");
NAME_MAP.put( "colorWrite", "ColorWrite");
NAME_MAP.put( "pointSprite", "PointSprite");
NAME_MAP.put( "depthFunc", "DepthFunc");
NAME_MAP.put( "alphaFunc", "AlphaFunc");
}
public J3MRenderStateOutputCapsule(J3MExporter exporter) {
super(exporter);
}
public OutputCapsule getCapsule(Savable object) {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
@Override
public void clear() {
super.clear();
offsetUnit = "";
}
@Override
public void writeToStream(OutputStreamWriter out) throws IOException {
out.write(" AdditionalRenderState {\n");
super.writeToStream(out);
out.write(" }\n");
}
@Override
protected void writeParameter(OutputStreamWriter out, String name, String value) throws IOException {
out.write(name);
out.write(" ");
out.write(value);
if( "PolyOffset".equals(name) ) {
out.write(" ");
out.write(offsetUnit);
}
}
@Override
protected void putParameter(String name, String value ) {
if( "offsetUnits".equals(name) ) {
offsetUnit = value;
return;
}
if( !NAME_MAP.containsKey(name) )
return;
super.putParameter(NAME_MAP.get(name), value);
}
}

@ -0,0 +1,99 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package com.jme3.material.plugin.export.material;
import com.jme3.export.OutputCapsule;
import com.jme3.export.Savable;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.util.HashMap;
/**
* @author tsr
*/
public class J3MRootOutputCapsule extends J3MOutputCapsule {
private final HashMap<Savable, J3MOutputCapsule> outCapsules;
private String name;
private String materialDefinition;
private Boolean isTransparent;
public J3MRootOutputCapsule(J3MExporter exporter) {
super(exporter);
outCapsules = new HashMap<>();
}
@Override
public void clear() {
super.clear();
isTransparent = null;
name = "";
materialDefinition = "";
outCapsules.clear();
}
public OutputCapsule getCapsule(Savable object) {
if (!outCapsules.containsKey(object)) {
outCapsules.put(object, new J3MRenderStateOutputCapsule(exporter));
}
return outCapsules.get(object);
}
@Override
public void writeToStream(OutputStreamWriter out) throws IOException {
out.write("Material " + name + " : " + materialDefinition + " {\n\n");
if (isTransparent != null)
out.write(" Transparent " + ((isTransparent) ? "On" : "Off") + "\n\n");
out.write(" MaterialParameters {\n");
super.writeToStream(out);
out.write(" }\n\n");
for (J3MOutputCapsule c : outCapsules.values()) {
c.writeToStream(out);
}
out.write("}\n");
}
@Override
public void write(String value, String name, String defVal) throws IOException {
switch (name) {
case "material_def":
materialDefinition = value;
break;
case "name":
this.name = value;
break;
default:
throw new UnsupportedOperationException(name + " string material parameter not supported yet");
}
}
@Override
public void write(boolean value, String name, boolean defVal) throws IOException {
if( value == defVal)
return;
switch (name) {
case "is_transparent":
isTransparent = value;
break;
default:
throw new UnsupportedOperationException(name + " boolean material parameter not supported yet");
}
}
@Override
public void write(Savable object, String name, Savable defVal) throws IOException {
if(object != null && !object.equals(defVal)) {
object.write(exporter);
}
}
}

@ -0,0 +1,115 @@
/*
* Copyright (c) 2009-2016 jMonkeyEngine
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR 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.plugin;
import com.jme3.asset.AssetInfo;
import com.jme3.asset.AssetKey;
import com.jme3.asset.AssetManager;
import com.jme3.material.Material;
import com.jme3.material.RenderState;
import com.jme3.material.plugin.export.material.J3MExporter;
import com.jme3.material.plugins.J3MLoader;
import com.jme3.math.ColorRGBA;
import com.jme3.system.JmeSystem;
import static org.junit.Assert.*;
import com.jme3.texture.Texture;
import com.jme3.texture.Texture2D;
import org.junit.Before;
import org.junit.Test;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Scanner;
public class TestMaterialWrite {
private AssetManager assetManager;
@Before
public void init() {
assetManager = JmeSystem.newAssetManager(
TestMaterialWrite.class.getResource("/com/jme3/asset/Desktop.cfg"));
}
@Test
public void testWriteMat() throws Exception {
Material mat = new Material(assetManager,"Common/MatDefs/Light/Lighting.j3md");
mat.setBoolean("UseMaterialColors", true);
mat.setColor("Diffuse", ColorRGBA.White);
mat.setColor("Ambient", ColorRGBA.DarkGray);
mat.setFloat("AlphaDiscardThreshold", 0.5f);
mat.setFloat("Shininess", 2.5f);
Texture tex = assetManager.loadTexture("Common/Textures/MissingTexture.png");
tex.setMagFilter(Texture.MagFilter.Nearest);
tex.setMinFilter(Texture.MinFilter.BilinearNoMipMaps);
tex.setWrap(Texture.WrapAxis.S, Texture.WrapMode.Repeat);
tex.setWrap(Texture.WrapAxis.T, Texture.WrapMode.MirroredRepeat);
mat.setTexture("DiffuseMap", tex);
mat.getAdditionalRenderState().setDepthWrite(false);
mat.getAdditionalRenderState().setDepthTest(false);
mat.getAdditionalRenderState().setBlendMode(RenderState.BlendMode.Alpha);
final ByteArrayOutputStream stream = new ByteArrayOutputStream();
J3MExporter exporter = new J3MExporter();
try {
exporter.save(mat, stream);
} catch (IOException e) {
e.printStackTrace();
}
System.err.println(stream.toString());
J3MLoader loader = new J3MLoader();
AssetInfo info = new AssetInfo(assetManager, new AssetKey("test")) {
@Override
public InputStream openStream() {
return new ByteArrayInputStream(stream.toByteArray());
}
};
Material mat2 = (Material)loader.load(info);
assertTrue(mat.contentEquals(mat2));
}
}

@ -40,6 +40,8 @@ import com.jme3.renderer.ViewPort;
import com.jme3.scene.Spatial; import com.jme3.scene.Spatial;
import com.jme3.scene.control.AbstractControl; import com.jme3.scene.control.AbstractControl;
import com.jme3.scene.control.Control; import com.jme3.scene.control.Control;
import com.jme3.util.clone.Cloner;
import com.jme3.util.clone.JmeCloneable;
import java.io.IOException; import java.io.IOException;
@ -67,6 +69,14 @@ public class NormalRecalcControl extends AbstractControl {
} }
@Override
public Object jmeClone() {
NormalRecalcControl control = (NormalRecalcControl)super.jmeClone();
control.setEnabled(true);
return control;
}
@Override
public Control cloneForSpatial(Spatial spatial) { public Control cloneForSpatial(Spatial spatial) {
NormalRecalcControl control = new NormalRecalcControl(terrain); NormalRecalcControl control = new NormalRecalcControl(terrain);
control.setSpatial(spatial); control.setSpatial(spatial);

@ -46,6 +46,8 @@ import com.jme3.scene.control.Control;
import com.jme3.terrain.Terrain; import com.jme3.terrain.Terrain;
import com.jme3.terrain.geomipmap.lodcalc.DistanceLodCalculator; import com.jme3.terrain.geomipmap.lodcalc.DistanceLodCalculator;
import com.jme3.terrain.geomipmap.lodcalc.LodCalculator; import com.jme3.terrain.geomipmap.lodcalc.LodCalculator;
import com.jme3.util.clone.Cloner;
import com.jme3.util.clone.JmeCloneable;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
@ -299,8 +301,31 @@ public class TerrainLodControl extends AbstractControl {
@Override
public Object jmeClone() {
if (spatial instanceof Terrain) {
TerrainLodControl cloned = new TerrainLodControl((Terrain) spatial, cameras);
cloned.setLodCalculator(lodCalculator.clone());
cloned.spatial = spatial;
return cloned;
}
return null;
}
@Override
public void cloneFields( Cloner cloner, Object original ) {
this.lodCalculator = cloner.clone(lodCalculator);
try {
// Not deep clone of the cameras themselves
this.cameras = cloner.javaClone(cameras);
} catch( CloneNotSupportedException e ) {
throw new RuntimeException("Error cloning", e);
}
}
@Override
public Control cloneForSpatial(Spatial spatial) { public Control cloneForSpatial(Spatial spatial) {
if (spatial instanceof Terrain) { if (spatial instanceof Terrain) {
List<Camera> cameraClone = new ArrayList<Camera>(); List<Camera> cameraClone = new ArrayList<Camera>();

@ -1,11 +1,11 @@
Material Signpost : Common/MatDefs/Light/Lighting.j3md { Material Signpost : Common/MatDefs/Light/Lighting.j3md {
MaterialParameters { MaterialParameters {
Shininess: 4.0 Shininess: 4.0
DiffuseMap: Models/Sign Post/Sign Post.jpg DiffuseMap: "Models/Sign Post/Sign Post.jpg"
NormalMap: Models/Sign Post/Sign Post_normal.jpg NormalMap: "Models/Sign Post/Sign Post_normal.jpg"
SpecularMap: Models/Sign Post/Sign Post_specular.jpg SpecularMap: "Models/Sign Post/Sign Post_specular.jpg"
UseMaterialColors : true UseMaterialColors : true
Ambient : 0.5 0.5 0.5 1.0 Ambient : 1.0 1.0 1.0 1.0
Diffuse : 1.0 1.0 1.0 1.0 Diffuse : 1.0 1.0 1.0 1.0
Specular : 1.0 1.0 1.0 1.0 Specular : 1.0 1.0 1.0 1.0
} }

@ -1 +0,0 @@
X-Comment: Created with jMonkeyPlatform

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save