Merge branch 'master' into experimental
This commit is contained in:
commit
a8fca2bcf6
138
.gitignore
vendored
138
.gitignore
vendored
@ -1,21 +1,34 @@
|
|||||||
*.dll
|
|
||||||
*.so
|
|
||||||
*.jnilib
|
|
||||||
*.dylib
|
|
||||||
*.iml
|
|
||||||
**/nbproject/private/
|
|
||||||
/.gradle/
|
/.gradle/
|
||||||
/.nb-gradle/
|
/.nb-gradle/private/
|
||||||
|
/.nb-gradle/profiles/private/
|
||||||
/.idea/
|
/.idea/
|
||||||
/dist/
|
/dist/
|
||||||
/build/
|
/build/
|
||||||
/bin/
|
|
||||||
/netbeans/
|
/netbeans/
|
||||||
/.classpath
|
/sdk/jdks/local/
|
||||||
/.project
|
/jme3-core/build/
|
||||||
/.settings
|
|
||||||
/jme3-core/src/main/resources/com/jme3/system/version.properties
|
/jme3-core/src/main/resources/com/jme3/system/version.properties
|
||||||
/jme3-*/build/
|
/jme3-plugins/build/
|
||||||
|
/jme3-desktop/build/
|
||||||
|
/jme3-android-native/build/
|
||||||
|
/jme3-android/build/
|
||||||
|
/jme3-android-examples/build/
|
||||||
|
/jme3-blender/build/
|
||||||
|
/jme3-effects/build/
|
||||||
|
/jme3-bullet/build/
|
||||||
|
/jme3-terrain/build/
|
||||||
|
/jme3-bullet-native/build/
|
||||||
|
/jme3-bullet-native-android/build/
|
||||||
|
/jme3-jogg/build/
|
||||||
|
/jme3-jbullet/build/
|
||||||
|
/jme3-lwjgl/build/
|
||||||
|
/jme3-networking/build/
|
||||||
|
/jme3-niftygui/build/
|
||||||
|
/jme3-testdata/build/
|
||||||
|
/jme3-examples/build/
|
||||||
|
/jme3-jogl/build/
|
||||||
|
/jme3-ios/build/
|
||||||
|
/jme3-gl-autogen/build/
|
||||||
/jme3-bullet-native/bullet.zip
|
/jme3-bullet-native/bullet.zip
|
||||||
/jme3-bullet-native/bullet-2.82-r2704/
|
/jme3-bullet-native/bullet-2.82-r2704/
|
||||||
/jme3-android-native/openal-soft/
|
/jme3-android-native/openal-soft/
|
||||||
@ -25,24 +38,113 @@
|
|||||||
/jme3-android-native/src/native/jme_decode/com_jme3_audio_plugins_NativeVorbisFile.h
|
/jme3-android-native/src/native/jme_decode/com_jme3_audio_plugins_NativeVorbisFile.h
|
||||||
/jme3-android-native/src/native/jme_decode/com_jme3_texture_plugins_AndroidNativeImageLoader.h
|
/jme3-android-native/src/native/jme_decode/com_jme3_texture_plugins_AndroidNativeImageLoader.h
|
||||||
/jme3-android-native/stb_image.h
|
/jme3-android-native/stb_image.h
|
||||||
/sdk/dist/
|
|
||||||
/sdk/jdks/local/
|
|
||||||
/sdk/build/
|
|
||||||
/sdk/jme3-tests-template/src/com/jme3/gde/templates/tests/JmeTestsProject.zip
|
/sdk/jme3-tests-template/src/com/jme3/gde/templates/tests/JmeTestsProject.zip
|
||||||
/sdk/jme3-tests-template/src/com/jme3/gde/templates/tests/JME3TestsAndroidProject.zip
|
/sdk/jme3-tests-template/src/com/jme3/gde/templates/tests/JME3TestsAndroidProject.zip
|
||||||
/sdk/jme3-project-testdata/release/
|
/sdk/jme3-project-testdata/release/
|
||||||
/sdk/JME3TestsTemplateAndroid/src/jme3test/
|
/sdk/JME3TestsTemplateAndroid/src/jme3test/
|
||||||
/sdk/JME3TestsTemplate/src/jme3test/
|
/sdk/JME3TestsTemplate/src/jme3test/
|
||||||
|
/sdk/build/
|
||||||
/sdk/jme3-core-baselibs/release/
|
/sdk/jme3-core-baselibs/release/
|
||||||
/sdk/jme3-core-libraries/release/
|
/sdk/jme3-core-libraries/release/
|
||||||
/sdk/jme3-project-baselibs/release/
|
/sdk/jme3-project-baselibs/release/
|
||||||
/sdk/jme3-project-libraries/release/
|
/sdk/jme3-project-libraries/release/
|
||||||
/sdk/jme3-*/build/
|
/sdk/jme3-codepalette/build/
|
||||||
/sdk/nbi/stub/ext/components/products/jdk/build/
|
/sdk/jme3-core-libraries/build/
|
||||||
/sdk/nbi/stub/ext/components/products/jdk/dist/
|
/sdk/jme3-code-check/build/
|
||||||
|
/sdk/jme3-core-baselibs/build/
|
||||||
|
/sdk/jme3-documentation/build/
|
||||||
|
/sdk/jme3-core-updatecenters/build/
|
||||||
|
/sdk/jme3-project-testdata/build/
|
||||||
|
/sdk/jme3-project-libraries/build/
|
||||||
|
/sdk/jme3-project-baselibs/build/
|
||||||
|
/sdk/jme3-templates/build/
|
||||||
|
/sdk/jme3-texture-editor/build/
|
||||||
|
/sdk/jme3-tests-template/build/
|
||||||
|
/sdk/jme3-upgrader/build/
|
||||||
|
/sdk/jme3-core/build/
|
||||||
|
/sdk/jme3-obfuscate/build/
|
||||||
|
/sdk/jme3-gui/build/
|
||||||
|
/sdk/jme3-cinematics/build/
|
||||||
|
/sdk/jme3-terrain-editor/build/
|
||||||
|
/sdk/jme3-lwjgl-applet/build/
|
||||||
|
/sdk/jme3-blender/build/
|
||||||
|
/sdk/jme3-navmesh-gen/build/
|
||||||
|
/sdk/jme3-angelfont/build/
|
||||||
|
/sdk/jme3-materialeditor/build/
|
||||||
|
/sdk/jme3-android/build/
|
||||||
|
/sdk/jme3-desktop-executables/build/
|
||||||
|
/sdk/jme3-ogrexml/build/
|
||||||
|
/sdk/jme3-ogretools/build/
|
||||||
|
/sdk/jme3-scenecomposer/build/
|
||||||
|
/sdk/jme3-assetpack-support/build/
|
||||||
|
/sdk/jme3-model-importer/build/
|
||||||
|
/sdk/jme3-wavefront/build/
|
||||||
|
/sdk/jme3-vehicle-creator/build/
|
||||||
|
/sdk/jme3-welcome-screen/build/
|
||||||
|
/sdk/jme3-glsl-support/build/
|
||||||
|
/sdk/jme3-dark-laf/build/
|
||||||
|
/sdk/nbproject/private/
|
||||||
|
/sdk/jme3-scenecomposer/nbproject/private/
|
||||||
|
/sdk/jme3-core/nbproject/private/
|
||||||
|
/sdk/jme3-core-baselibs/nbproject/private/
|
||||||
|
/sdk/jme3-welcome-screen/nbproject/private/
|
||||||
|
/sdk/jme3-lwjgl-applet/nbproject/private/
|
||||||
|
/sdk/jme3-ogrexml/nbproject/private/
|
||||||
|
/sdk/jme3-upgrader/nbproject/private/
|
||||||
|
/sdk/jme3-obfuscate/nbproject/private/
|
||||||
|
/sdk/jme3-navmesh-gen/nbproject/private/
|
||||||
|
/sdk/jme3-wavefront/nbproject/private/
|
||||||
|
/sdk/jme3-project-libraries/nbproject/private/
|
||||||
|
/sdk/jme3-ogretools/nbproject/private/
|
||||||
|
/sdk/jme3-assetpack-support/nbproject/private/
|
||||||
|
/sdk/jme3-cinematics/nbproject/private/
|
||||||
|
/sdk/jme3-model-importer/nbproject/private/
|
||||||
|
/sdk/jme3-desktop-executables/nbproject/private/
|
||||||
|
/sdk/jme3-glsl-support/nbproject/private/
|
||||||
|
/sdk/jme3-android/nbproject/private/
|
||||||
|
/sdk/jme3-angelfont/nbproject/private/
|
||||||
|
/sdk/jme3-codepalette/nbproject/private/
|
||||||
|
/sdk/jme3-documentation/nbproject/private/
|
||||||
|
/sdk/jme3-vehicle-creator/nbproject/private/
|
||||||
|
/sdk/jme3-code-check/nbproject/private/
|
||||||
|
/sdk/jme3-blender/nbproject/private/
|
||||||
|
/sdk/jme3-core-libraries/nbproject/private/
|
||||||
|
/sdk/jme3-core-updatecenters/nbproject/private/
|
||||||
|
/sdk/jme3-gui/nbproject/private/
|
||||||
|
/sdk/jme3-materialeditor/nbproject/private/
|
||||||
|
/sdk/jme3-project-baselibs/nbproject/private/
|
||||||
|
/sdk/jme3-project-testdata/nbproject/private/
|
||||||
|
/sdk/jme3-templates/nbproject/private/
|
||||||
|
/sdk/jme3-terrain-editor/nbproject/private/
|
||||||
|
/sdk/jme3-tests-template/nbproject/private/
|
||||||
|
/sdk/jme3-texture-editor/nbproject/private/
|
||||||
|
/sdk/JME3TestsTemplate/nbproject/private/
|
||||||
|
/sdk/JME3TestsTemplateAndroid/nbproject/private/
|
||||||
|
/bin
|
||||||
|
/.classpath
|
||||||
|
/.project
|
||||||
|
/.settings
|
||||||
|
*.dll
|
||||||
|
*.so
|
||||||
|
*.jnilib
|
||||||
|
*.dylib
|
||||||
|
*.iml
|
||||||
|
.DS_Store
|
||||||
|
/sdk/dist/
|
||||||
!/jme3-bullet-native/libs/native/windows/x86_64/bulletjme.dll
|
!/jme3-bullet-native/libs/native/windows/x86_64/bulletjme.dll
|
||||||
!/jme3-bullet-native/libs/native/windows/x86/bulletjme.dll
|
!/jme3-bullet-native/libs/native/windows/x86/bulletjme.dll
|
||||||
!/jme3-bullet-native/libs/native/osx/x86/libbulletjme.dylib
|
!/jme3-bullet-native/libs/native/osx/x86/libbulletjme.dylib
|
||||||
!/jme3-bullet-native/libs/native/osx/x86_64/libbulletjme.dylib
|
!/jme3-bullet-native/libs/native/osx/x86_64/libbulletjme.dylib
|
||||||
!/jme3-bullet-native/libs/native/linux/x86/libbulletjme.so
|
!/jme3-bullet-native/libs/native/linux/x86/libbulletjme.so
|
||||||
!/jme3-bullet-native/libs/native/linux/x86_64/libbulletjme.so
|
!/jme3-bullet-native/libs/native/linux/x86_64/libbulletjme.so
|
||||||
|
/.nb-gradle/
|
||||||
|
/sdk/ant-jme/nbproject/private/
|
||||||
|
/sdk/nbi/stub/ext/engine/nbproject/private/
|
||||||
|
/sdk/nbi/stub/ext/components/products/jdk/nbproject/private/
|
||||||
|
/sdk/nbi/stub/ext/components/products/blender/nbproject/private/
|
||||||
|
/sdk/nbi/stub/ext/components/products/helloworld/nbproject/private/
|
||||||
|
/sdk/BasicGameTemplate/nbproject/private/
|
||||||
|
/sdk/nbi/stub/ext/components/products/jdk/build/
|
||||||
|
/sdk/nbi/stub/ext/components/products/jdk/dist/
|
||||||
|
/sdk/jme3-dark-laf/nbproject/private/
|
||||||
|
jme3-lwjgl3/build/
|
||||||
|
@ -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 {
|
||||||
|
@ -50,7 +50,7 @@ public class AndroidHarness extends Activity implements TouchListener, DialogInt
|
|||||||
/**
|
/**
|
||||||
* The jme3 application object
|
* The jme3 application object
|
||||||
*/
|
*/
|
||||||
protected Application app = null;
|
protected LegacyApplication app = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the desired RGB size for the surfaceview. 16 = RGB565, 24 = RGB888.
|
* Sets the desired RGB size for the surfaceview. 16 = RGB565, 24 = RGB888.
|
||||||
@ -178,7 +178,7 @@ public class AndroidHarness extends Activity implements TouchListener, DialogInt
|
|||||||
private boolean inConfigChange = false;
|
private boolean inConfigChange = false;
|
||||||
|
|
||||||
private class DataObject {
|
private class DataObject {
|
||||||
protected Application app = null;
|
protected LegacyApplication app = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -241,7 +241,7 @@ public class AndroidHarness extends Activity implements TouchListener, DialogInt
|
|||||||
try {
|
try {
|
||||||
if (app == null) {
|
if (app == null) {
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
Class<? extends Application> clazz = (Class<? extends Application>) Class.forName(appClass);
|
Class<? extends LegacyApplication> clazz = (Class<? extends LegacyApplication>) Class.forName(appClass);
|
||||||
app = clazz.newInstance();
|
app = clazz.newInstance();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -207,7 +207,7 @@ public class AndroidHarnessFragment extends Fragment implements
|
|||||||
protected ImageView splashImageView = null;
|
protected ImageView splashImageView = null;
|
||||||
final private String ESCAPE_EVENT = "TouchEscape";
|
final private String ESCAPE_EVENT = "TouchEscape";
|
||||||
private boolean firstDrawFrame = true;
|
private boolean firstDrawFrame = true;
|
||||||
private Application app = null;
|
private LegacyApplication app = null;
|
||||||
private int viewWidth = 0;
|
private int viewWidth = 0;
|
||||||
private int viewHeight = 0;
|
private int viewHeight = 0;
|
||||||
|
|
||||||
@ -258,7 +258,7 @@ public class AndroidHarnessFragment extends Fragment implements
|
|||||||
try {
|
try {
|
||||||
if (app == null) {
|
if (app == null) {
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
Class<? extends Application> clazz = (Class<? extends Application>) Class.forName(appClass);
|
Class<? extends LegacyApplication> clazz = (Class<? extends LegacyApplication>) Class.forName(appClass);
|
||||||
app = clazz.newInstance();
|
app = clazz.newInstance();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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) {
|
||||||
|
@ -215,8 +215,8 @@ public class Edge {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* The method computes the crossing pint of this edge and another edge. If
|
* The method computes the crossing pint of this edge and another edge. If
|
||||||
* there is no crossing then null is returned. This method also allows to
|
* there is no crossing then null is returned. Also null is returned if the edges are parallel.
|
||||||
* get the crossing point of the straight lines that contain these edges if
|
* This method also allows to get the crossing point of the straight lines that contain these edges if
|
||||||
* you set the 'extend' parameter to true.
|
* you set the 'extend' parameter to true.
|
||||||
*
|
*
|
||||||
* @param edge
|
* @param edge
|
||||||
@ -227,7 +227,7 @@ public class Edge {
|
|||||||
* @param extendSecondEdge
|
* @param extendSecondEdge
|
||||||
* set to <b>true</b> to find a crossing point along the whole
|
* set to <b>true</b> to find a crossing point along the whole
|
||||||
* straight that contains the given edge
|
* straight that contains the given edge
|
||||||
* @return cross point on null if none exist
|
* @return cross point on null if none exist or the edges are parallel
|
||||||
*/
|
*/
|
||||||
public Vector3f getCrossPoint(Edge edge, boolean extendThisEdge, boolean extendSecondEdge) {
|
public Vector3f getCrossPoint(Edge edge, boolean extendThisEdge, boolean extendSecondEdge) {
|
||||||
Vector3d P1 = new Vector3d(this.getFirstVertex());
|
Vector3d P1 = new Vector3d(this.getFirstVertex());
|
||||||
@ -235,6 +235,11 @@ public class Edge {
|
|||||||
Vector3d u = new Vector3d(this.getSecondVertex()).subtract(P1).normalizeLocal();
|
Vector3d u = new Vector3d(this.getSecondVertex()).subtract(P1).normalizeLocal();
|
||||||
Vector3d v = new Vector3d(edge.getSecondVertex()).subtract(P2).normalizeLocal();
|
Vector3d v = new Vector3d(edge.getSecondVertex()).subtract(P2).normalizeLocal();
|
||||||
|
|
||||||
|
if(Math.abs(u.dot(v)) >= 1 - FastMath.DBL_EPSILON) {
|
||||||
|
// the edges are parallel; do not care about the crossing point
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
double t1 = 0, t2 = 0;
|
double t1 = 0, t2 = 0;
|
||||||
if(u.x == 0 && v.x == 0) {
|
if(u.x == 0 && v.x == 0) {
|
||||||
t2 = (u.z * (P2.y - P1.y) - u.y * (P2.z - P1.z)) / (u.y * v.z - u.z * v.y);
|
t2 = (u.z * (P2.y - P1.y) - u.y * (P2.z - P1.z)) / (u.y * v.z - u.z * v.y);
|
||||||
@ -262,11 +267,11 @@ public class Edge {
|
|||||||
// the lines cross, check if p1 and p2 are within the edges
|
// the lines cross, check if p1 and p2 are within the edges
|
||||||
Vector3d p = p1.subtract(P1);
|
Vector3d p = p1.subtract(P1);
|
||||||
double cos = p.dot(u) / p.length();
|
double cos = p.dot(u) / p.length();
|
||||||
if (extendThisEdge || p.length()<= FastMath.FLT_EPSILON || cos >= 1 - FastMath.FLT_EPSILON && p.length() <= this.getLength()) {
|
if (extendThisEdge || p.length()<= FastMath.FLT_EPSILON || cos >= 1 - FastMath.FLT_EPSILON && p.length() - this.getLength() <= FastMath.FLT_EPSILON) {
|
||||||
// p1 is inside the first edge, lets check the other edge now
|
// p1 is inside the first edge, lets check the other edge now
|
||||||
p = p2.subtract(P2);
|
p = p2.subtract(P2);
|
||||||
cos = p.dot(v) / p.length();
|
cos = p.dot(v) / p.length();
|
||||||
if(extendSecondEdge || p.length()<= FastMath.FLT_EPSILON || cos >= 1 - FastMath.FLT_EPSILON && p.length() <= edge.getLength()) {
|
if(extendSecondEdge || p.length()<= FastMath.FLT_EPSILON || cos >= 1 - FastMath.FLT_EPSILON && p.length() - edge.getLength() <= FastMath.FLT_EPSILON) {
|
||||||
return p1.toVector3f();
|
return p1.toVector3f();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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());
|
||||||
@ -279,16 +278,6 @@ public class Face implements Comparator<Integer> {
|
|||||||
// two special cases will improve the computations speed
|
// two special cases will improve the computations speed
|
||||||
if(face.getIndexes().size() == 3) {
|
if(face.getIndexes().size() == 3) {
|
||||||
triangulatedFaces.add(face.getIndexes().clone());
|
triangulatedFaces.add(face.getIndexes().clone());
|
||||||
} else if(face.getIndexes().size() == 4) {
|
|
||||||
// in case face has 4 verts we use the plain triangulation
|
|
||||||
indexes[0] = face.getIndex(0);
|
|
||||||
indexes[1] = face.getIndex(1);
|
|
||||||
indexes[2] = face.getIndex(2);
|
|
||||||
triangulatedFaces.add(new IndexesLoop(indexes));
|
|
||||||
|
|
||||||
indexes[1] = face.getIndex(2);
|
|
||||||
indexes[2] = face.getIndex(3);
|
|
||||||
triangulatedFaces.add(new IndexesLoop(indexes));
|
|
||||||
} else {
|
} else {
|
||||||
int previousIndex1 = -1, previousIndex2 = -1, previousIndex3 = -1;
|
int previousIndex1 = -1, previousIndex2 = -1, previousIndex3 = -1;
|
||||||
while (face.vertexCount() > 0) {
|
while (face.vertexCount() > 0) {
|
||||||
|
@ -286,30 +286,33 @@ 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();
|
||||||
Structure defbase = (Structure) parent.getFieldValue("defbase");
|
if(parent != null) {
|
||||||
List<String> groupNames = new ArrayList<String>();
|
// the mesh might be saved without its parent (it is then unused)
|
||||||
List<Structure> defs = defbase.evaluateListBase();
|
Structure defbase = (Structure) parent.getFieldValue("defbase");
|
||||||
for (Structure def : defs) {
|
List<String> groupNames = new ArrayList<String>();
|
||||||
groupNames.add(def.getFieldValue("name").toString());
|
List<Structure> defs = defbase.evaluateListBase();
|
||||||
}
|
for (Structure def : defs) {
|
||||||
|
groupNames.add(def.getFieldValue("name").toString());
|
||||||
|
}
|
||||||
|
|
||||||
Pointer pDvert = (Pointer) meshStructure.getFieldValue("dvert");// dvert = DeformVERTices
|
Pointer pDvert = (Pointer) meshStructure.getFieldValue("dvert");// dvert = DeformVERTices
|
||||||
if (pDvert.isNotNull()) {// assigning weights and bone indices
|
if (pDvert.isNotNull()) {// assigning weights and bone indices
|
||||||
List<Structure> dverts = pDvert.fetchData();
|
List<Structure> dverts = pDvert.fetchData();
|
||||||
for (Structure dvert : dverts) {
|
for (Structure dvert : dverts) {
|
||||||
Map<String, Float> weightsForVertex = new HashMap<String, Float>();
|
Map<String, Float> weightsForVertex = new HashMap<String, Float>();
|
||||||
Pointer pDW = (Pointer) dvert.getFieldValue("dw");
|
Pointer pDW = (Pointer) dvert.getFieldValue("dw");
|
||||||
if (pDW.isNotNull()) {
|
if (pDW.isNotNull()) {
|
||||||
List<Structure> dw = pDW.fetchData();
|
List<Structure> dw = pDW.fetchData();
|
||||||
for (Structure deformWeight : dw) {
|
for (Structure deformWeight : dw) {
|
||||||
int groupIndex = ((Number) deformWeight.getFieldValue("def_nr")).intValue();
|
int groupIndex = ((Number) deformWeight.getFieldValue("def_nr")).intValue();
|
||||||
float weight = ((Number) deformWeight.getFieldValue("weight")).floatValue();
|
float weight = ((Number) deformWeight.getFieldValue("weight")).floatValue();
|
||||||
String groupName = groupNames.get(groupIndex);
|
String groupName = groupNames.get(groupIndex);
|
||||||
|
|
||||||
weightsForVertex.put(groupName, weight);
|
weightsForVertex.put(groupName, weight);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
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
|
||||||
*
|
*
|
||||||
|
@ -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,17 @@ 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
|
@Override
|
||||||
protected void controlRender(RenderManager rm, ViewPort vp) {
|
protected void controlRender(RenderManager rm, ViewPort vp) {
|
||||||
}
|
}
|
||||||
@ -263,6 +276,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 +297,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.
|
||||||
@ -348,6 +350,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();
|
||||||
@ -388,6 +391,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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -33,85 +33,30 @@ package com.jme3.app;
|
|||||||
|
|
||||||
import com.jme3.app.state.AppStateManager;
|
import com.jme3.app.state.AppStateManager;
|
||||||
import com.jme3.asset.AssetManager;
|
import com.jme3.asset.AssetManager;
|
||||||
import com.jme3.audio.AudioContext;
|
|
||||||
import com.jme3.audio.AudioRenderer;
|
import com.jme3.audio.AudioRenderer;
|
||||||
import com.jme3.audio.Listener;
|
import com.jme3.audio.Listener;
|
||||||
import com.jme3.input.*;
|
import com.jme3.input.InputManager;
|
||||||
import com.jme3.math.Vector3f;
|
|
||||||
import com.jme3.profile.AppProfiler;
|
import com.jme3.profile.AppProfiler;
|
||||||
import com.jme3.profile.AppStep;
|
|
||||||
import com.jme3.renderer.Camera;
|
import com.jme3.renderer.Camera;
|
||||||
import com.jme3.renderer.RenderManager;
|
import com.jme3.renderer.RenderManager;
|
||||||
import com.jme3.renderer.Renderer;
|
import com.jme3.renderer.Renderer;
|
||||||
import com.jme3.renderer.ViewPort;
|
import com.jme3.renderer.ViewPort;
|
||||||
import com.jme3.system.*;
|
import com.jme3.system.*;
|
||||||
import com.jme3.system.JmeContext.Type;
|
|
||||||
import java.net.MalformedURLException;
|
|
||||||
import java.net.URL;
|
|
||||||
import java.util.concurrent.Callable;
|
import java.util.concurrent.Callable;
|
||||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
|
||||||
import java.util.concurrent.Future;
|
import java.util.concurrent.Future;
|
||||||
import java.util.logging.Level;
|
|
||||||
import java.util.logging.Logger;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The <code>Application</code> class represents an instance of a
|
* The <code>Application</code> interface represents the minimum exposed
|
||||||
* real-time 3D rendering jME application.
|
* capabilities of a concrete jME3 application.
|
||||||
*
|
|
||||||
* An <code>Application</code> provides all the tools that are commonly used in jME3
|
|
||||||
* applications.
|
|
||||||
*
|
|
||||||
* jME3 applications *SHOULD NOT EXTEND* this class but extend {@link com.jme3.app.SimpleApplication} instead.
|
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
public class Application implements SystemListener {
|
public interface Application {
|
||||||
|
|
||||||
private static final Logger logger = Logger.getLogger(Application.class.getName());
|
|
||||||
|
|
||||||
protected AssetManager assetManager;
|
|
||||||
|
|
||||||
protected AudioRenderer audioRenderer;
|
|
||||||
protected Renderer renderer;
|
|
||||||
protected RenderManager renderManager;
|
|
||||||
protected ViewPort viewPort;
|
|
||||||
protected ViewPort guiViewPort;
|
|
||||||
|
|
||||||
protected JmeContext context;
|
|
||||||
protected AppSettings settings;
|
|
||||||
protected Timer timer = new NanoTimer();
|
|
||||||
protected Camera cam;
|
|
||||||
protected Listener listener;
|
|
||||||
|
|
||||||
protected boolean inputEnabled = true;
|
|
||||||
protected LostFocusBehavior lostFocusBehavior = LostFocusBehavior.ThrottleOnLostFocus;
|
|
||||||
protected float speed = 1f;
|
|
||||||
protected boolean paused = false;
|
|
||||||
protected MouseInput mouseInput;
|
|
||||||
protected KeyInput keyInput;
|
|
||||||
protected JoyInput joyInput;
|
|
||||||
protected TouchInput touchInput;
|
|
||||||
protected InputManager inputManager;
|
|
||||||
protected AppStateManager stateManager;
|
|
||||||
|
|
||||||
protected AppProfiler prof;
|
|
||||||
|
|
||||||
private final ConcurrentLinkedQueue<AppTask<?>> taskQueue = new ConcurrentLinkedQueue<AppTask<?>>();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new instance of <code>Application</code>.
|
|
||||||
*/
|
|
||||||
public Application(){
|
|
||||||
initStateManager();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determine the application's behavior when unfocused.
|
* Determine the application's behavior when unfocused.
|
||||||
*
|
*
|
||||||
* @return The lost focus behavior of the application.
|
* @return The lost focus behavior of the application.
|
||||||
*/
|
*/
|
||||||
public LostFocusBehavior getLostFocusBehavior() {
|
public LostFocusBehavior getLostFocusBehavior();
|
||||||
return lostFocusBehavior;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Change the application's behavior when unfocused.
|
* Change the application's behavior when unfocused.
|
||||||
@ -125,9 +70,7 @@ public class Application implements SystemListener {
|
|||||||
*
|
*
|
||||||
* @see LostFocusBehavior
|
* @see LostFocusBehavior
|
||||||
*/
|
*/
|
||||||
public void setLostFocusBehavior(LostFocusBehavior lostFocusBehavior) {
|
public void setLostFocusBehavior(LostFocusBehavior lostFocusBehavior);
|
||||||
this.lostFocusBehavior = lostFocusBehavior;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if pause on lost focus is enabled, false otherwise.
|
* Returns true if pause on lost focus is enabled, false otherwise.
|
||||||
@ -136,9 +79,7 @@ public class Application implements SystemListener {
|
|||||||
*
|
*
|
||||||
* @see #getLostFocusBehavior()
|
* @see #getLostFocusBehavior()
|
||||||
*/
|
*/
|
||||||
public boolean isPauseOnLostFocus() {
|
public boolean isPauseOnLostFocus();
|
||||||
return getLostFocusBehavior() == LostFocusBehavior.PauseOnLostFocus;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enable or disable pause on lost focus.
|
* Enable or disable pause on lost focus.
|
||||||
@ -156,49 +97,7 @@ public class Application implements SystemListener {
|
|||||||
*
|
*
|
||||||
* @see #setLostFocusBehavior(com.jme3.app.LostFocusBehavior)
|
* @see #setLostFocusBehavior(com.jme3.app.LostFocusBehavior)
|
||||||
*/
|
*/
|
||||||
public void setPauseOnLostFocus(boolean pauseOnLostFocus) {
|
public void setPauseOnLostFocus(boolean pauseOnLostFocus);
|
||||||
if (pauseOnLostFocus) {
|
|
||||||
setLostFocusBehavior(LostFocusBehavior.PauseOnLostFocus);
|
|
||||||
} else {
|
|
||||||
setLostFocusBehavior(LostFocusBehavior.Disabled);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Deprecated
|
|
||||||
public void setAssetManager(AssetManager assetManager){
|
|
||||||
if (this.assetManager != null)
|
|
||||||
throw new IllegalStateException("Can only set asset manager"
|
|
||||||
+ " before initialization.");
|
|
||||||
|
|
||||||
this.assetManager = assetManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initAssetManager(){
|
|
||||||
URL assetCfgUrl = null;
|
|
||||||
|
|
||||||
if (settings != null){
|
|
||||||
String assetCfg = settings.getString("AssetConfigURL");
|
|
||||||
if (assetCfg != null){
|
|
||||||
try {
|
|
||||||
assetCfgUrl = new URL(assetCfg);
|
|
||||||
} catch (MalformedURLException ex) {
|
|
||||||
}
|
|
||||||
if (assetCfgUrl == null) {
|
|
||||||
assetCfgUrl = Application.class.getClassLoader().getResource(assetCfg);
|
|
||||||
if (assetCfgUrl == null) {
|
|
||||||
logger.log(Level.SEVERE, "Unable to access AssetConfigURL in asset config:{0}", assetCfg);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (assetCfgUrl == null) {
|
|
||||||
assetCfgUrl = JmeSystem.getPlatformAssetConfigURL();
|
|
||||||
}
|
|
||||||
if (assetManager == null){
|
|
||||||
assetManager = JmeSystem.newAssetManager(assetCfgUrl);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the display settings to define the display created.
|
* Set the display settings to define the display created.
|
||||||
@ -210,332 +109,83 @@ public class Application implements SystemListener {
|
|||||||
*
|
*
|
||||||
* @param settings The settings to set.
|
* @param settings The settings to set.
|
||||||
*/
|
*/
|
||||||
public void setSettings(AppSettings settings){
|
public void setSettings(AppSettings settings);
|
||||||
this.settings = settings;
|
|
||||||
if (context != null && settings.useInput() != inputEnabled){
|
|
||||||
// may need to create or destroy input based
|
|
||||||
// on settings change
|
|
||||||
inputEnabled = !inputEnabled;
|
|
||||||
if (inputEnabled){
|
|
||||||
initInput();
|
|
||||||
}else{
|
|
||||||
destroyInput();
|
|
||||||
}
|
|
||||||
}else{
|
|
||||||
inputEnabled = settings.useInput();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the Timer implementation that will be used for calculating
|
* Sets the Timer implementation that will be used for calculating
|
||||||
* frame times. By default, Application will use the Timer as returned
|
* frame times. By default, Application will use the Timer as returned
|
||||||
* by the current JmeContext implementation.
|
* by the current JmeContext implementation.
|
||||||
*/
|
*/
|
||||||
public void setTimer(Timer timer){
|
public void setTimer(Timer timer);
|
||||||
this.timer = timer;
|
|
||||||
|
|
||||||
if (timer != null) {
|
public Timer getTimer();
|
||||||
timer.reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (renderManager != null) {
|
|
||||||
renderManager.setTimer(timer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Timer getTimer(){
|
|
||||||
return timer;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initDisplay(){
|
|
||||||
// aquire important objects
|
|
||||||
// from the context
|
|
||||||
settings = context.getSettings();
|
|
||||||
|
|
||||||
// Only reset the timer if a user has not already provided one
|
|
||||||
if (timer == null) {
|
|
||||||
timer = context.getTimer();
|
|
||||||
}
|
|
||||||
|
|
||||||
renderer = context.getRenderer();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initAudio(){
|
|
||||||
if (settings.getAudioRenderer() != null && context.getType() != Type.Headless){
|
|
||||||
audioRenderer = JmeSystem.newAudioRenderer(settings);
|
|
||||||
audioRenderer.initialize();
|
|
||||||
AudioContext.setAudioRenderer(audioRenderer);
|
|
||||||
|
|
||||||
listener = new Listener();
|
|
||||||
audioRenderer.setListener(listener);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates the camera to use for rendering. Default values are perspective
|
|
||||||
* projection with 45° field of view, with near and far values 1 and 1000
|
|
||||||
* units respectively.
|
|
||||||
*/
|
|
||||||
private void initCamera(){
|
|
||||||
cam = new Camera(settings.getWidth(), settings.getHeight());
|
|
||||||
|
|
||||||
cam.setFrustumPerspective(45f, (float)cam.getWidth() / cam.getHeight(), 1f, 1000f);
|
|
||||||
cam.setLocation(new Vector3f(0f, 0f, 10f));
|
|
||||||
cam.lookAt(new Vector3f(0f, 0f, 0f), Vector3f.UNIT_Y);
|
|
||||||
|
|
||||||
renderManager = new RenderManager(renderer);
|
|
||||||
//Remy - 09/14/2010 setted the timer in the renderManager
|
|
||||||
renderManager.setTimer(timer);
|
|
||||||
|
|
||||||
if (prof != null) {
|
|
||||||
renderManager.setAppProfiler(prof);
|
|
||||||
}
|
|
||||||
|
|
||||||
viewPort = renderManager.createMainView("Default", cam);
|
|
||||||
viewPort.setClearFlags(true, true, true);
|
|
||||||
|
|
||||||
// Create a new cam for the gui
|
|
||||||
Camera guiCam = new Camera(settings.getWidth(), settings.getHeight());
|
|
||||||
guiViewPort = renderManager.createPostView("Gui Default", guiCam);
|
|
||||||
guiViewPort.setClearFlags(false, false, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initializes mouse and keyboard input. Also
|
|
||||||
* initializes joystick input if joysticks are enabled in the
|
|
||||||
* AppSettings.
|
|
||||||
*/
|
|
||||||
private void initInput(){
|
|
||||||
mouseInput = context.getMouseInput();
|
|
||||||
if (mouseInput != null)
|
|
||||||
mouseInput.initialize();
|
|
||||||
|
|
||||||
keyInput = context.getKeyInput();
|
|
||||||
if (keyInput != null)
|
|
||||||
keyInput.initialize();
|
|
||||||
|
|
||||||
touchInput = context.getTouchInput();
|
|
||||||
if (touchInput != null)
|
|
||||||
touchInput.initialize();
|
|
||||||
|
|
||||||
if (!settings.getBoolean("DisableJoysticks")){
|
|
||||||
joyInput = context.getJoyInput();
|
|
||||||
if (joyInput != null)
|
|
||||||
joyInput.initialize();
|
|
||||||
}
|
|
||||||
|
|
||||||
inputManager = new InputManager(mouseInput, keyInput, joyInput, touchInput);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initStateManager(){
|
|
||||||
stateManager = new AppStateManager(this);
|
|
||||||
|
|
||||||
// Always register a ResetStatsState to make sure
|
|
||||||
// that the stats are cleared every frame
|
|
||||||
stateManager.attach(new ResetStatsState());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return The {@link AssetManager asset manager} for this application.
|
* @return The {@link AssetManager asset manager} for this application.
|
||||||
*/
|
*/
|
||||||
public AssetManager getAssetManager(){
|
public AssetManager getAssetManager();
|
||||||
return assetManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the {@link InputManager input manager}.
|
* @return the {@link InputManager input manager}.
|
||||||
*/
|
*/
|
||||||
public InputManager getInputManager(){
|
public InputManager getInputManager();
|
||||||
return inputManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the {@link AppStateManager app state manager}
|
* @return the {@link AppStateManager app state manager}
|
||||||
*/
|
*/
|
||||||
public AppStateManager getStateManager() {
|
public AppStateManager getStateManager();
|
||||||
return stateManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the {@link RenderManager render manager}
|
* @return the {@link RenderManager render manager}
|
||||||
*/
|
*/
|
||||||
public RenderManager getRenderManager() {
|
public RenderManager getRenderManager();
|
||||||
return renderManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return The {@link Renderer renderer} for the application
|
* @return The {@link Renderer renderer} for the application
|
||||||
*/
|
*/
|
||||||
public Renderer getRenderer(){
|
public Renderer getRenderer();
|
||||||
return renderer;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return The {@link AudioRenderer audio renderer} for the application
|
* @return The {@link AudioRenderer audio renderer} for the application
|
||||||
*/
|
*/
|
||||||
public AudioRenderer getAudioRenderer() {
|
public AudioRenderer getAudioRenderer();
|
||||||
return audioRenderer;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return The {@link Listener listener} object for audio
|
* @return The {@link Listener listener} object for audio
|
||||||
*/
|
*/
|
||||||
public Listener getListener() {
|
public Listener getListener();
|
||||||
return listener;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return The {@link JmeContext display context} for the application
|
* @return The {@link JmeContext display context} for the application
|
||||||
*/
|
*/
|
||||||
public JmeContext getContext(){
|
public JmeContext getContext();
|
||||||
return context;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return The {@link Camera camera} for the application
|
* @return The main {@link Camera camera} for the application
|
||||||
*/
|
*/
|
||||||
public Camera getCamera(){
|
public Camera getCamera();
|
||||||
return cam;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Starts the application in {@link Type#Display display} mode.
|
|
||||||
*
|
|
||||||
* @see #start(com.jme3.system.JmeContext.Type)
|
|
||||||
*/
|
|
||||||
public void start(){
|
|
||||||
start(JmeContext.Type.Display, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Starts the application in {@link Type#Display display} mode.
|
|
||||||
*
|
|
||||||
* @see #start(com.jme3.system.JmeContext.Type)
|
|
||||||
*/
|
|
||||||
public void start(boolean waitFor){
|
|
||||||
start(JmeContext.Type.Display, waitFor);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Starts the application.
|
* Starts the application.
|
||||||
* Creating a rendering context and executing
|
|
||||||
* the main loop in a separate thread.
|
|
||||||
*/
|
*/
|
||||||
public void start(JmeContext.Type contextType) {
|
public void start();
|
||||||
start(contextType, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Starts the application.
|
* Starts the application.
|
||||||
* Creating a rendering context and executing
|
|
||||||
* the main loop in a separate thread.
|
|
||||||
*/
|
*/
|
||||||
public void start(JmeContext.Type contextType, boolean waitFor){
|
public void start(boolean waitFor);
|
||||||
if (context != null && context.isCreated()){
|
|
||||||
logger.warning("start() called when application already created!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (settings == null){
|
|
||||||
settings = new AppSettings(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.log(Level.FINE, "Starting application: {0}", getClass().getName());
|
|
||||||
context = JmeSystem.newContext(settings, contextType);
|
|
||||||
context.setSystemListener(this);
|
|
||||||
context.create(waitFor);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets an AppProfiler hook that will be called back for
|
* Sets an AppProfiler hook that will be called back for
|
||||||
* specific steps within a single update frame. Value defaults
|
* specific steps within a single update frame. Value defaults
|
||||||
* to null.
|
* to null.
|
||||||
*/
|
*/
|
||||||
public void setAppProfiler(AppProfiler prof) {
|
public void setAppProfiler(AppProfiler prof);
|
||||||
this.prof = prof;
|
|
||||||
if (renderManager != null) {
|
|
||||||
renderManager.setAppProfiler(prof);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the current AppProfiler hook, or null if none is set.
|
* Returns the current AppProfiler hook, or null if none is set.
|
||||||
*/
|
*/
|
||||||
public AppProfiler getAppProfiler() {
|
public AppProfiler getAppProfiler();
|
||||||
return prof;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initializes the application's canvas for use.
|
|
||||||
* <p>
|
|
||||||
* After calling this method, cast the {@link #getContext() context} to
|
|
||||||
* {@link JmeCanvasContext},
|
|
||||||
* then acquire the canvas with {@link JmeCanvasContext#getCanvas() }
|
|
||||||
* and attach it to an AWT/Swing Frame.
|
|
||||||
* The rendering thread will start when the canvas becomes visible on
|
|
||||||
* screen, however if you wish to start the context immediately you
|
|
||||||
* may call {@link #startCanvas() } to force the rendering thread
|
|
||||||
* to start.
|
|
||||||
*
|
|
||||||
* @see JmeCanvasContext
|
|
||||||
* @see Type#Canvas
|
|
||||||
*/
|
|
||||||
public void createCanvas(){
|
|
||||||
if (context != null && context.isCreated()){
|
|
||||||
logger.warning("createCanvas() called when application already created!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (settings == null){
|
|
||||||
settings = new AppSettings(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.log(Level.FINE, "Starting application: {0}", getClass().getName());
|
|
||||||
context = JmeSystem.newContext(settings, JmeContext.Type.Canvas);
|
|
||||||
context.setSystemListener(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Starts the rendering thread after createCanvas() has been called.
|
|
||||||
* <p>
|
|
||||||
* Same as calling startCanvas(false)
|
|
||||||
*
|
|
||||||
* @see #startCanvas(boolean)
|
|
||||||
*/
|
|
||||||
public void startCanvas(){
|
|
||||||
startCanvas(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Starts the rendering thread after createCanvas() has been called.
|
|
||||||
* <p>
|
|
||||||
* Calling this method is optional, the canvas will start automatically
|
|
||||||
* when it becomes visible.
|
|
||||||
*
|
|
||||||
* @param waitFor If true, the current thread will block until the
|
|
||||||
* rendering thread is running
|
|
||||||
*/
|
|
||||||
public void startCanvas(boolean waitFor){
|
|
||||||
context.create(waitFor);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determine if the application has already started.
|
|
||||||
*
|
|
||||||
* After {@link #start() } but before {@link #stop() }.
|
|
||||||
*
|
|
||||||
* @return if started
|
|
||||||
*/
|
|
||||||
public boolean isStarted() {
|
|
||||||
return context != null && context.isCreated();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Internal use only.
|
|
||||||
*/
|
|
||||||
public void reshape(int w, int h){
|
|
||||||
renderManager.notifyReshape(w, h);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Restarts the context, applying any changed settings.
|
* Restarts the context, applying any changed settings.
|
||||||
@ -544,10 +194,7 @@ public class Application implements SystemListener {
|
|||||||
* applied immediately; calling this method forces the context
|
* applied immediately; calling this method forces the context
|
||||||
* to restart, applying the new settings.
|
* to restart, applying the new settings.
|
||||||
*/
|
*/
|
||||||
public void restart(){
|
public void restart();
|
||||||
context.setSettings(settings);
|
|
||||||
context.restart();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Requests the context to close, shutting down the main loop
|
* Requests the context to close, shutting down the main loop
|
||||||
@ -557,102 +204,14 @@ public class Application implements SystemListener {
|
|||||||
*
|
*
|
||||||
* @see #stop(boolean)
|
* @see #stop(boolean)
|
||||||
*/
|
*/
|
||||||
public void stop(){
|
public void stop();
|
||||||
stop(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Requests the context to close, shutting down the main loop
|
* Requests the context to close, shutting down the main loop
|
||||||
* and making necessary cleanup operations.
|
* and making necessary cleanup operations.
|
||||||
* After the application has stopped, it cannot be used anymore.
|
* After the application has stopped, it cannot be used anymore.
|
||||||
*/
|
*/
|
||||||
public void stop(boolean waitFor){
|
public void stop(boolean waitFor);
|
||||||
logger.log(Level.FINE, "Closing application: {0}", getClass().getName());
|
|
||||||
context.destroy(waitFor);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Do not call manually.
|
|
||||||
* Callback from ContextListener.
|
|
||||||
* <p>
|
|
||||||
* Initializes the <code>Application</code>, by creating a display and
|
|
||||||
* default camera. If display settings are not specified, a default
|
|
||||||
* 640x480 display is created. Default values are used for the camera;
|
|
||||||
* perspective projection with 45° field of view, with near
|
|
||||||
* and far values 1 and 1000 units respectively.
|
|
||||||
*/
|
|
||||||
public void initialize(){
|
|
||||||
if (assetManager == null){
|
|
||||||
initAssetManager();
|
|
||||||
}
|
|
||||||
|
|
||||||
initDisplay();
|
|
||||||
initCamera();
|
|
||||||
|
|
||||||
if (inputEnabled){
|
|
||||||
initInput();
|
|
||||||
}
|
|
||||||
initAudio();
|
|
||||||
|
|
||||||
// update timer so that the next delta is not too large
|
|
||||||
// timer.update();
|
|
||||||
timer.reset();
|
|
||||||
|
|
||||||
// user code here..
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Internal use only.
|
|
||||||
*/
|
|
||||||
public void handleError(String errMsg, Throwable t){
|
|
||||||
// Print error to log.
|
|
||||||
logger.log(Level.SEVERE, errMsg, t);
|
|
||||||
// Display error message on screen if not in headless mode
|
|
||||||
if (context.getType() != JmeContext.Type.Headless) {
|
|
||||||
if (t != null) {
|
|
||||||
JmeSystem.showErrorDialog(errMsg + "\n" + t.getClass().getSimpleName() +
|
|
||||||
(t.getMessage() != null ? ": " + t.getMessage() : ""));
|
|
||||||
} else {
|
|
||||||
JmeSystem.showErrorDialog(errMsg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
stop(); // stop the application
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Internal use only.
|
|
||||||
*/
|
|
||||||
public void gainFocus(){
|
|
||||||
if (lostFocusBehavior != LostFocusBehavior.Disabled) {
|
|
||||||
if (lostFocusBehavior == LostFocusBehavior.PauseOnLostFocus) {
|
|
||||||
paused = false;
|
|
||||||
}
|
|
||||||
context.setAutoFlushFrames(true);
|
|
||||||
if (inputManager != null) {
|
|
||||||
inputManager.reset();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Internal use only.
|
|
||||||
*/
|
|
||||||
public void loseFocus(){
|
|
||||||
if (lostFocusBehavior != LostFocusBehavior.Disabled){
|
|
||||||
if (lostFocusBehavior == LostFocusBehavior.PauseOnLostFocus) {
|
|
||||||
paused = true;
|
|
||||||
}
|
|
||||||
context.setAutoFlushFrames(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Internal use only.
|
|
||||||
*/
|
|
||||||
public void requestClose(boolean esc){
|
|
||||||
context.destroy(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enqueues a task/callable object to execute in the jME3
|
* Enqueues a task/callable object to execute in the jME3
|
||||||
@ -664,11 +223,7 @@ public class Application implements SystemListener {
|
|||||||
*
|
*
|
||||||
* @param callable The callable to run in the main jME3 thread
|
* @param callable The callable to run in the main jME3 thread
|
||||||
*/
|
*/
|
||||||
public <V> Future<V> enqueue(Callable<V> callable) {
|
public <V> Future<V> enqueue(Callable<V> callable);
|
||||||
AppTask<V> task = new AppTask<V>(callable);
|
|
||||||
taskQueue.add(task);
|
|
||||||
return task;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enqueues a runnable object to execute in the jME3
|
* Enqueues a runnable object to execute in the jME3
|
||||||
@ -680,107 +235,13 @@ public class Application implements SystemListener {
|
|||||||
*
|
*
|
||||||
* @param runnable The runnable to run in the main jME3 thread
|
* @param runnable The runnable to run in the main jME3 thread
|
||||||
*/
|
*/
|
||||||
public void enqueue(Runnable runnable){
|
public void enqueue(Runnable runnable);
|
||||||
enqueue(new RunnableWrapper(runnable));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Runs tasks enqueued via {@link #enqueue(Callable)}
|
|
||||||
*/
|
|
||||||
protected void runQueuedTasks() {
|
|
||||||
AppTask<?> task;
|
|
||||||
while( (task = taskQueue.poll()) != null ) {
|
|
||||||
if (!task.isCancelled()) {
|
|
||||||
task.invoke();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Do not call manually.
|
|
||||||
* Callback from ContextListener.
|
|
||||||
*/
|
|
||||||
public void update(){
|
|
||||||
// Make sure the audio renderer is available to callables
|
|
||||||
AudioContext.setAudioRenderer(audioRenderer);
|
|
||||||
|
|
||||||
if (prof!=null) prof.appStep(AppStep.QueuedTasks);
|
|
||||||
runQueuedTasks();
|
|
||||||
|
|
||||||
if (speed == 0 || paused)
|
|
||||||
return;
|
|
||||||
|
|
||||||
timer.update();
|
|
||||||
|
|
||||||
if (inputEnabled){
|
|
||||||
if (prof!=null) prof.appStep(AppStep.ProcessInput);
|
|
||||||
inputManager.update(timer.getTimePerFrame());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (audioRenderer != null){
|
|
||||||
if (prof!=null) prof.appStep(AppStep.ProcessAudio);
|
|
||||||
audioRenderer.update(timer.getTimePerFrame());
|
|
||||||
}
|
|
||||||
|
|
||||||
// user code here..
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void destroyInput(){
|
|
||||||
if (mouseInput != null)
|
|
||||||
mouseInput.destroy();
|
|
||||||
|
|
||||||
if (keyInput != null)
|
|
||||||
keyInput.destroy();
|
|
||||||
|
|
||||||
if (joyInput != null)
|
|
||||||
joyInput.destroy();
|
|
||||||
|
|
||||||
if (touchInput != null)
|
|
||||||
touchInput.destroy();
|
|
||||||
|
|
||||||
inputManager = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Do not call manually.
|
|
||||||
* Callback from ContextListener.
|
|
||||||
*/
|
|
||||||
public void destroy(){
|
|
||||||
stateManager.cleanup();
|
|
||||||
|
|
||||||
destroyInput();
|
|
||||||
if (audioRenderer != null)
|
|
||||||
audioRenderer.cleanup();
|
|
||||||
|
|
||||||
timer.reset();
|
|
||||||
context = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return The GUI viewport. Which is used for the on screen
|
* @return The GUI viewport. Which is used for the on screen
|
||||||
* statistics and FPS.
|
* statistics and FPS.
|
||||||
*/
|
*/
|
||||||
public ViewPort getGuiViewPort() {
|
public ViewPort getGuiViewPort();
|
||||||
return guiViewPort;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ViewPort getViewPort() {
|
|
||||||
return viewPort;
|
|
||||||
}
|
|
||||||
|
|
||||||
private class RunnableWrapper implements Callable{
|
|
||||||
private final Runnable runnable;
|
|
||||||
|
|
||||||
public RunnableWrapper(Runnable runnable){
|
|
||||||
this.runnable = runnable;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object call(){
|
|
||||||
runnable.run();
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
public ViewPort getViewPort();
|
||||||
}
|
}
|
||||||
|
774
jme3-core/src/main/java/com/jme3/app/LegacyApplication.java
Normal file
774
jme3-core/src/main/java/com/jme3/app/LegacyApplication.java
Normal file
@ -0,0 +1,774 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2009-2012 jMonkeyEngine
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are
|
||||||
|
* met:
|
||||||
|
*
|
||||||
|
* * Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* * Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
|
||||||
|
* may be used to endorse or promote products derived from this software
|
||||||
|
* without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||||
|
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||||
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||||
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||||
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||||
|
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||||
|
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||||
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
package com.jme3.app;
|
||||||
|
|
||||||
|
import com.jme3.app.state.AppStateManager;
|
||||||
|
import com.jme3.asset.AssetManager;
|
||||||
|
import com.jme3.audio.AudioContext;
|
||||||
|
import com.jme3.audio.AudioRenderer;
|
||||||
|
import com.jme3.audio.Listener;
|
||||||
|
import com.jme3.input.*;
|
||||||
|
import com.jme3.math.Vector3f;
|
||||||
|
import com.jme3.profile.AppProfiler;
|
||||||
|
import com.jme3.profile.AppStep;
|
||||||
|
import com.jme3.renderer.Camera;
|
||||||
|
import com.jme3.renderer.RenderManager;
|
||||||
|
import com.jme3.renderer.Renderer;
|
||||||
|
import com.jme3.renderer.ViewPort;
|
||||||
|
import com.jme3.system.*;
|
||||||
|
import com.jme3.system.JmeContext.Type;
|
||||||
|
import java.net.MalformedURLException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.util.concurrent.Callable;
|
||||||
|
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||||
|
import java.util.concurrent.Future;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The <code>LegacyApplication</code> class represents an instance of a
|
||||||
|
* real-time 3D rendering jME application.
|
||||||
|
*
|
||||||
|
* An <code>LegacyApplication</code> provides all the tools that are commonly used in jME3
|
||||||
|
* applications.
|
||||||
|
*
|
||||||
|
* jME3 applications *SHOULD NOT EXTEND* this class but extend {@link com.jme3.app.SimpleApplication} instead.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class LegacyApplication implements Application, SystemListener {
|
||||||
|
|
||||||
|
private static final Logger logger = Logger.getLogger(LegacyApplication.class.getName());
|
||||||
|
|
||||||
|
protected AssetManager assetManager;
|
||||||
|
|
||||||
|
protected AudioRenderer audioRenderer;
|
||||||
|
protected Renderer renderer;
|
||||||
|
protected RenderManager renderManager;
|
||||||
|
protected ViewPort viewPort;
|
||||||
|
protected ViewPort guiViewPort;
|
||||||
|
|
||||||
|
protected JmeContext context;
|
||||||
|
protected AppSettings settings;
|
||||||
|
protected Timer timer = new NanoTimer();
|
||||||
|
protected Camera cam;
|
||||||
|
protected Listener listener;
|
||||||
|
|
||||||
|
protected boolean inputEnabled = true;
|
||||||
|
protected LostFocusBehavior lostFocusBehavior = LostFocusBehavior.ThrottleOnLostFocus;
|
||||||
|
protected float speed = 1f;
|
||||||
|
protected boolean paused = false;
|
||||||
|
protected MouseInput mouseInput;
|
||||||
|
protected KeyInput keyInput;
|
||||||
|
protected JoyInput joyInput;
|
||||||
|
protected TouchInput touchInput;
|
||||||
|
protected InputManager inputManager;
|
||||||
|
protected AppStateManager stateManager;
|
||||||
|
|
||||||
|
protected AppProfiler prof;
|
||||||
|
|
||||||
|
private final ConcurrentLinkedQueue<AppTask<?>> taskQueue = new ConcurrentLinkedQueue<AppTask<?>>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new instance of <code>LegacyApplication</code>.
|
||||||
|
*/
|
||||||
|
public LegacyApplication(){
|
||||||
|
initStateManager();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine the application's behavior when unfocused.
|
||||||
|
*
|
||||||
|
* @return The lost focus behavior of the application.
|
||||||
|
*/
|
||||||
|
public LostFocusBehavior getLostFocusBehavior() {
|
||||||
|
return lostFocusBehavior;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Change the application's behavior when unfocused.
|
||||||
|
*
|
||||||
|
* By default, the application will
|
||||||
|
* {@link LostFocusBehavior#ThrottleOnLostFocus throttle the update loop}
|
||||||
|
* so as to not take 100% CPU usage when it is not in focus, e.g.
|
||||||
|
* alt-tabbed, minimized, or obstructed by another window.
|
||||||
|
*
|
||||||
|
* @param lostFocusBehavior The new lost focus behavior to use.
|
||||||
|
*
|
||||||
|
* @see LostFocusBehavior
|
||||||
|
*/
|
||||||
|
public void setLostFocusBehavior(LostFocusBehavior lostFocusBehavior) {
|
||||||
|
this.lostFocusBehavior = lostFocusBehavior;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if pause on lost focus is enabled, false otherwise.
|
||||||
|
*
|
||||||
|
* @return true if pause on lost focus is enabled
|
||||||
|
*
|
||||||
|
* @see #getLostFocusBehavior()
|
||||||
|
*/
|
||||||
|
public boolean isPauseOnLostFocus() {
|
||||||
|
return getLostFocusBehavior() == LostFocusBehavior.PauseOnLostFocus;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enable or disable pause on lost focus.
|
||||||
|
* <p>
|
||||||
|
* By default, pause on lost focus is enabled.
|
||||||
|
* If enabled, the application will stop updating
|
||||||
|
* when it loses focus or becomes inactive (e.g. alt-tab).
|
||||||
|
* For online or real-time applications, this might not be preferable,
|
||||||
|
* so this feature should be set to disabled. For other applications,
|
||||||
|
* it is best to keep it on so that CPU usage is not used when
|
||||||
|
* not necessary.
|
||||||
|
*
|
||||||
|
* @param pauseOnLostFocus True to enable pause on lost focus, false
|
||||||
|
* otherwise.
|
||||||
|
*
|
||||||
|
* @see #setLostFocusBehavior(com.jme3.app.LostFocusBehavior)
|
||||||
|
*/
|
||||||
|
public void setPauseOnLostFocus(boolean pauseOnLostFocus) {
|
||||||
|
if (pauseOnLostFocus) {
|
||||||
|
setLostFocusBehavior(LostFocusBehavior.PauseOnLostFocus);
|
||||||
|
} else {
|
||||||
|
setLostFocusBehavior(LostFocusBehavior.Disabled);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
|
public void setAssetManager(AssetManager assetManager){
|
||||||
|
if (this.assetManager != null)
|
||||||
|
throw new IllegalStateException("Can only set asset manager"
|
||||||
|
+ " before initialization.");
|
||||||
|
|
||||||
|
this.assetManager = assetManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initAssetManager(){
|
||||||
|
URL assetCfgUrl = null;
|
||||||
|
|
||||||
|
if (settings != null){
|
||||||
|
String assetCfg = settings.getString("AssetConfigURL");
|
||||||
|
if (assetCfg != null){
|
||||||
|
try {
|
||||||
|
assetCfgUrl = new URL(assetCfg);
|
||||||
|
} catch (MalformedURLException ex) {
|
||||||
|
}
|
||||||
|
if (assetCfgUrl == null) {
|
||||||
|
assetCfgUrl = LegacyApplication.class.getClassLoader().getResource(assetCfg);
|
||||||
|
if (assetCfgUrl == null) {
|
||||||
|
logger.log(Level.SEVERE, "Unable to access AssetConfigURL in asset config:{0}", assetCfg);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (assetCfgUrl == null) {
|
||||||
|
assetCfgUrl = JmeSystem.getPlatformAssetConfigURL();
|
||||||
|
}
|
||||||
|
if (assetManager == null){
|
||||||
|
assetManager = JmeSystem.newAssetManager(assetCfgUrl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the display settings to define the display created.
|
||||||
|
* <p>
|
||||||
|
* Examples of display parameters include display pixel width and height,
|
||||||
|
* color bit depth, z-buffer bits, anti-aliasing samples, and update frequency.
|
||||||
|
* If this method is called while the application is already running, then
|
||||||
|
* {@link #restart() } must be called to apply the settings to the display.
|
||||||
|
*
|
||||||
|
* @param settings The settings to set.
|
||||||
|
*/
|
||||||
|
public void setSettings(AppSettings settings){
|
||||||
|
this.settings = settings;
|
||||||
|
if (context != null && settings.useInput() != inputEnabled){
|
||||||
|
// may need to create or destroy input based
|
||||||
|
// on settings change
|
||||||
|
inputEnabled = !inputEnabled;
|
||||||
|
if (inputEnabled){
|
||||||
|
initInput();
|
||||||
|
}else{
|
||||||
|
destroyInput();
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
inputEnabled = settings.useInput();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the Timer implementation that will be used for calculating
|
||||||
|
* frame times. By default, Application will use the Timer as returned
|
||||||
|
* by the current JmeContext implementation.
|
||||||
|
*/
|
||||||
|
public void setTimer(Timer timer){
|
||||||
|
this.timer = timer;
|
||||||
|
|
||||||
|
if (timer != null) {
|
||||||
|
timer.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (renderManager != null) {
|
||||||
|
renderManager.setTimer(timer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Timer getTimer(){
|
||||||
|
return timer;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initDisplay(){
|
||||||
|
// aquire important objects
|
||||||
|
// from the context
|
||||||
|
settings = context.getSettings();
|
||||||
|
|
||||||
|
// Only reset the timer if a user has not already provided one
|
||||||
|
if (timer == null) {
|
||||||
|
timer = context.getTimer();
|
||||||
|
}
|
||||||
|
|
||||||
|
renderer = context.getRenderer();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initAudio(){
|
||||||
|
if (settings.getAudioRenderer() != null && context.getType() != Type.Headless){
|
||||||
|
audioRenderer = JmeSystem.newAudioRenderer(settings);
|
||||||
|
audioRenderer.initialize();
|
||||||
|
AudioContext.setAudioRenderer(audioRenderer);
|
||||||
|
|
||||||
|
listener = new Listener();
|
||||||
|
audioRenderer.setListener(listener);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates the camera to use for rendering. Default values are perspective
|
||||||
|
* projection with 45° field of view, with near and far values 1 and 1000
|
||||||
|
* units respectively.
|
||||||
|
*/
|
||||||
|
private void initCamera(){
|
||||||
|
cam = new Camera(settings.getWidth(), settings.getHeight());
|
||||||
|
|
||||||
|
cam.setFrustumPerspective(45f, (float)cam.getWidth() / cam.getHeight(), 1f, 1000f);
|
||||||
|
cam.setLocation(new Vector3f(0f, 0f, 10f));
|
||||||
|
cam.lookAt(new Vector3f(0f, 0f, 0f), Vector3f.UNIT_Y);
|
||||||
|
|
||||||
|
renderManager = new RenderManager(renderer);
|
||||||
|
//Remy - 09/14/2010 setted the timer in the renderManager
|
||||||
|
renderManager.setTimer(timer);
|
||||||
|
|
||||||
|
if (prof != null) {
|
||||||
|
renderManager.setAppProfiler(prof);
|
||||||
|
}
|
||||||
|
|
||||||
|
viewPort = renderManager.createMainView("Default", cam);
|
||||||
|
viewPort.setClearFlags(true, true, true);
|
||||||
|
|
||||||
|
// Create a new cam for the gui
|
||||||
|
Camera guiCam = new Camera(settings.getWidth(), settings.getHeight());
|
||||||
|
guiViewPort = renderManager.createPostView("Gui Default", guiCam);
|
||||||
|
guiViewPort.setClearFlags(false, false, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes mouse and keyboard input. Also
|
||||||
|
* initializes joystick input if joysticks are enabled in the
|
||||||
|
* AppSettings.
|
||||||
|
*/
|
||||||
|
private void initInput(){
|
||||||
|
mouseInput = context.getMouseInput();
|
||||||
|
if (mouseInput != null)
|
||||||
|
mouseInput.initialize();
|
||||||
|
|
||||||
|
keyInput = context.getKeyInput();
|
||||||
|
if (keyInput != null)
|
||||||
|
keyInput.initialize();
|
||||||
|
|
||||||
|
touchInput = context.getTouchInput();
|
||||||
|
if (touchInput != null)
|
||||||
|
touchInput.initialize();
|
||||||
|
|
||||||
|
if (!settings.getBoolean("DisableJoysticks")){
|
||||||
|
joyInput = context.getJoyInput();
|
||||||
|
if (joyInput != null)
|
||||||
|
joyInput.initialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
inputManager = new InputManager(mouseInput, keyInput, joyInput, touchInput);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initStateManager(){
|
||||||
|
stateManager = new AppStateManager(this);
|
||||||
|
|
||||||
|
// Always register a ResetStatsState to make sure
|
||||||
|
// that the stats are cleared every frame
|
||||||
|
stateManager.attach(new ResetStatsState());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The {@link AssetManager asset manager} for this application.
|
||||||
|
*/
|
||||||
|
public AssetManager getAssetManager(){
|
||||||
|
return assetManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the {@link InputManager input manager}.
|
||||||
|
*/
|
||||||
|
public InputManager getInputManager(){
|
||||||
|
return inputManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the {@link AppStateManager app state manager}
|
||||||
|
*/
|
||||||
|
public AppStateManager getStateManager() {
|
||||||
|
return stateManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the {@link RenderManager render manager}
|
||||||
|
*/
|
||||||
|
public RenderManager getRenderManager() {
|
||||||
|
return renderManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The {@link Renderer renderer} for the application
|
||||||
|
*/
|
||||||
|
public Renderer getRenderer(){
|
||||||
|
return renderer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The {@link AudioRenderer audio renderer} for the application
|
||||||
|
*/
|
||||||
|
public AudioRenderer getAudioRenderer() {
|
||||||
|
return audioRenderer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The {@link Listener listener} object for audio
|
||||||
|
*/
|
||||||
|
public Listener getListener() {
|
||||||
|
return listener;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The {@link JmeContext display context} for the application
|
||||||
|
*/
|
||||||
|
public JmeContext getContext(){
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The {@link Camera camera} for the application
|
||||||
|
*/
|
||||||
|
public Camera getCamera(){
|
||||||
|
return cam;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts the application in {@link Type#Display display} mode.
|
||||||
|
*
|
||||||
|
* @see #start(com.jme3.system.JmeContext.Type)
|
||||||
|
*/
|
||||||
|
public void start(){
|
||||||
|
start(JmeContext.Type.Display, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts the application in {@link Type#Display display} mode.
|
||||||
|
*
|
||||||
|
* @see #start(com.jme3.system.JmeContext.Type)
|
||||||
|
*/
|
||||||
|
public void start(boolean waitFor){
|
||||||
|
start(JmeContext.Type.Display, waitFor);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts the application.
|
||||||
|
* Creating a rendering context and executing
|
||||||
|
* the main loop in a separate thread.
|
||||||
|
*/
|
||||||
|
public void start(JmeContext.Type contextType) {
|
||||||
|
start(contextType, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts the application.
|
||||||
|
* Creating a rendering context and executing
|
||||||
|
* the main loop in a separate thread.
|
||||||
|
*/
|
||||||
|
public void start(JmeContext.Type contextType, boolean waitFor){
|
||||||
|
if (context != null && context.isCreated()){
|
||||||
|
logger.warning("start() called when application already created!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (settings == null){
|
||||||
|
settings = new AppSettings(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.log(Level.FINE, "Starting application: {0}", getClass().getName());
|
||||||
|
context = JmeSystem.newContext(settings, contextType);
|
||||||
|
context.setSystemListener(this);
|
||||||
|
context.create(waitFor);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets an AppProfiler hook that will be called back for
|
||||||
|
* specific steps within a single update frame. Value defaults
|
||||||
|
* to null.
|
||||||
|
*/
|
||||||
|
public void setAppProfiler(AppProfiler prof) {
|
||||||
|
this.prof = prof;
|
||||||
|
if (renderManager != null) {
|
||||||
|
renderManager.setAppProfiler(prof);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the current AppProfiler hook, or null if none is set.
|
||||||
|
*/
|
||||||
|
public AppProfiler getAppProfiler() {
|
||||||
|
return prof;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the application's canvas for use.
|
||||||
|
* <p>
|
||||||
|
* After calling this method, cast the {@link #getContext() context} to
|
||||||
|
* {@link JmeCanvasContext},
|
||||||
|
* then acquire the canvas with {@link JmeCanvasContext#getCanvas() }
|
||||||
|
* and attach it to an AWT/Swing Frame.
|
||||||
|
* The rendering thread will start when the canvas becomes visible on
|
||||||
|
* screen, however if you wish to start the context immediately you
|
||||||
|
* may call {@link #startCanvas() } to force the rendering thread
|
||||||
|
* to start.
|
||||||
|
*
|
||||||
|
* @see JmeCanvasContext
|
||||||
|
* @see Type#Canvas
|
||||||
|
*/
|
||||||
|
public void createCanvas(){
|
||||||
|
if (context != null && context.isCreated()){
|
||||||
|
logger.warning("createCanvas() called when application already created!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (settings == null){
|
||||||
|
settings = new AppSettings(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.log(Level.FINE, "Starting application: {0}", getClass().getName());
|
||||||
|
context = JmeSystem.newContext(settings, JmeContext.Type.Canvas);
|
||||||
|
context.setSystemListener(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts the rendering thread after createCanvas() has been called.
|
||||||
|
* <p>
|
||||||
|
* Same as calling startCanvas(false)
|
||||||
|
*
|
||||||
|
* @see #startCanvas(boolean)
|
||||||
|
*/
|
||||||
|
public void startCanvas(){
|
||||||
|
startCanvas(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts the rendering thread after createCanvas() has been called.
|
||||||
|
* <p>
|
||||||
|
* Calling this method is optional, the canvas will start automatically
|
||||||
|
* when it becomes visible.
|
||||||
|
*
|
||||||
|
* @param waitFor If true, the current thread will block until the
|
||||||
|
* rendering thread is running
|
||||||
|
*/
|
||||||
|
public void startCanvas(boolean waitFor){
|
||||||
|
context.create(waitFor);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal use only.
|
||||||
|
*/
|
||||||
|
public void reshape(int w, int h){
|
||||||
|
renderManager.notifyReshape(w, h);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Restarts the context, applying any changed settings.
|
||||||
|
* <p>
|
||||||
|
* Changes to the {@link AppSettings} of this Application are not
|
||||||
|
* applied immediately; calling this method forces the context
|
||||||
|
* to restart, applying the new settings.
|
||||||
|
*/
|
||||||
|
public void restart(){
|
||||||
|
context.setSettings(settings);
|
||||||
|
context.restart();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Requests the context to close, shutting down the main loop
|
||||||
|
* and making necessary cleanup operations.
|
||||||
|
*
|
||||||
|
* Same as calling stop(false)
|
||||||
|
*
|
||||||
|
* @see #stop(boolean)
|
||||||
|
*/
|
||||||
|
public void stop(){
|
||||||
|
stop(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Requests the context to close, shutting down the main loop
|
||||||
|
* and making necessary cleanup operations.
|
||||||
|
* After the application has stopped, it cannot be used anymore.
|
||||||
|
*/
|
||||||
|
public void stop(boolean waitFor){
|
||||||
|
logger.log(Level.FINE, "Closing application: {0}", getClass().getName());
|
||||||
|
context.destroy(waitFor);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Do not call manually.
|
||||||
|
* Callback from ContextListener.
|
||||||
|
* <p>
|
||||||
|
* Initializes the <code>Application</code>, by creating a display and
|
||||||
|
* default camera. If display settings are not specified, a default
|
||||||
|
* 640x480 display is created. Default values are used for the camera;
|
||||||
|
* perspective projection with 45° field of view, with near
|
||||||
|
* and far values 1 and 1000 units respectively.
|
||||||
|
*/
|
||||||
|
public void initialize(){
|
||||||
|
if (assetManager == null){
|
||||||
|
initAssetManager();
|
||||||
|
}
|
||||||
|
|
||||||
|
initDisplay();
|
||||||
|
initCamera();
|
||||||
|
|
||||||
|
if (inputEnabled){
|
||||||
|
initInput();
|
||||||
|
}
|
||||||
|
initAudio();
|
||||||
|
|
||||||
|
// update timer so that the next delta is not too large
|
||||||
|
// timer.update();
|
||||||
|
timer.reset();
|
||||||
|
|
||||||
|
// user code here..
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal use only.
|
||||||
|
*/
|
||||||
|
public void handleError(String errMsg, Throwable t){
|
||||||
|
// Print error to log.
|
||||||
|
logger.log(Level.SEVERE, errMsg, t);
|
||||||
|
// Display error message on screen if not in headless mode
|
||||||
|
if (context.getType() != JmeContext.Type.Headless) {
|
||||||
|
if (t != null) {
|
||||||
|
JmeSystem.showErrorDialog(errMsg + "\n" + t.getClass().getSimpleName() +
|
||||||
|
(t.getMessage() != null ? ": " + t.getMessage() : ""));
|
||||||
|
} else {
|
||||||
|
JmeSystem.showErrorDialog(errMsg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stop(); // stop the application
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal use only.
|
||||||
|
*/
|
||||||
|
public void gainFocus(){
|
||||||
|
if (lostFocusBehavior != LostFocusBehavior.Disabled) {
|
||||||
|
if (lostFocusBehavior == LostFocusBehavior.PauseOnLostFocus) {
|
||||||
|
paused = false;
|
||||||
|
}
|
||||||
|
context.setAutoFlushFrames(true);
|
||||||
|
if (inputManager != null) {
|
||||||
|
inputManager.reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal use only.
|
||||||
|
*/
|
||||||
|
public void loseFocus(){
|
||||||
|
if (lostFocusBehavior != LostFocusBehavior.Disabled){
|
||||||
|
if (lostFocusBehavior == LostFocusBehavior.PauseOnLostFocus) {
|
||||||
|
paused = true;
|
||||||
|
}
|
||||||
|
context.setAutoFlushFrames(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal use only.
|
||||||
|
*/
|
||||||
|
public void requestClose(boolean esc){
|
||||||
|
context.destroy(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enqueues a task/callable object to execute in the jME3
|
||||||
|
* rendering thread.
|
||||||
|
* <p>
|
||||||
|
* Callables are executed right at the beginning of the main loop.
|
||||||
|
* They are executed even if the application is currently paused
|
||||||
|
* or out of focus.
|
||||||
|
*
|
||||||
|
* @param callable The callable to run in the main jME3 thread
|
||||||
|
*/
|
||||||
|
public <V> Future<V> enqueue(Callable<V> callable) {
|
||||||
|
AppTask<V> task = new AppTask<V>(callable);
|
||||||
|
taskQueue.add(task);
|
||||||
|
return task;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enqueues a runnable object to execute in the jME3
|
||||||
|
* rendering thread.
|
||||||
|
* <p>
|
||||||
|
* Runnables are executed right at the beginning of the main loop.
|
||||||
|
* They are executed even if the application is currently paused
|
||||||
|
* or out of focus.
|
||||||
|
*
|
||||||
|
* @param runnable The runnable to run in the main jME3 thread
|
||||||
|
*/
|
||||||
|
public void enqueue(Runnable runnable){
|
||||||
|
enqueue(new RunnableWrapper(runnable));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runs tasks enqueued via {@link #enqueue(Callable)}
|
||||||
|
*/
|
||||||
|
protected void runQueuedTasks() {
|
||||||
|
AppTask<?> task;
|
||||||
|
while( (task = taskQueue.poll()) != null ) {
|
||||||
|
if (!task.isCancelled()) {
|
||||||
|
task.invoke();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Do not call manually.
|
||||||
|
* Callback from ContextListener.
|
||||||
|
*/
|
||||||
|
public void update(){
|
||||||
|
// Make sure the audio renderer is available to callables
|
||||||
|
AudioContext.setAudioRenderer(audioRenderer);
|
||||||
|
|
||||||
|
if (prof!=null) prof.appStep(AppStep.QueuedTasks);
|
||||||
|
runQueuedTasks();
|
||||||
|
|
||||||
|
if (speed == 0 || paused)
|
||||||
|
return;
|
||||||
|
|
||||||
|
timer.update();
|
||||||
|
|
||||||
|
if (inputEnabled){
|
||||||
|
if (prof!=null) prof.appStep(AppStep.ProcessInput);
|
||||||
|
inputManager.update(timer.getTimePerFrame());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (audioRenderer != null){
|
||||||
|
if (prof!=null) prof.appStep(AppStep.ProcessAudio);
|
||||||
|
audioRenderer.update(timer.getTimePerFrame());
|
||||||
|
}
|
||||||
|
|
||||||
|
// user code here..
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void destroyInput(){
|
||||||
|
if (mouseInput != null)
|
||||||
|
mouseInput.destroy();
|
||||||
|
|
||||||
|
if (keyInput != null)
|
||||||
|
keyInput.destroy();
|
||||||
|
|
||||||
|
if (joyInput != null)
|
||||||
|
joyInput.destroy();
|
||||||
|
|
||||||
|
if (touchInput != null)
|
||||||
|
touchInput.destroy();
|
||||||
|
|
||||||
|
inputManager = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Do not call manually.
|
||||||
|
* Callback from ContextListener.
|
||||||
|
*/
|
||||||
|
public void destroy(){
|
||||||
|
stateManager.cleanup();
|
||||||
|
|
||||||
|
destroyInput();
|
||||||
|
if (audioRenderer != null)
|
||||||
|
audioRenderer.cleanup();
|
||||||
|
|
||||||
|
timer.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The GUI viewport. Which is used for the on screen
|
||||||
|
* statistics and FPS.
|
||||||
|
*/
|
||||||
|
public ViewPort getGuiViewPort() {
|
||||||
|
return guiViewPort;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ViewPort getViewPort() {
|
||||||
|
return viewPort;
|
||||||
|
}
|
||||||
|
|
||||||
|
private class RunnableWrapper implements Callable{
|
||||||
|
private final Runnable runnable;
|
||||||
|
|
||||||
|
public RunnableWrapper(Runnable runnable){
|
||||||
|
this.runnable = runnable;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object call(){
|
||||||
|
runnable.run();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -63,7 +63,7 @@ import com.jme3.system.JmeSystem;
|
|||||||
* A {@link com.jme3.app.FlyCamAppState} is by default attached as well and can
|
* A {@link com.jme3.app.FlyCamAppState} is by default attached as well and can
|
||||||
* be removed by calling <code>stateManager.detach( stateManager.getState(FlyCamAppState.class) );</code>
|
* be removed by calling <code>stateManager.detach( stateManager.getState(FlyCamAppState.class) );</code>
|
||||||
*/
|
*/
|
||||||
public abstract class SimpleApplication extends Application {
|
public abstract class SimpleApplication extends LegacyApplication {
|
||||||
|
|
||||||
public static final String INPUT_MAPPING_EXIT = "SIMPLEAPP_Exit";
|
public static final String INPUT_MAPPING_EXIT = "SIMPLEAPP_Exit";
|
||||||
public static final String INPUT_MAPPING_CAMERA_POS = DebugKeysAppState.INPUT_MAPPING_CAMERA_POS;
|
public static final String INPUT_MAPPING_CAMERA_POS = DebugKeysAppState.INPUT_MAPPING_CAMERA_POS;
|
||||||
|
@ -195,7 +195,7 @@ public class StatsAppState extends AbstractAppState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void loadDarken() {
|
public void loadDarken() {
|
||||||
Material mat = new Material(app.assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
|
Material mat = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md");
|
||||||
mat.setColor("Color", new ColorRGBA(0,0,0,0.5f));
|
mat.setColor("Color", new ColorRGBA(0,0,0,0.5f));
|
||||||
mat.getAdditionalRenderState().setBlendMode(BlendMode.Alpha);
|
mat.getAdditionalRenderState().setBlendMode(BlendMode.Alpha);
|
||||||
|
|
||||||
|
@ -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 StatsView 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) {
|
} catch (IOException ex) {
|
||||||
logger.log(Level.SEVERE, "Error while saving screenshot", ex);
|
logger.log(Level.SEVERE, "Error while saving screenshot", ex);
|
||||||
} finally {
|
|
||||||
if (outStream != null){
|
|
||||||
try {
|
|
||||||
outStream.close();
|
|
||||||
} catch (IOException 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -41,6 +41,7 @@ import com.jme3.export.OutputCapsule;
|
|||||||
import com.jme3.math.Vector3f;
|
import com.jme3.math.Vector3f;
|
||||||
import com.jme3.scene.Node;
|
import com.jme3.scene.Node;
|
||||||
import com.jme3.util.PlaceholderAssets;
|
import com.jme3.util.PlaceholderAssets;
|
||||||
|
import com.jme3.util.clone.Cloner;
|
||||||
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;
|
||||||
@ -724,6 +725,30 @@ public class AudioNode extends Node implements AudioSource {
|
|||||||
return clone;
|
return clone;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called internally by com.jme3.util.clone.Cloner. Do not call directly.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void cloneFields( Cloner cloner, Object original ) {
|
||||||
|
super.cloneFields(cloner, original);
|
||||||
|
|
||||||
|
this.direction = cloner.clone(direction);
|
||||||
|
this.velocity = cloner.clone(velocity);
|
||||||
|
|
||||||
|
// Change in behavior: the filters were not cloned before meaning
|
||||||
|
// that two cloned audio nodes would share the same filter instance.
|
||||||
|
// While settings will only be applied when the filter is actually
|
||||||
|
// set, I think it's probably surprising to callers if the values of
|
||||||
|
// a filter change from one AudioNode when a different AudioNode's
|
||||||
|
// filter attributes are updated.
|
||||||
|
// Plus if they disable and re-enable the thing using the filter then
|
||||||
|
// the settings get reapplied and it might be surprising to have them
|
||||||
|
// suddenly be strange.
|
||||||
|
// ...so I'll clone them. -pspeed
|
||||||
|
this.dryFilter = cloner.clone(dryFilter);
|
||||||
|
this.reverbFilter = cloner.clone(reverbFilter);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void write(JmeExporter ex) throws IOException {
|
public void write(JmeExporter ex) throws IOException {
|
||||||
super.write(ex);
|
super.write(ex);
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2009-2012 jMonkeyEngine
|
* Copyright (c) 2009-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
|
||||||
@ -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,15 +58,15 @@ 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;
|
||||||
protected float currentValue;
|
protected float currentValue;
|
||||||
protected Vector3f direction = new Vector3f();
|
protected Vector3f direction = new Vector3f();
|
||||||
protected Vector3f lookAt;
|
protected Vector3f lookAt = null;
|
||||||
protected Vector3f upVector = Vector3f.UNIT_Y;
|
protected Vector3f upVector = Vector3f.UNIT_Y;
|
||||||
protected Quaternion rotation;
|
protected Quaternion rotation = null;
|
||||||
protected Direction directionType = Direction.None;
|
protected Direction directionType = Direction.None;
|
||||||
protected MotionPath path;
|
protected MotionPath path;
|
||||||
private boolean isControl = true;
|
private boolean isControl = true;
|
||||||
@ -118,7 +120,6 @@ public class MotionEvent extends AbstractCinematicEvent implements Control {
|
|||||||
*/
|
*/
|
||||||
public MotionEvent(Spatial spatial, MotionPath path) {
|
public MotionEvent(Spatial spatial, MotionPath path) {
|
||||||
super();
|
super();
|
||||||
this.spatial = spatial;
|
|
||||||
spatial.addControl(this);
|
spatial.addControl(this);
|
||||||
this.path = path;
|
this.path = path;
|
||||||
}
|
}
|
||||||
@ -130,7 +131,6 @@ public class MotionEvent extends AbstractCinematicEvent implements Control {
|
|||||||
*/
|
*/
|
||||||
public MotionEvent(Spatial spatial, MotionPath path, float initialDuration) {
|
public MotionEvent(Spatial spatial, MotionPath path, float initialDuration) {
|
||||||
super(initialDuration);
|
super(initialDuration);
|
||||||
this.spatial = spatial;
|
|
||||||
spatial.addControl(this);
|
spatial.addControl(this);
|
||||||
this.path = path;
|
this.path = path;
|
||||||
}
|
}
|
||||||
@ -142,7 +142,6 @@ public class MotionEvent extends AbstractCinematicEvent implements Control {
|
|||||||
*/
|
*/
|
||||||
public MotionEvent(Spatial spatial, MotionPath path, LoopMode loopMode) {
|
public MotionEvent(Spatial spatial, MotionPath path, LoopMode loopMode) {
|
||||||
super();
|
super();
|
||||||
this.spatial = spatial;
|
|
||||||
spatial.addControl(this);
|
spatial.addControl(this);
|
||||||
this.path = path;
|
this.path = path;
|
||||||
this.loopMode = loopMode;
|
this.loopMode = loopMode;
|
||||||
@ -155,7 +154,6 @@ public class MotionEvent extends AbstractCinematicEvent implements Control {
|
|||||||
*/
|
*/
|
||||||
public MotionEvent(Spatial spatial, MotionPath path, float initialDuration, LoopMode loopMode) {
|
public MotionEvent(Spatial spatial, MotionPath path, float initialDuration, LoopMode loopMode) {
|
||||||
super(initialDuration);
|
super(initialDuration);
|
||||||
this.spatial = spatial;
|
|
||||||
spatial.addControl(this);
|
spatial.addControl(this);
|
||||||
this.path = path;
|
this.path = path;
|
||||||
this.loopMode = loopMode;
|
this.loopMode = loopMode;
|
||||||
@ -211,9 +209,9 @@ public class MotionEvent extends AbstractCinematicEvent implements Control {
|
|||||||
public void write(JmeExporter ex) throws IOException {
|
public void write(JmeExporter ex) throws IOException {
|
||||||
super.write(ex);
|
super.write(ex);
|
||||||
OutputCapsule oc = ex.getCapsule(this);
|
OutputCapsule oc = ex.getCapsule(this);
|
||||||
oc.write(lookAt, "lookAt", Vector3f.ZERO);
|
oc.write(lookAt, "lookAt", null);
|
||||||
oc.write(upVector, "upVector", Vector3f.UNIT_Y);
|
oc.write(upVector, "upVector", Vector3f.UNIT_Y);
|
||||||
oc.write(rotation, "rotation", Quaternion.IDENTITY);
|
oc.write(rotation, "rotation", null);
|
||||||
oc.write(directionType, "directionType", Direction.None);
|
oc.write(directionType, "directionType", Direction.None);
|
||||||
oc.write(path, "path", null);
|
oc.write(path, "path", null);
|
||||||
}
|
}
|
||||||
@ -222,9 +220,9 @@ public class MotionEvent extends AbstractCinematicEvent implements Control {
|
|||||||
public void read(JmeImporter im) throws IOException {
|
public void read(JmeImporter im) throws IOException {
|
||||||
super.read(im);
|
super.read(im);
|
||||||
InputCapsule in = im.getCapsule(this);
|
InputCapsule in = im.getCapsule(this);
|
||||||
lookAt = (Vector3f) in.readSavable("lookAt", Vector3f.ZERO);
|
lookAt = (Vector3f) in.readSavable("lookAt", null);
|
||||||
upVector = (Vector3f) in.readSavable("upVector", Vector3f.UNIT_Y);
|
upVector = (Vector3f) in.readSavable("upVector", Vector3f.UNIT_Y);
|
||||||
rotation = (Quaternion) in.readSavable("rotation", Quaternion.IDENTITY);
|
rotation = (Quaternion) in.readSavable("rotation", null);
|
||||||
directionType = in.readEnum("directionType", Direction.class, Direction.None);
|
directionType = in.readEnum("directionType", Direction.class, Direction.None);
|
||||||
path = (MotionPath) in.readSavable("path", null);
|
path = (MotionPath) in.readSavable("path", null);
|
||||||
}
|
}
|
||||||
@ -274,15 +272,17 @@ 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();
|
||||||
|
control.setPath(path);
|
||||||
control.playState = playState;
|
control.playState = playState;
|
||||||
control.currentWayPoint = currentWayPoint;
|
control.currentWayPoint = currentWayPoint;
|
||||||
control.currentValue = currentValue;
|
control.currentValue = currentValue;
|
||||||
control.direction = direction.clone();
|
control.direction = direction.clone();
|
||||||
control.lookAt = lookAt.clone();
|
control.lookAt = lookAt;
|
||||||
control.upVector = upVector.clone();
|
control.upVector = upVector.clone();
|
||||||
control.rotation = rotation.clone();
|
control.rotation = rotation;
|
||||||
control.initialDuration = initialDuration;
|
control.initialDuration = initialDuration;
|
||||||
control.speed = speed;
|
control.speed = speed;
|
||||||
control.loopMode = loopMode;
|
control.loopMode = loopMode;
|
||||||
@ -291,6 +291,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;
|
||||||
|
control.upVector = upVector.clone();
|
||||||
|
control.rotation = rotation;
|
||||||
|
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) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -157,6 +174,13 @@ public class ParticleEmitter extends Geometry {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ParticleEmitter clone(boolean cloneMaterial) {
|
public ParticleEmitter clone(boolean cloneMaterial) {
|
||||||
|
return (ParticleEmitter)super.clone(cloneMaterial);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The old clone() method that did not use the new Cloner utility.
|
||||||
|
*/
|
||||||
|
public ParticleEmitter oldClone(boolean cloneMaterial) {
|
||||||
ParticleEmitter clone = (ParticleEmitter) super.clone(cloneMaterial);
|
ParticleEmitter clone = (ParticleEmitter) super.clone(cloneMaterial);
|
||||||
clone.shape = shape.deepClone();
|
clone.shape = shape.deepClone();
|
||||||
|
|
||||||
@ -194,6 +218,44 @@ public class ParticleEmitter extends Geometry {
|
|||||||
return clone;
|
return clone;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called internally by com.jme3.util.clone.Cloner. Do not call directly.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void cloneFields( Cloner cloner, Object original ) {
|
||||||
|
super.cloneFields(cloner, original);
|
||||||
|
|
||||||
|
this.shape = cloner.clone(shape);
|
||||||
|
this.control = cloner.clone(control);
|
||||||
|
this.faceNormal = cloner.clone(faceNormal);
|
||||||
|
this.startColor = cloner.clone(startColor);
|
||||||
|
this.endColor = cloner.clone(endColor);
|
||||||
|
this.particleInfluencer = cloner.clone(particleInfluencer);
|
||||||
|
|
||||||
|
// change in behavior: gravity was not cloned before -pspeed
|
||||||
|
this.gravity = cloner.clone(gravity);
|
||||||
|
|
||||||
|
// So, simply setting the mesh type will cause all kinds of things
|
||||||
|
// to happen:
|
||||||
|
// 1) the new mesh gets created.
|
||||||
|
// 2) it is set to the Geometry
|
||||||
|
// 3) the particles array is recreated because setNumParticles()
|
||||||
|
//
|
||||||
|
// ...so this should be equivalent but simpler than half of the old clone()
|
||||||
|
// method. Note: we do not ever want to share particleMesh so we do not
|
||||||
|
// clone it at all.
|
||||||
|
setMeshType(meshType);
|
||||||
|
|
||||||
|
// change in behavior: temp and lastPos were not cloned before...
|
||||||
|
// perhaps because it was believed that 'transient' fields were exluded
|
||||||
|
// from cloning? (they aren't)
|
||||||
|
// If it was ok for these to be shared because of how they are used
|
||||||
|
// then they could just as well be made static... else I think it's clearer
|
||||||
|
// to clone them.
|
||||||
|
this.temp = cloner.clone(temp);
|
||||||
|
this.lastPos = cloner.clone(lastPos);
|
||||||
|
}
|
||||||
|
|
||||||
public ParticleEmitter(String name, Type type, int numParticles) {
|
public ParticleEmitter(String name, Type type, int numParticles) {
|
||||||
super(name);
|
super(name);
|
||||||
setBatchHint(BatchHint.Never);
|
setBatchHint(BatchHint.Never);
|
||||||
|
@ -39,6 +39,8 @@ import com.jme3.export.JmeImporter;
|
|||||||
import com.jme3.export.OutputCapsule;
|
import com.jme3.export.OutputCapsule;
|
||||||
import com.jme3.math.FastMath;
|
import com.jme3.math.FastMath;
|
||||||
import com.jme3.math.Vector3f;
|
import com.jme3.math.Vector3f;
|
||||||
|
import com.jme3.util.clone.Cloner;
|
||||||
|
import com.jme3.util.clone.JmeCloneable;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -109,6 +111,35 @@ public class DefaultParticleInfluencer implements ParticleInfluencer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called internally by com.jme3.util.clone.Cloner. Do not call directly.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Object jmeClone() {
|
||||||
|
try {
|
||||||
|
return super.clone();
|
||||||
|
} catch (CloneNotSupportedException ex) {
|
||||||
|
throw new AssertionError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called internally by com.jme3.util.clone.Cloner. Do not call directly.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void cloneFields( Cloner cloner, Object original ) {
|
||||||
|
this.initialVelocity = cloner.clone(initialVelocity);
|
||||||
|
|
||||||
|
// Change in behavior: I'm cloning 'for real' the 'temp' field because
|
||||||
|
// otherwise it will be shared across all clones. Note: if this is
|
||||||
|
// ok because of how its used then it might as well be static and let
|
||||||
|
// everything share it.
|
||||||
|
// Note 2: transient fields _are_ cloned just like anything else so
|
||||||
|
// thinking it wouldn't get cloned is also not right.
|
||||||
|
// -pspeed
|
||||||
|
this.temp = cloner.clone(temp);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setInitialVelocity(Vector3f initialVelocity) {
|
public void setInitialVelocity(Vector3f initialVelocity) {
|
||||||
this.initialVelocity.set(initialVelocity);
|
this.initialVelocity.set(initialVelocity);
|
||||||
|
@ -36,6 +36,8 @@ import com.jme3.effect.shapes.EmitterShape;
|
|||||||
import com.jme3.export.JmeExporter;
|
import com.jme3.export.JmeExporter;
|
||||||
import com.jme3.export.JmeImporter;
|
import com.jme3.export.JmeImporter;
|
||||||
import com.jme3.math.Vector3f;
|
import com.jme3.math.Vector3f;
|
||||||
|
import com.jme3.util.clone.Cloner;
|
||||||
|
import com.jme3.util.clone.JmeCloneable;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -83,4 +85,23 @@ public class EmptyParticleInfluencer implements ParticleInfluencer {
|
|||||||
throw new AssertionError();
|
throw new AssertionError();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called internally by com.jme3.util.clone.Cloner. Do not call directly.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Object jmeClone() {
|
||||||
|
try {
|
||||||
|
return super.clone();
|
||||||
|
} catch (CloneNotSupportedException ex) {
|
||||||
|
throw new AssertionError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called internally by com.jme3.util.clone.Cloner. Do not call directly.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void cloneFields( Cloner cloner, Object original ) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -39,6 +39,8 @@ import com.jme3.export.JmeImporter;
|
|||||||
import com.jme3.export.OutputCapsule;
|
import com.jme3.export.OutputCapsule;
|
||||||
import com.jme3.math.FastMath;
|
import com.jme3.math.FastMath;
|
||||||
import com.jme3.math.Matrix3f;
|
import com.jme3.math.Matrix3f;
|
||||||
|
import com.jme3.util.clone.Cloner;
|
||||||
|
import com.jme3.util.clone.JmeCloneable;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -36,12 +36,13 @@ import com.jme3.effect.ParticleEmitter;
|
|||||||
import com.jme3.effect.shapes.EmitterShape;
|
import com.jme3.effect.shapes.EmitterShape;
|
||||||
import com.jme3.export.Savable;
|
import com.jme3.export.Savable;
|
||||||
import com.jme3.math.Vector3f;
|
import com.jme3.math.Vector3f;
|
||||||
|
import com.jme3.util.clone.JmeCloneable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An interface that defines the methods to affect initial velocity of the particles.
|
* An interface that defines the methods to affect initial velocity of the particles.
|
||||||
* @author Marcin Roguski (Kaelthas)
|
* @author Marcin Roguski (Kaelthas)
|
||||||
*/
|
*/
|
||||||
public interface ParticleInfluencer extends Savable, Cloneable {
|
public interface ParticleInfluencer extends Savable, Cloneable, JmeCloneable {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method influences the particle.
|
* This method influences the particle.
|
||||||
|
@ -38,6 +38,7 @@ import com.jme3.export.JmeImporter;
|
|||||||
import com.jme3.export.OutputCapsule;
|
import com.jme3.export.OutputCapsule;
|
||||||
import com.jme3.math.FastMath;
|
import com.jme3.math.FastMath;
|
||||||
import com.jme3.math.Vector3f;
|
import com.jme3.math.Vector3f;
|
||||||
|
import com.jme3.util.clone.Cloner;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -119,6 +120,18 @@ public class RadialParticleInfluencer extends DefaultParticleInfluencer {
|
|||||||
this.horizontal = horizontal;
|
this.horizontal = horizontal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called internally by com.jme3.util.clone.Cloner. Do not call directly.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void cloneFields( Cloner cloner, Object original ) {
|
||||||
|
super.cloneFields(cloner, original);
|
||||||
|
|
||||||
|
// Change in behavior: the old origin was not cloned -pspeed
|
||||||
|
this.origin = cloner.clone(origin);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void write(JmeExporter ex) throws IOException {
|
public void write(JmeExporter ex) throws IOException {
|
||||||
super.write(ex);
|
super.write(ex);
|
||||||
|
@ -37,6 +37,8 @@ import com.jme3.export.JmeImporter;
|
|||||||
import com.jme3.export.OutputCapsule;
|
import com.jme3.export.OutputCapsule;
|
||||||
import com.jme3.math.FastMath;
|
import com.jme3.math.FastMath;
|
||||||
import com.jme3.math.Vector3f;
|
import com.jme3.math.Vector3f;
|
||||||
|
import com.jme3.util.clone.Cloner;
|
||||||
|
import com.jme3.util.clone.JmeCloneable;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
public class EmitterBoxShape implements EmitterShape {
|
public class EmitterBoxShape implements EmitterShape {
|
||||||
@ -86,6 +88,27 @@ public class EmitterBoxShape implements EmitterShape {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called internally by com.jme3.util.clone.Cloner. Do not call directly.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Object jmeClone() {
|
||||||
|
try {
|
||||||
|
return super.clone();
|
||||||
|
} catch (CloneNotSupportedException ex) {
|
||||||
|
throw new AssertionError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called internally by com.jme3.util.clone.Cloner. Do not call directly.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void cloneFields( Cloner cloner, Object original ) {
|
||||||
|
this.min = cloner.clone(min);
|
||||||
|
this.len = cloner.clone(len);
|
||||||
|
}
|
||||||
|
|
||||||
public Vector3f getMin() {
|
public Vector3f getMin() {
|
||||||
return min;
|
return min;
|
||||||
}
|
}
|
||||||
|
@ -40,6 +40,8 @@ import com.jme3.math.Vector3f;
|
|||||||
import com.jme3.scene.Mesh;
|
import com.jme3.scene.Mesh;
|
||||||
import com.jme3.scene.VertexBuffer.Type;
|
import com.jme3.scene.VertexBuffer.Type;
|
||||||
import com.jme3.util.BufferUtils;
|
import com.jme3.util.BufferUtils;
|
||||||
|
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;
|
||||||
@ -168,6 +170,27 @@ public class EmitterMeshVertexShape implements EmitterShape {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called internally by com.jme3.util.clone.Cloner. Do not call directly.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Object jmeClone() {
|
||||||
|
try {
|
||||||
|
return super.clone();
|
||||||
|
} catch (CloneNotSupportedException ex) {
|
||||||
|
throw new AssertionError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called internally by com.jme3.util.clone.Cloner. Do not call directly.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void cloneFields( Cloner cloner, Object original ) {
|
||||||
|
this.vertices = cloner.clone(vertices);
|
||||||
|
this.normals = cloner.clone(normals);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void write(JmeExporter ex) throws IOException {
|
public void write(JmeExporter ex) throws IOException {
|
||||||
OutputCapsule oc = ex.getCapsule(this);
|
OutputCapsule oc = ex.getCapsule(this);
|
||||||
|
@ -35,6 +35,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.math.Vector3f;
|
import com.jme3.math.Vector3f;
|
||||||
|
import com.jme3.util.clone.Cloner;
|
||||||
|
import com.jme3.util.clone.JmeCloneable;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
public class EmitterPointShape implements EmitterShape {
|
public class EmitterPointShape implements EmitterShape {
|
||||||
@ -59,6 +61,26 @@ public class EmitterPointShape implements EmitterShape {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called internally by com.jme3.util.clone.Cloner. Do not call directly.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Object jmeClone() {
|
||||||
|
try {
|
||||||
|
return super.clone();
|
||||||
|
} catch (CloneNotSupportedException ex) {
|
||||||
|
throw new AssertionError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called internally by com.jme3.util.clone.Cloner. Do not call directly.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void cloneFields( Cloner cloner, Object original ) {
|
||||||
|
this.point = cloner.clone(point);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void getRandomPoint(Vector3f store) {
|
public void getRandomPoint(Vector3f store) {
|
||||||
store.set(point);
|
store.set(point);
|
||||||
|
@ -33,12 +33,13 @@ package com.jme3.effect.shapes;
|
|||||||
|
|
||||||
import com.jme3.export.Savable;
|
import com.jme3.export.Savable;
|
||||||
import com.jme3.math.Vector3f;
|
import com.jme3.math.Vector3f;
|
||||||
|
import com.jme3.util.clone.JmeCloneable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This interface declares methods used by all shapes that represent particle emitters.
|
* This interface declares methods used by all shapes that represent particle emitters.
|
||||||
* @author Kirill
|
* @author Kirill
|
||||||
*/
|
*/
|
||||||
public interface EmitterShape extends Savable, Cloneable {
|
public interface EmitterShape extends Savable, Cloneable, JmeCloneable {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method fills in the initial position of the particle.
|
* This method fills in the initial position of the particle.
|
||||||
|
@ -37,6 +37,8 @@ import com.jme3.export.JmeImporter;
|
|||||||
import com.jme3.export.OutputCapsule;
|
import com.jme3.export.OutputCapsule;
|
||||||
import com.jme3.math.FastMath;
|
import com.jme3.math.FastMath;
|
||||||
import com.jme3.math.Vector3f;
|
import com.jme3.math.Vector3f;
|
||||||
|
import com.jme3.util.clone.Cloner;
|
||||||
|
import com.jme3.util.clone.JmeCloneable;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
public class EmitterSphereShape implements EmitterShape {
|
public class EmitterSphereShape implements EmitterShape {
|
||||||
@ -71,6 +73,26 @@ public class EmitterSphereShape implements EmitterShape {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called internally by com.jme3.util.clone.Cloner. Do not call directly.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Object jmeClone() {
|
||||||
|
try {
|
||||||
|
return super.clone();
|
||||||
|
} catch (CloneNotSupportedException ex) {
|
||||||
|
throw new AssertionError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called internally by com.jme3.util.clone.Cloner. Do not call directly.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void cloneFields( Cloner cloner, Object original ) {
|
||||||
|
this.center = cloner.clone(center);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void getRandomPoint(Vector3f store) {
|
public void getRandomPoint(Vector3f store) {
|
||||||
do {
|
do {
|
||||||
|
@ -38,6 +38,7 @@ import com.jme3.material.Material;
|
|||||||
import com.jme3.math.ColorRGBA;
|
import com.jme3.math.ColorRGBA;
|
||||||
import com.jme3.renderer.RenderManager;
|
import com.jme3.renderer.RenderManager;
|
||||||
import com.jme3.scene.Node;
|
import com.jme3.scene.Node;
|
||||||
|
import com.jme3.util.clone.Cloner;
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
@ -84,6 +85,27 @@ public class BitmapText extends Node {
|
|||||||
return clone;
|
return clone;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called internally by com.jme3.util.clone.Cloner. Do not call directly.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void cloneFields( Cloner cloner, Object original ) {
|
||||||
|
super.cloneFields(cloner, original);
|
||||||
|
|
||||||
|
for( int i = 0; i < textPages.length; i++ ) {
|
||||||
|
textPages[i] = cloner.clone(textPages[i]);
|
||||||
|
}
|
||||||
|
this.block = cloner.clone(block);
|
||||||
|
|
||||||
|
// Change in behavior: The 'letters' field was not cloned or recreated
|
||||||
|
// before. I'm not sure how this worked and suspect BitmapText was just
|
||||||
|
// not cloneable if you planned to change the text later. -pspeed
|
||||||
|
this.letters = new Letters(font, block, letters.getQuad().isRightToLeft());
|
||||||
|
|
||||||
|
// Just noticed BitmapText is not even writable/readable really...
|
||||||
|
// so I guess cloning doesn't come up that often.
|
||||||
|
}
|
||||||
|
|
||||||
public BitmapFont getFont() {
|
public BitmapFont getFont() {
|
||||||
return font;
|
return font;
|
||||||
}
|
}
|
||||||
|
@ -123,6 +123,13 @@ class BitmapTextPage extends Geometry {
|
|||||||
return clone;
|
return clone;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Here is where one might add JmeCloneable related stuff except
|
||||||
|
// the old clone() method doesn't actually bother to clone anything.
|
||||||
|
// The arrays and the pageQuads are shared across all BitmapTextPage
|
||||||
|
// clones and it doesn't seem to bother anything. That means the
|
||||||
|
// fields could probably just as well be static... but this code is
|
||||||
|
// all very fragile. I'm not tipping that particular boat today. -pspeed
|
||||||
|
|
||||||
void assemble(Letters quads) {
|
void assemble(Letters quads) {
|
||||||
pageQuads.clear();
|
pageQuads.clear();
|
||||||
quads.rewind();
|
quads.rewind();
|
||||||
|
@ -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
|
||||||
|
@ -33,6 +33,8 @@ package com.jme3.light;
|
|||||||
|
|
||||||
import com.jme3.export.*;
|
import com.jme3.export.*;
|
||||||
import com.jme3.scene.Spatial;
|
import com.jme3.scene.Spatial;
|
||||||
|
import com.jme3.util.clone.Cloner;
|
||||||
|
import com.jme3.util.clone.JmeCloneable;
|
||||||
import com.jme3.util.SortUtil;
|
import com.jme3.util.SortUtil;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
@ -43,7 +45,7 @@ import java.util.*;
|
|||||||
*
|
*
|
||||||
* @author Kirill Vainer
|
* @author Kirill Vainer
|
||||||
*/
|
*/
|
||||||
public final class LightList implements Iterable<Light>, Savable, Cloneable {
|
public final class LightList implements Iterable<Light>, Savable, Cloneable, JmeCloneable {
|
||||||
|
|
||||||
private Light[] list, tlist;
|
private Light[] list, tlist;
|
||||||
private float[] distToOwner;
|
private float[] distToOwner;
|
||||||
@ -302,6 +304,24 @@ public final class LightList implements Iterable<Light>, Savable, Cloneable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LightList jmeClone() {
|
||||||
|
try{
|
||||||
|
LightList clone = (LightList)super.clone();
|
||||||
|
clone.tlist = null; // list used for sorting only
|
||||||
|
return clone;
|
||||||
|
}catch (CloneNotSupportedException ex){
|
||||||
|
throw new AssertionError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void cloneFields( Cloner cloner, Object original ) {
|
||||||
|
this.owner = cloner.clone(owner);
|
||||||
|
this.list = cloner.clone(list);
|
||||||
|
this.distToOwner = cloner.clone(distToOwner);
|
||||||
|
}
|
||||||
|
|
||||||
public void write(JmeExporter ex) throws IOException {
|
public void write(JmeExporter ex) throws IOException {
|
||||||
OutputCapsule oc = ex.getCapsule(this);
|
OutputCapsule oc = ex.getCapsule(this);
|
||||||
// oc.write(owner, "owner", null);
|
// oc.write(owner, "owner", null);
|
||||||
|
@ -244,16 +244,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 {
|
||||||
|
@ -311,6 +311,8 @@ public class RenderState implements Cloneable, Savable {
|
|||||||
boolean applyPolyOffset = true;
|
boolean applyPolyOffset = true;
|
||||||
boolean stencilTest = false;
|
boolean stencilTest = false;
|
||||||
boolean applyStencilTest = false;
|
boolean applyStencilTest = false;
|
||||||
|
float lineWidth = 1;
|
||||||
|
boolean applyLineWidth = false;
|
||||||
TestFunction depthFunc = TestFunction.LessOrEqual;
|
TestFunction depthFunc = TestFunction.LessOrEqual;
|
||||||
//by default depth func will be applied anyway if depth test is applied
|
//by default depth func will be applied anyway if depth test is applied
|
||||||
boolean applyDepthFunc = false;
|
boolean applyDepthFunc = false;
|
||||||
@ -350,6 +352,9 @@ public class RenderState implements Cloneable, Savable {
|
|||||||
oc.write(backStencilDepthPassOperation, "backStencilDepthPassOperation", StencilOperation.Keep);
|
oc.write(backStencilDepthPassOperation, "backStencilDepthPassOperation", StencilOperation.Keep);
|
||||||
oc.write(frontStencilFunction, "frontStencilFunction", TestFunction.Always);
|
oc.write(frontStencilFunction, "frontStencilFunction", TestFunction.Always);
|
||||||
oc.write(backStencilFunction, "backStencilFunction", TestFunction.Always);
|
oc.write(backStencilFunction, "backStencilFunction", TestFunction.Always);
|
||||||
|
oc.write(depthFunc, "depthFunc", TestFunction.LessOrEqual);
|
||||||
|
oc.write(alphaFunc, "alphaFunc", TestFunction.Greater);
|
||||||
|
oc.write(lineWidth, "lineWidth", 1);
|
||||||
|
|
||||||
// Only "additional render state" has them set to false by default
|
// Only "additional render state" has them set to false by default
|
||||||
oc.write(applyPointSprite, "applyPointSprite", true);
|
oc.write(applyPointSprite, "applyPointSprite", true);
|
||||||
@ -364,8 +369,7 @@ public class RenderState implements Cloneable, Savable {
|
|||||||
oc.write(applyPolyOffset, "applyPolyOffset", true);
|
oc.write(applyPolyOffset, "applyPolyOffset", true);
|
||||||
oc.write(applyDepthFunc, "applyDepthFunc", true);
|
oc.write(applyDepthFunc, "applyDepthFunc", true);
|
||||||
oc.write(applyAlphaFunc, "applyAlphaFunc", false);
|
oc.write(applyAlphaFunc, "applyAlphaFunc", false);
|
||||||
oc.write(depthFunc, "depthFunc", TestFunction.LessOrEqual);
|
oc.write(applyLineWidth, "applyLineWidth", true);
|
||||||
oc.write(alphaFunc, "alphaFunc", TestFunction.Greater);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -394,6 +398,8 @@ public class RenderState implements Cloneable, Savable {
|
|||||||
backStencilFunction = ic.readEnum("backStencilFunction", TestFunction.class, TestFunction.Always);
|
backStencilFunction = ic.readEnum("backStencilFunction", TestFunction.class, TestFunction.Always);
|
||||||
depthFunc = ic.readEnum("depthFunc", TestFunction.class, TestFunction.LessOrEqual);
|
depthFunc = ic.readEnum("depthFunc", TestFunction.class, TestFunction.LessOrEqual);
|
||||||
alphaFunc = ic.readEnum("alphaFunc", TestFunction.class, TestFunction.Greater);
|
alphaFunc = ic.readEnum("alphaFunc", TestFunction.class, TestFunction.Greater);
|
||||||
|
lineWidth = ic.readFloat("lineWidth", 1);
|
||||||
|
|
||||||
|
|
||||||
applyPointSprite = ic.readBoolean("applyPointSprite", true);
|
applyPointSprite = ic.readBoolean("applyPointSprite", true);
|
||||||
applyWireFrame = ic.readBoolean("applyWireFrame", true);
|
applyWireFrame = ic.readBoolean("applyWireFrame", true);
|
||||||
@ -407,6 +413,8 @@ public class RenderState implements Cloneable, Savable {
|
|||||||
applyPolyOffset = ic.readBoolean("applyPolyOffset", true);
|
applyPolyOffset = ic.readBoolean("applyPolyOffset", true);
|
||||||
applyDepthFunc = ic.readBoolean("applyDepthFunc", true);
|
applyDepthFunc = ic.readBoolean("applyDepthFunc", true);
|
||||||
applyAlphaFunc = ic.readBoolean("applyAlphaFunc", false);
|
applyAlphaFunc = ic.readBoolean("applyAlphaFunc", false);
|
||||||
|
applyLineWidth = ic.readBoolean("applyLineWidth", true);
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -528,6 +536,10 @@ public class RenderState implements Cloneable, Savable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(lineWidth != rs.lineWidth){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -804,7 +816,16 @@ public class RenderState implements Cloneable, Savable {
|
|||||||
cachedHashCode = -1;
|
cachedHashCode = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the mesh line width.
|
||||||
|
* This is to use in conjunction with {@link #setWireframe(boolean)} or with a mesh in {@link Mesh.Mode#Lines} mode.
|
||||||
|
* @param lineWidth the line width.
|
||||||
|
*/
|
||||||
|
public void setLineWidth(float lineWidth) {
|
||||||
|
this.lineWidth = lineWidth;
|
||||||
|
this.applyLineWidth = true;
|
||||||
|
cachedHashCode = -1;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if stencil test is enabled.
|
* Check if stencil test is enabled.
|
||||||
@ -1119,6 +1140,14 @@ public class RenderState implements Cloneable, Savable {
|
|||||||
return alphaFunc;
|
return alphaFunc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* returns the wireframe line width
|
||||||
|
*
|
||||||
|
* @return the line width
|
||||||
|
*/
|
||||||
|
public float getLineWidth() {
|
||||||
|
return lineWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public boolean isApplyAlphaFallOff() {
|
public boolean isApplyAlphaFallOff() {
|
||||||
@ -1169,7 +1198,9 @@ public class RenderState implements Cloneable, Savable {
|
|||||||
return applyAlphaFunc;
|
return applyAlphaFunc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isApplyLineWidth() {
|
||||||
|
return applyLineWidth;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
@ -1200,6 +1231,7 @@ public class RenderState implements Cloneable, Savable {
|
|||||||
hash = 79 * hash + (this.backStencilDepthPassOperation != null ? this.backStencilDepthPassOperation.hashCode() : 0);
|
hash = 79 * hash + (this.backStencilDepthPassOperation != null ? this.backStencilDepthPassOperation.hashCode() : 0);
|
||||||
hash = 79 * hash + (this.frontStencilFunction != null ? this.frontStencilFunction.hashCode() : 0);
|
hash = 79 * hash + (this.frontStencilFunction != null ? this.frontStencilFunction.hashCode() : 0);
|
||||||
hash = 79 * hash + (this.backStencilFunction != null ? this.backStencilFunction.hashCode() : 0);
|
hash = 79 * hash + (this.backStencilFunction != null ? this.backStencilFunction.hashCode() : 0);
|
||||||
|
hash = 79 * hash + Float.floatToIntBits(this.lineWidth);
|
||||||
cachedHashCode = hash;
|
cachedHashCode = hash;
|
||||||
}
|
}
|
||||||
return cachedHashCode;
|
return cachedHashCode;
|
||||||
@ -1324,6 +1356,11 @@ public class RenderState implements Cloneable, Savable {
|
|||||||
state.frontStencilFunction = frontStencilFunction;
|
state.frontStencilFunction = frontStencilFunction;
|
||||||
state.backStencilFunction = backStencilFunction;
|
state.backStencilFunction = backStencilFunction;
|
||||||
}
|
}
|
||||||
|
if (additionalState.applyLineWidth) {
|
||||||
|
state.lineWidth = additionalState.lineWidth;
|
||||||
|
} else {
|
||||||
|
state.lineWidth = lineWidth;
|
||||||
|
}
|
||||||
state.cachedHashCode = -1;
|
state.cachedHashCode = -1;
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
@ -1351,6 +1388,7 @@ public class RenderState implements Cloneable, Savable {
|
|||||||
backStencilFunction = state.backStencilFunction;
|
backStencilFunction = state.backStencilFunction;
|
||||||
depthFunc = state.depthFunc;
|
depthFunc = state.depthFunc;
|
||||||
alphaFunc = state.alphaFunc;
|
alphaFunc = state.alphaFunc;
|
||||||
|
lineWidth = state.lineWidth;
|
||||||
|
|
||||||
applyPointSprite = true;
|
applyPointSprite = true;
|
||||||
applyWireFrame = true;
|
applyWireFrame = true;
|
||||||
@ -1364,6 +1402,7 @@ public class RenderState implements Cloneable, Savable {
|
|||||||
applyPolyOffset = true;
|
applyPolyOffset = true;
|
||||||
applyDepthFunc = true;
|
applyDepthFunc = true;
|
||||||
applyAlphaFunc = false;
|
applyAlphaFunc = false;
|
||||||
|
applyLineWidth = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -1393,6 +1432,7 @@ public class RenderState implements Cloneable, Savable {
|
|||||||
+ "\napplyPolyOffset=" + applyPolyOffset
|
+ "\napplyPolyOffset=" + applyPolyOffset
|
||||||
+ "\noffsetFactor=" + offsetFactor
|
+ "\noffsetFactor=" + offsetFactor
|
||||||
+ "\noffsetUnits=" + offsetUnits
|
+ "\noffsetUnits=" + offsetUnits
|
||||||
|
+ "\nlineWidth=" + lineWidth
|
||||||
+ "\n]";
|
+ "\n]";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -90,7 +90,7 @@ public class Spline implements Savable {
|
|||||||
type = splineType;
|
type = splineType;
|
||||||
this.curveTension = curveTension;
|
this.curveTension = curveTension;
|
||||||
this.cycle = cycle;
|
this.cycle = cycle;
|
||||||
this.computeTotalLentgh();
|
this.computeTotalLength();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -116,7 +116,7 @@ public class Spline implements Savable {
|
|||||||
this.controlPoints.addAll(controlPoints);
|
this.controlPoints.addAll(controlPoints);
|
||||||
this.curveTension = curveTension;
|
this.curveTension = curveTension;
|
||||||
this.cycle = cycle;
|
this.cycle = cycle;
|
||||||
this.computeTotalLentgh();
|
this.computeTotalLength();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -144,7 +144,7 @@ public class Spline implements Savable {
|
|||||||
this.weights[i] = controlPoint.w;
|
this.weights[i] = controlPoint.w;
|
||||||
}
|
}
|
||||||
CurveAndSurfaceMath.prepareNurbsKnots(knots, basisFunctionDegree);
|
CurveAndSurfaceMath.prepareNurbsKnots(knots, basisFunctionDegree);
|
||||||
this.computeTotalLentgh();
|
this.computeTotalLength();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initCatmullRomWayPoints(List<Vector3f> list) {
|
private void initCatmullRomWayPoints(List<Vector3f> list) {
|
||||||
@ -186,7 +186,7 @@ public class Spline implements Savable {
|
|||||||
controlPoints.add(controlPoints.get(0).clone());
|
controlPoints.add(controlPoints.get(0).clone());
|
||||||
}
|
}
|
||||||
if (controlPoints.size() > 1) {
|
if (controlPoints.size() > 1) {
|
||||||
this.computeTotalLentgh();
|
this.computeTotalLength();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -197,7 +197,7 @@ public class Spline implements Savable {
|
|||||||
public void removeControlPoint(Vector3f controlPoint) {
|
public void removeControlPoint(Vector3f controlPoint) {
|
||||||
controlPoints.remove(controlPoint);
|
controlPoints.remove(controlPoint);
|
||||||
if (controlPoints.size() > 1) {
|
if (controlPoints.size() > 1) {
|
||||||
this.computeTotalLentgh();
|
this.computeTotalLength();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -209,7 +209,7 @@ public class Spline implements Savable {
|
|||||||
/**
|
/**
|
||||||
* This method computes the total length of the curve.
|
* This method computes the total length of the curve.
|
||||||
*/
|
*/
|
||||||
private void computeTotalLentgh() {
|
private void computeTotalLength() {
|
||||||
totalLength = 0;
|
totalLength = 0;
|
||||||
float l = 0;
|
float l = 0;
|
||||||
if (segmentsLength == null) {
|
if (segmentsLength == null) {
|
||||||
@ -317,7 +317,7 @@ public class Spline implements Savable {
|
|||||||
public void setCurveTension(float curveTension) {
|
public void setCurveTension(float curveTension) {
|
||||||
this.curveTension = curveTension;
|
this.curveTension = curveTension;
|
||||||
if(type==SplineType.CatmullRom && !getControlPoints().isEmpty()) {
|
if(type==SplineType.CatmullRom && !getControlPoints().isEmpty()) {
|
||||||
this.computeTotalLentgh();
|
this.computeTotalLength();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -342,7 +342,7 @@ public class Spline implements Savable {
|
|||||||
controlPoints.add(controlPoints.get(0));
|
controlPoints.add(controlPoints.get(0));
|
||||||
}
|
}
|
||||||
this.cycle = cycle;
|
this.cycle = cycle;
|
||||||
this.computeTotalLentgh();
|
this.computeTotalLength();
|
||||||
} else {
|
} else {
|
||||||
this.cycle = cycle;
|
this.cycle = cycle;
|
||||||
}
|
}
|
||||||
@ -369,7 +369,7 @@ public class Spline implements Savable {
|
|||||||
*/
|
*/
|
||||||
public void setType(SplineType type) {
|
public void setType(SplineType type) {
|
||||||
this.type = type;
|
this.type = type;
|
||||||
this.computeTotalLentgh();
|
this.computeTotalLength();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -435,9 +435,13 @@ public class Spline implements Savable {
|
|||||||
OutputCapsule oc = ex.getCapsule(this);
|
OutputCapsule oc = ex.getCapsule(this);
|
||||||
oc.writeSavableArrayList((ArrayList) controlPoints, "controlPoints", null);
|
oc.writeSavableArrayList((ArrayList) controlPoints, "controlPoints", null);
|
||||||
oc.write(type, "type", SplineType.CatmullRom);
|
oc.write(type, "type", SplineType.CatmullRom);
|
||||||
float list[] = new float[segmentsLength.size()];
|
|
||||||
for (int i = 0; i < segmentsLength.size(); i++) {
|
float list[] = null;
|
||||||
list[i] = segmentsLength.get(i);
|
if (segmentsLength != null) {
|
||||||
|
list = new float[segmentsLength.size()];
|
||||||
|
for (int i = 0; i < segmentsLength.size(); i++) {
|
||||||
|
list[i] = segmentsLength.get(i);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
oc.write(list, "segmentsLength", null);
|
oc.write(list, "segmentsLength", null);
|
||||||
|
|
||||||
@ -454,7 +458,7 @@ public class Spline implements Savable {
|
|||||||
public void read(JmeImporter im) throws IOException {
|
public void read(JmeImporter im) throws IOException {
|
||||||
InputCapsule in = im.getCapsule(this);
|
InputCapsule in = im.getCapsule(this);
|
||||||
|
|
||||||
controlPoints = (ArrayList<Vector3f>) in.readSavableArrayList("wayPoints", null);
|
controlPoints = (ArrayList<Vector3f>) in.readSavableArrayList("controlPoints", new ArrayList<Vector3f>()); /* Empty List as default, prevents null pointers */
|
||||||
float list[] = in.readFloatArray("segmentsLength", null);
|
float list[] = in.readFloatArray("segmentsLength", null);
|
||||||
if (list != null) {
|
if (list != null) {
|
||||||
segmentsLength = new ArrayList<Float>();
|
segmentsLength = new ArrayList<Float>();
|
||||||
|
@ -402,7 +402,9 @@ public class FilterPostProcessor implements SceneProcessor, Savable {
|
|||||||
viewPort.setOutputFrameBuffer(outputBuffer);
|
viewPort.setOutputFrameBuffer(outputBuffer);
|
||||||
viewPort = null;
|
viewPort = null;
|
||||||
|
|
||||||
renderFrameBuffer.dispose();
|
if(renderFrameBuffer != null){
|
||||||
|
renderFrameBuffer.dispose();
|
||||||
|
}
|
||||||
if(depthTexture!=null){
|
if(depthTexture!=null){
|
||||||
depthTexture.getImage().dispose();
|
depthTexture.getImage().dispose();
|
||||||
}
|
}
|
||||||
|
@ -101,7 +101,7 @@ public class RenderContext {
|
|||||||
public float pointSize = 1;
|
public float pointSize = 1;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see Mesh#setLineWidth(float)
|
* @see RenderState#setLineWidth(float)
|
||||||
*/
|
*/
|
||||||
public float lineWidth = 1;
|
public float lineWidth = 1;
|
||||||
|
|
||||||
|
@ -797,6 +797,10 @@ public final class GLRenderer implements Renderer {
|
|||||||
gl.glDisable(GL.GL_STENCIL_TEST);
|
gl.glDisable(GL.GL_STENCIL_TEST);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (context.lineWidth != state.getLineWidth()) {
|
||||||
|
gl.glLineWidth(state.getLineWidth());
|
||||||
|
context.lineWidth = state.getLineWidth();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private int convertStencilOperation(StencilOperation stencilOp) {
|
private int convertStencilOperation(StencilOperation stencilOp) {
|
||||||
@ -1530,6 +1534,7 @@ public final class GLRenderer implements Renderer {
|
|||||||
updateFrameBufferAttachment(fb, depthBuf);
|
updateFrameBufferAttachment(fb, depthBuf);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
setReadDrawBuffers(fb);
|
setReadDrawBuffers(fb);
|
||||||
checkFrameBufferError();
|
checkFrameBufferError();
|
||||||
|
|
||||||
@ -2074,7 +2079,6 @@ public final class GLRenderer implements Renderer {
|
|||||||
|
|
||||||
// bind texture
|
// bind texture
|
||||||
int target = convertTextureType(type, img.getMultiSamples(), -1);
|
int target = convertTextureType(type, img.getMultiSamples(), -1);
|
||||||
|
|
||||||
bindTextureAndUnit(target, img, unit);
|
bindTextureAndUnit(target, img, unit);
|
||||||
|
|
||||||
if (!img.hasMipmaps() && img.isGeneratedMipmapsRequired()) {
|
if (!img.hasMipmaps() && img.isGeneratedMipmapsRequired()) {
|
||||||
@ -2825,7 +2829,8 @@ public final class GLRenderer implements Renderer {
|
|||||||
throw new RendererException("Mesh instancing is not supported by the video hardware");
|
throw new RendererException("Mesh instancing is not supported by the video hardware");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (context.lineWidth != mesh.getLineWidth()) {
|
//this is kept for backward compatibility.
|
||||||
|
if (mesh.getLineWidth() != -1 && context.lineWidth != mesh.getLineWidth()) {
|
||||||
gl.glLineWidth(mesh.getLineWidth());
|
gl.glLineWidth(mesh.getLineWidth());
|
||||||
context.lineWidth = mesh.getLineWidth();
|
context.lineWidth = mesh.getLineWidth();
|
||||||
}
|
}
|
||||||
|
@ -39,6 +39,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.export.binary.BinaryImporter;
|
import com.jme3.export.binary.BinaryImporter;
|
||||||
|
import com.jme3.util.clone.Cloner;
|
||||||
import com.jme3.util.SafeArrayList;
|
import com.jme3.util.SafeArrayList;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
@ -70,6 +71,20 @@ public class AssetLinkNode extends Node {
|
|||||||
assetLoaderKeys.add(key);
|
assetLoaderKeys.add(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called internally by com.jme3.util.clone.Cloner. Do not call directly.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void cloneFields( Cloner cloner, Object original ) {
|
||||||
|
super.cloneFields(cloner, original);
|
||||||
|
|
||||||
|
// This is a change in behavior because the old version did not clone
|
||||||
|
// this list... changes to one clone would be reflected in all.
|
||||||
|
// I think that's probably undesirable. -pspeed
|
||||||
|
this.assetLoaderKeys = cloner.clone(assetLoaderKeys);
|
||||||
|
this.assetChildren = new HashMap<ModelKey, Spatial>();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a "linked" child. These are loaded from the assetManager when the
|
* Add a "linked" child. These are loaded from the assetManager when the
|
||||||
* AssetLinkNode is loaded from a binary file.
|
* AssetLinkNode is loaded from a binary file.
|
||||||
|
@ -48,6 +48,8 @@ import com.jme3.math.Vector3f;
|
|||||||
import com.jme3.scene.mesh.IndexBuffer;
|
import com.jme3.scene.mesh.IndexBuffer;
|
||||||
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;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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.
|
||||||
@ -383,7 +385,7 @@ public class BatchNode extends GeometryGroupNode {
|
|||||||
maxVertCount = geom.getVertexCount();
|
maxVertCount = geom.getVertexCount();
|
||||||
}
|
}
|
||||||
Mesh.Mode listMode;
|
Mesh.Mode listMode;
|
||||||
float listLineWidth = 1f;
|
//float listLineWidth = 1f;
|
||||||
int components;
|
int components;
|
||||||
switch (geom.getMesh().getMode()) {
|
switch (geom.getMesh().getMode()) {
|
||||||
case Points:
|
case Points:
|
||||||
@ -394,7 +396,7 @@ public class BatchNode extends GeometryGroupNode {
|
|||||||
case LineStrip:
|
case LineStrip:
|
||||||
case Lines:
|
case Lines:
|
||||||
listMode = Mesh.Mode.Lines;
|
listMode = Mesh.Mode.Lines;
|
||||||
listLineWidth = geom.getMesh().getLineWidth();
|
//listLineWidth = geom.getMesh().getLineWidth();
|
||||||
components = 2;
|
components = 2;
|
||||||
break;
|
break;
|
||||||
case TriangleFan:
|
case TriangleFan:
|
||||||
@ -426,19 +428,20 @@ public class BatchNode extends GeometryGroupNode {
|
|||||||
+ " primitive types: " + mode + " != " + listMode);
|
+ " primitive types: " + mode + " != " + listMode);
|
||||||
}
|
}
|
||||||
mode = listMode;
|
mode = listMode;
|
||||||
if (mode == Mesh.Mode.Lines) {
|
//Not needed anymore as lineWidth is now in RenderState and will be taken into account when merging according to the material
|
||||||
if (lineWidth != 1f && listLineWidth != lineWidth) {
|
// if (mode == Mesh.Mode.Lines) {
|
||||||
throw new UnsupportedOperationException("When using Mesh Line mode, cannot combine meshes with different line width "
|
// if (lineWidth != 1f && listLineWidth != lineWidth) {
|
||||||
+ lineWidth + " != " + listLineWidth);
|
// throw new UnsupportedOperationException("When using Mesh Line mode, cannot combine meshes with different line width "
|
||||||
}
|
// + lineWidth + " != " + listLineWidth);
|
||||||
lineWidth = listLineWidth;
|
// }
|
||||||
}
|
// lineWidth = listLineWidth;
|
||||||
|
// }
|
||||||
compsForBuf[VertexBuffer.Type.Index.ordinal()] = components;
|
compsForBuf[VertexBuffer.Type.Index.ordinal()] = components;
|
||||||
}
|
}
|
||||||
|
|
||||||
outMesh.setMaxNumWeights(maxWeights);
|
outMesh.setMaxNumWeights(maxWeights);
|
||||||
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 we can fit all of the meshes
|
// make sure we create an UnsignedInt buffer so we can fit all of the meshes
|
||||||
formatForBuf[VertexBuffer.Type.Index.ordinal()] = VertexBuffer.Format.UnsignedInt;
|
formatForBuf[VertexBuffer.Type.Index.ordinal()] = VertexBuffer.Format.UnsignedInt;
|
||||||
@ -661,7 +664,7 @@ public class BatchNode extends GeometryGroupNode {
|
|||||||
vars.release();
|
vars.release();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected class Batch {
|
protected class Batch implements JmeCloneable {
|
||||||
/**
|
/**
|
||||||
* 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
|
||||||
@ -674,6 +677,25 @@ public class BatchNode extends GeometryGroupNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Geometry geometry;
|
Geometry geometry;
|
||||||
|
|
||||||
|
public final Geometry getGeometry() {
|
||||||
|
return geometry;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Batch jmeClone() {
|
||||||
|
try {
|
||||||
|
return (Batch)super.clone();
|
||||||
|
} catch (CloneNotSupportedException ex) {
|
||||||
|
throw new AssertionError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void cloneFields( Cloner cloner, Object original ) {
|
||||||
|
this.geometry = cloner.clone(geometry);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void setNeedsFullRebatch(boolean needsFullRebatch) {
|
protected void setNeedsFullRebatch(boolean needsFullRebatch) {
|
||||||
@ -700,6 +722,26 @@ public class BatchNode extends GeometryGroupNode {
|
|||||||
return clone;
|
return clone;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called internally by com.jme3.util.clone.Cloner. Do not call directly.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void cloneFields( Cloner cloner, Object original ) {
|
||||||
|
super.cloneFields(cloner, original);
|
||||||
|
|
||||||
|
this.batches = cloner.clone(batches);
|
||||||
|
this.tmpFloat = cloner.clone(tmpFloat);
|
||||||
|
this.tmpFloatN = cloner.clone(tmpFloatN);
|
||||||
|
this.tmpFloatT = cloner.clone(tmpFloatT);
|
||||||
|
|
||||||
|
|
||||||
|
HashMap<Geometry, Batch> newBatchesByGeom = new HashMap<Geometry, Batch>();
|
||||||
|
for( Map.Entry<Geometry, Batch> e : batchesByGeom.entrySet() ) {
|
||||||
|
newBatchesByGeom.put(cloner.clone(e.getKey()), cloner.clone(e.getValue()));
|
||||||
|
}
|
||||||
|
this.batchesByGeom = newBatchesByGeom;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int collideWith(Collidable other, CollisionResults results) {
|
public int collideWith(Collidable other, CollisionResults results) {
|
||||||
int total = 0;
|
int total = 0;
|
||||||
|
@ -36,6 +36,7 @@ import com.jme3.export.JmeImporter;
|
|||||||
import com.jme3.renderer.Camera;
|
import com.jme3.renderer.Camera;
|
||||||
import com.jme3.scene.control.CameraControl;
|
import com.jme3.scene.control.CameraControl;
|
||||||
import com.jme3.scene.control.CameraControl.ControlDirection;
|
import com.jme3.scene.control.CameraControl.ControlDirection;
|
||||||
|
import com.jme3.util.clone.Cloner;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -94,6 +95,19 @@ public class CameraNode extends Node {
|
|||||||
// camControl.getCamera().lookAt(position, upVector);
|
// camControl.getCamera().lookAt(position, upVector);
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called internally by com.jme3.util.clone.Cloner. Do not call directly.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void cloneFields( Cloner cloner, Object original ) {
|
||||||
|
super.cloneFields(cloner, original);
|
||||||
|
|
||||||
|
// A change in behavior... I think previously CameraNode was probably
|
||||||
|
// not really cloneable... or at least its camControl would be pointing
|
||||||
|
// to the wrong control. -pspeed
|
||||||
|
this.camControl = cloner.clone(camControl);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void read(JmeImporter im) throws IOException {
|
public void read(JmeImporter im) throws IOException {
|
||||||
super.read(im);
|
super.read(im);
|
||||||
|
@ -43,6 +43,8 @@ import com.jme3.material.Material;
|
|||||||
import com.jme3.math.Matrix4f;
|
import com.jme3.math.Matrix4f;
|
||||||
import com.jme3.renderer.Camera;
|
import com.jme3.renderer.Camera;
|
||||||
import com.jme3.scene.VertexBuffer.Type;
|
import com.jme3.scene.VertexBuffer.Type;
|
||||||
|
import com.jme3.util.clone.Cloner;
|
||||||
|
import com.jme3.util.clone.IdentityCloneFunction;
|
||||||
import com.jme3.util.TempVars;
|
import com.jme3.util.TempVars;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Queue;
|
import java.util.Queue;
|
||||||
@ -491,6 +493,13 @@ public class Geometry extends Spatial {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public Geometry clone(boolean cloneMaterial) {
|
public Geometry clone(boolean cloneMaterial) {
|
||||||
|
return (Geometry)super.clone(cloneMaterial);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The old clone() method that did not use the new Cloner utility.
|
||||||
|
*/
|
||||||
|
public Geometry oldClone(boolean cloneMaterial) {
|
||||||
Geometry geomClone = (Geometry) super.clone(cloneMaterial);
|
Geometry geomClone = (Geometry) super.clone(cloneMaterial);
|
||||||
|
|
||||||
// This geometry is managed,
|
// This geometry is managed,
|
||||||
@ -534,11 +543,58 @@ public class Geometry extends Spatial {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public Spatial deepClone() {
|
public Spatial deepClone() {
|
||||||
|
return super.deepClone();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Spatial oldDeepClone() {
|
||||||
Geometry geomClone = clone(true);
|
Geometry geomClone = clone(true);
|
||||||
geomClone.mesh = mesh.deepClone();
|
geomClone.mesh = mesh.deepClone();
|
||||||
return geomClone;
|
return geomClone;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called internally by com.jme3.util.clone.Cloner. Do not call directly.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void cloneFields( Cloner cloner, Object original ) {
|
||||||
|
super.cloneFields(cloner, original);
|
||||||
|
|
||||||
|
// If this is a grouped node and if our group node is
|
||||||
|
// also cloned then we'll grab it's reference.
|
||||||
|
if( groupNode != null ) {
|
||||||
|
if( cloner.isCloned(groupNode) ) {
|
||||||
|
// Then resolve the reference
|
||||||
|
this.groupNode = cloner.clone(groupNode);
|
||||||
|
} else {
|
||||||
|
// We are on our own now
|
||||||
|
this.groupNode = null;
|
||||||
|
this.startIndex = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The above is based on the fact that if we were
|
||||||
|
// cloning the hierarchy that contained the parent
|
||||||
|
// group then it would have been shallow cloned before
|
||||||
|
// this child. Can't really be otherwise.
|
||||||
|
}
|
||||||
|
|
||||||
|
this.cachedWorldMat = cloner.clone(cachedWorldMat);
|
||||||
|
|
||||||
|
// See if we are doing a shallow clone or a deep mesh clone
|
||||||
|
boolean shallowClone = (cloner.getCloneFunction(Mesh.class) instanceof IdentityCloneFunction);
|
||||||
|
|
||||||
|
// See if we clone the mesh using the special animation
|
||||||
|
// semi-deep cloning
|
||||||
|
if( shallowClone && mesh != null && mesh.getBuffer(Type.BindPosePosition) != null ) {
|
||||||
|
// Then we need to clone the mesh a little deeper
|
||||||
|
this.mesh = mesh.cloneForAnim();
|
||||||
|
} else {
|
||||||
|
// Do whatever the cloner wants to do about it
|
||||||
|
this.mesh = cloner.clone(mesh);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.material = cloner.clone(material);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void write(JmeExporter ex) throws IOException {
|
public void write(JmeExporter ex) throws IOException {
|
||||||
super.write(ex);
|
super.write(ex);
|
||||||
|
@ -36,6 +36,7 @@ import com.jme3.export.JmeImporter;
|
|||||||
import com.jme3.light.Light;
|
import com.jme3.light.Light;
|
||||||
import com.jme3.scene.control.LightControl;
|
import com.jme3.scene.control.LightControl;
|
||||||
import com.jme3.scene.control.LightControl.ControlDirection;
|
import com.jme3.scene.control.LightControl.ControlDirection;
|
||||||
|
import com.jme3.util.clone.Cloner;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -94,6 +95,19 @@ public class LightNode extends Node {
|
|||||||
return lightControl.getLight();
|
return lightControl.getLight();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called internally by com.jme3.util.clone.Cloner. Do not call directly.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void cloneFields( Cloner cloner, Object original ) {
|
||||||
|
super.cloneFields(cloner, original);
|
||||||
|
|
||||||
|
// A change in behavior... I think previously LightNode was probably
|
||||||
|
// not really cloneable... or at least its lightControl would be pointing
|
||||||
|
// to the wrong control. -pspeed
|
||||||
|
this.lightControl = cloner.clone(lightControl);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void read(JmeImporter im) throws IOException {
|
public void read(JmeImporter im) throws IOException {
|
||||||
super.read(im);
|
super.read(im);
|
||||||
|
@ -37,12 +37,12 @@ import com.jme3.collision.Collidable;
|
|||||||
import com.jme3.collision.CollisionResults;
|
import com.jme3.collision.CollisionResults;
|
||||||
import com.jme3.collision.bih.BIHTree;
|
import com.jme3.collision.bih.BIHTree;
|
||||||
import com.jme3.export.*;
|
import com.jme3.export.*;
|
||||||
|
import com.jme3.material.Material;
|
||||||
import com.jme3.material.RenderState;
|
import com.jme3.material.RenderState;
|
||||||
import com.jme3.math.Matrix4f;
|
import com.jme3.math.Matrix4f;
|
||||||
import com.jme3.math.Triangle;
|
import com.jme3.math.Triangle;
|
||||||
import com.jme3.math.Vector2f;
|
import com.jme3.math.Vector2f;
|
||||||
import com.jme3.math.Vector3f;
|
import com.jme3.math.Vector3f;
|
||||||
import com.jme3.renderer.Renderer;
|
|
||||||
import com.jme3.scene.VertexBuffer.Format;
|
import com.jme3.scene.VertexBuffer.Format;
|
||||||
import com.jme3.scene.VertexBuffer.Type;
|
import com.jme3.scene.VertexBuffer.Type;
|
||||||
import com.jme3.scene.VertexBuffer.Usage;
|
import com.jme3.scene.VertexBuffer.Usage;
|
||||||
@ -50,8 +50,9 @@ import com.jme3.scene.mesh.*;
|
|||||||
import com.jme3.util.BufferUtils;
|
import com.jme3.util.BufferUtils;
|
||||||
import com.jme3.util.IntMap;
|
import com.jme3.util.IntMap;
|
||||||
import com.jme3.util.IntMap.Entry;
|
import com.jme3.util.IntMap.Entry;
|
||||||
import com.jme3.util.NativeObject;
|
|
||||||
import com.jme3.util.SafeArrayList;
|
import com.jme3.util.SafeArrayList;
|
||||||
|
import com.jme3.util.clone.Cloner;
|
||||||
|
import com.jme3.util.clone.JmeCloneable;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.*;
|
import java.nio.*;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@ -67,13 +68,13 @@ import java.util.ArrayList;
|
|||||||
* Points can also be used for {@link RenderState#setPointSprite(boolean) point
|
* Points can also be used for {@link RenderState#setPointSprite(boolean) point
|
||||||
* sprite} mode.</li>
|
* sprite} mode.</li>
|
||||||
* <li>Lines - 2 vertices represent a line segment, with the width specified
|
* <li>Lines - 2 vertices represent a line segment, with the width specified
|
||||||
* via {@link Mesh#setLineWidth(float) }.</li>
|
* via {@link Material#getAdditionalRenderState()} and {@link RenderState#setLineWidth(float)}.</li>
|
||||||
* <li>Triangles - 3 vertices represent a solid triangle primitive. </li>
|
* <li>Triangles - 3 vertices represent a solid triangle primitive. </li>
|
||||||
* </ul>
|
* </ul>
|
||||||
*
|
*
|
||||||
* @author Kirill Vainer
|
* @author Kirill Vainer
|
||||||
*/
|
*/
|
||||||
public class Mesh extends NativeObject implements Savable {
|
public class Mesh extends NativeObject implements Savable, Cloneable, JmeCloneable {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The mode of the Mesh specifies both the type of primitive represented
|
* The mode of the Mesh specifies both the type of primitive represented
|
||||||
@ -88,7 +89,7 @@ public class Mesh extends NativeObject implements Savable {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* A primitive is a line segment. Every two vertices specify
|
* A primitive is a line segment. Every two vertices specify
|
||||||
* a single line. {@link Mesh#setLineWidth(float) } can be used
|
* a single line. {@link Material#getAdditionalRenderState()} and {@link RenderState#setLineWidth(float)} can be used
|
||||||
* to set the width of the lines.
|
* to set the width of the lines.
|
||||||
*/
|
*/
|
||||||
Lines(true),
|
Lines(true),
|
||||||
@ -96,7 +97,7 @@ public class Mesh extends NativeObject implements Savable {
|
|||||||
/**
|
/**
|
||||||
* A primitive is a line segment. The first two vertices specify
|
* A primitive is a line segment. The first two vertices specify
|
||||||
* a single line, while subsequent vertices are combined with the
|
* a single line, while subsequent vertices are combined with the
|
||||||
* previous vertex to make a line. {@link Mesh#setLineWidth(float) } can
|
* previous vertex to make a line. {@link Material#getAdditionalRenderState()} and {@link RenderState#setLineWidth(float)} can
|
||||||
* be used to set the width of the lines.
|
* be used to set the width of the lines.
|
||||||
*/
|
*/
|
||||||
LineStrip(false),
|
LineStrip(false),
|
||||||
@ -104,7 +105,7 @@ public class Mesh extends NativeObject implements Savable {
|
|||||||
/**
|
/**
|
||||||
* Identical to {@link #LineStrip} except that at the end
|
* Identical to {@link #LineStrip} except that at the end
|
||||||
* the last vertex is connected with the first to form a line.
|
* the last vertex is connected with the first to form a line.
|
||||||
* {@link Mesh#setLineWidth(float) } can be used
|
* {@link Material#getAdditionalRenderState()} and {@link RenderState#setLineWidth(float)} can be used
|
||||||
* to set the width of the lines.
|
* to set the width of the lines.
|
||||||
*/
|
*/
|
||||||
LineLoop(false),
|
LineLoop(false),
|
||||||
@ -136,7 +137,6 @@ public class Mesh extends NativeObject implements Savable {
|
|||||||
* for each patch (default is 3 for triangle tesselation)
|
* for each patch (default is 3 for triangle tesselation)
|
||||||
*/
|
*/
|
||||||
Patch(true);
|
Patch(true);
|
||||||
|
|
||||||
private boolean listMode = false;
|
private boolean listMode = false;
|
||||||
|
|
||||||
private Mode(boolean listMode){
|
private Mode(boolean listMode){
|
||||||
@ -169,7 +169,7 @@ public class Mesh extends NativeObject implements Savable {
|
|||||||
private IntMap<VertexBuffer> buffers = new IntMap<VertexBuffer>();
|
private IntMap<VertexBuffer> buffers = new IntMap<VertexBuffer>();
|
||||||
private VertexBuffer[] lodLevels;
|
private VertexBuffer[] lodLevels;
|
||||||
private float pointSize = 1;
|
private float pointSize = 1;
|
||||||
private float lineWidth = 1;
|
private float lineWidth = -1;
|
||||||
|
|
||||||
private transient int vertexArrayID = -1;
|
private transient int vertexArrayID = -1;
|
||||||
|
|
||||||
@ -278,6 +278,34 @@ public class Mesh extends NativeObject implements Savable {
|
|||||||
return clone;
|
return clone;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called internally by com.jme3.util.clone.Cloner. Do not call directly.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Mesh jmeClone() {
|
||||||
|
try {
|
||||||
|
Mesh clone = (Mesh)super.clone();
|
||||||
|
return clone;
|
||||||
|
} catch (CloneNotSupportedException ex) {
|
||||||
|
throw new AssertionError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called internally by com.jme3.util.clone.Cloner. Do not call directly.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void cloneFields( Cloner cloner, Object original ) {
|
||||||
|
|
||||||
|
// Probably could clone this now but it will get regenerated anyway.
|
||||||
|
this.collisionTree = null;
|
||||||
|
|
||||||
|
this.meshBound = cloner.clone(meshBound);
|
||||||
|
this.buffersList = cloner.clone(buffersList);
|
||||||
|
this.buffers = cloner.clone(buffers);
|
||||||
|
this.lodLevels = cloner.clone(lodLevels);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates the {@link Type#BindPosePosition}, {@link Type#BindPoseNormal},
|
* Generates the {@link Type#BindPosePosition}, {@link Type#BindPoseNormal},
|
||||||
* and {@link Type#BindPoseTangent}
|
* and {@link Type#BindPoseTangent}
|
||||||
@ -534,7 +562,9 @@ public class Mesh extends NativeObject implements Savable {
|
|||||||
* Returns the line width for line meshes.
|
* Returns the line width for line meshes.
|
||||||
*
|
*
|
||||||
* @return the line width
|
* @return the line width
|
||||||
|
* @deprecated use {@link Material#getAdditionalRenderState()} and {@link RenderState#getLineWidth()}
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public float getLineWidth() {
|
public float getLineWidth() {
|
||||||
return lineWidth;
|
return lineWidth;
|
||||||
}
|
}
|
||||||
@ -545,7 +575,9 @@ public class Mesh extends NativeObject implements Savable {
|
|||||||
* the default value is 1.0.
|
* the default value is 1.0.
|
||||||
*
|
*
|
||||||
* @param lineWidth The line width
|
* @param lineWidth The line width
|
||||||
|
* @deprecated use {@link Material#getAdditionalRenderState()} and {@link RenderState#setLineWidth(float)}
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public void setLineWidth(float lineWidth) {
|
public void setLineWidth(float lineWidth) {
|
||||||
this.lineWidth = lineWidth;
|
this.lineWidth = lineWidth;
|
||||||
}
|
}
|
||||||
@ -1052,16 +1084,16 @@ public class Mesh extends NativeObject implements Savable {
|
|||||||
*/
|
*/
|
||||||
public IndexBuffer getIndicesAsList(){
|
public IndexBuffer getIndicesAsList(){
|
||||||
IndexBuffer ib = getIndexBuffer();
|
IndexBuffer ib = getIndexBuffer();
|
||||||
if (ib != null) {
|
if (ib != null){
|
||||||
if (mode.isListMode()) {
|
if (mode.isListMode()){
|
||||||
// already in list mode
|
// already in list mode
|
||||||
return ib;
|
return ib;
|
||||||
} else {
|
}else{
|
||||||
// not in list mode but it does have an index buffer
|
// not in list mode but it does have an index buffer
|
||||||
// wrap it so the data is converted to list format
|
// wrap it so the data is converted to list format
|
||||||
return new WrappedIndexBuffer(this);
|
return new WrappedIndexBuffer(this);
|
||||||
}
|
}
|
||||||
} else {
|
}else{
|
||||||
// return a virtual index buffer that will supply
|
// return a virtual index buffer that will supply
|
||||||
// "fake" indices in list format
|
// "fake" indices in list format
|
||||||
return new VirtualIndexBuffer(vertCount, mode);
|
return new VirtualIndexBuffer(vertCount, mode);
|
||||||
@ -1399,7 +1431,7 @@ public class Mesh extends NativeObject implements Savable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//creating hw animation buffers empty so that they are put in the cache
|
//creating hw animation buffers empty so that they are put in the cache
|
||||||
if (isAnimated()) {
|
if(isAnimated()){
|
||||||
VertexBuffer hwBoneIndex = new VertexBuffer(Type.HWBoneIndex);
|
VertexBuffer hwBoneIndex = new VertexBuffer(Type.HWBoneIndex);
|
||||||
hwBoneIndex.setUsage(Usage.CpuOnly);
|
hwBoneIndex.setUsage(Usage.CpuOnly);
|
||||||
setBuffer(hwBoneIndex);
|
setBuffer(hwBoneIndex);
|
||||||
|
@ -40,6 +40,7 @@ import com.jme3.export.Savable;
|
|||||||
import com.jme3.material.Material;
|
import com.jme3.material.Material;
|
||||||
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 java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -366,7 +367,6 @@ public class Node extends Spatial {
|
|||||||
// transform update down the tree-
|
// transform update down the tree-
|
||||||
child.setTransformRefresh();
|
child.setTransformRefresh();
|
||||||
child.setLightListRefresh();
|
child.setLightListRefresh();
|
||||||
child.setMatParamOverrideRefresh();
|
|
||||||
if (logger.isLoggable(Level.FINE)) {
|
if (logger.isLoggable(Level.FINE)) {
|
||||||
logger.log(Level.FINE,"Child ({0}) attached to this node ({1})",
|
logger.log(Level.FINE,"Child ({0}) attached to this node ({1})",
|
||||||
new Object[]{child.getName(), getName()});
|
new Object[]{child.getName(), getName()});
|
||||||
@ -714,7 +714,17 @@ public class Node extends Spatial {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Spatial deepClone(){
|
public Spatial deepClone() {
|
||||||
|
Node nodeClone = (Node)super.deepClone();
|
||||||
|
|
||||||
|
// Reset the fields of the clone that should be in a 'new' state.
|
||||||
|
nodeClone.updateList = null;
|
||||||
|
nodeClone.updateListValid = false; // safe because parent is nulled out in super.clone()
|
||||||
|
|
||||||
|
return nodeClone;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Spatial oldDeepClone(){
|
||||||
Node nodeClone = (Node) super.clone();
|
Node nodeClone = (Node) super.clone();
|
||||||
nodeClone.children = new SafeArrayList<Spatial>(Spatial.class);
|
nodeClone.children = new SafeArrayList<Spatial>(Spatial.class);
|
||||||
for (Spatial child : children){
|
for (Spatial child : children){
|
||||||
@ -725,6 +735,21 @@ public class Node extends Spatial {
|
|||||||
return nodeClone;
|
return nodeClone;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called internally by com.jme3.util.clone.Cloner. Do not call directly.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void cloneFields( Cloner cloner, Object original ) {
|
||||||
|
super.cloneFields(cloner, original);
|
||||||
|
|
||||||
|
this.children = cloner.clone(children);
|
||||||
|
|
||||||
|
// Only the outer cloning thing knows whether this should be nulled
|
||||||
|
// or not... after all, we might be cloning a root node in which case
|
||||||
|
// cloning this list is fine.
|
||||||
|
this.updateList = cloner.clone(updateList);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void write(JmeExporter e) throws IOException {
|
public void write(JmeExporter e) throws IOException {
|
||||||
super.write(e);
|
super.write(e);
|
||||||
|
@ -48,6 +48,9 @@ import com.jme3.renderer.queue.RenderQueue;
|
|||||||
import com.jme3.renderer.queue.RenderQueue.Bucket;
|
import com.jme3.renderer.queue.RenderQueue.Bucket;
|
||||||
import com.jme3.renderer.queue.RenderQueue.ShadowMode;
|
import com.jme3.renderer.queue.RenderQueue.ShadowMode;
|
||||||
import com.jme3.scene.control.Control;
|
import com.jme3.scene.control.Control;
|
||||||
|
import com.jme3.util.clone.Cloner;
|
||||||
|
import com.jme3.util.clone.IdentityCloneFunction;
|
||||||
|
import com.jme3.util.clone.JmeCloneable;
|
||||||
import com.jme3.util.SafeArrayList;
|
import com.jme3.util.SafeArrayList;
|
||||||
import com.jme3.util.TempVars;
|
import com.jme3.util.TempVars;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@ -64,7 +67,7 @@ import java.util.logging.Logger;
|
|||||||
* @author Joshua Slack
|
* @author Joshua Slack
|
||||||
* @version $Revision: 4075 $, $Data$
|
* @version $Revision: 4075 $, $Data$
|
||||||
*/
|
*/
|
||||||
public abstract class Spatial implements Savable, Cloneable, Collidable, CloneableSmartAsset {
|
public abstract class Spatial implements Savable, Cloneable, Collidable, CloneableSmartAsset, JmeCloneable {
|
||||||
|
|
||||||
private static final Logger logger = Logger.getLogger(Spatial.class.getName());
|
private static final Logger logger = Logger.getLogger(Spatial.class.getName());
|
||||||
|
|
||||||
@ -1344,12 +1347,43 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
|
|||||||
* Note that meshes of geometries are not cloned explicitly, they
|
* Note that meshes of geometries are not cloned explicitly, they
|
||||||
* are shared if static, or specially cloned if animated.
|
* are shared if static, or specially cloned if animated.
|
||||||
*
|
*
|
||||||
* All controls will be cloned using the Control.cloneForSpatial method
|
|
||||||
* on the clone.
|
|
||||||
*
|
|
||||||
* @see Mesh#cloneForAnim()
|
* @see Mesh#cloneForAnim()
|
||||||
*/
|
*/
|
||||||
public Spatial clone(boolean cloneMaterial) {
|
public Spatial clone( boolean cloneMaterial ) {
|
||||||
|
|
||||||
|
// Setup the cloner for the type of cloning we want to do.
|
||||||
|
Cloner cloner = new Cloner();
|
||||||
|
|
||||||
|
// First, we definitely do not want to clone our own parent
|
||||||
|
cloner.setClonedValue(parent, null);
|
||||||
|
|
||||||
|
// If we aren't cloning materials then we will make sure those
|
||||||
|
// aren't cloned also
|
||||||
|
if( !cloneMaterial ) {
|
||||||
|
cloner.setCloneFunction(Material.class, new IdentityCloneFunction<Material>());
|
||||||
|
}
|
||||||
|
|
||||||
|
// By default the meshes are not cloned. The geometry
|
||||||
|
// may choose to selectively force them to be cloned but
|
||||||
|
// normally they will be shared
|
||||||
|
cloner.setCloneFunction(Mesh.class, new IdentityCloneFunction<Mesh>());
|
||||||
|
|
||||||
|
// Clone it!
|
||||||
|
Spatial clone = cloner.clone(this);
|
||||||
|
|
||||||
|
// Because we've nulled the parent out we need to make sure
|
||||||
|
// the transforms and stuff get refreshed.
|
||||||
|
clone.setTransformRefresh();
|
||||||
|
clone.setLightListRefresh();
|
||||||
|
clone.setMatParamOverrideRefresh();
|
||||||
|
|
||||||
|
return clone;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The old clone() method that did not use the new Cloner utility.
|
||||||
|
*/
|
||||||
|
public Spatial oldClone(boolean cloneMaterial) {
|
||||||
try {
|
try {
|
||||||
Spatial clone = (Spatial) super.clone();
|
Spatial clone = (Spatial) super.clone();
|
||||||
if (worldBound != null) {
|
if (worldBound != null) {
|
||||||
@ -1433,7 +1467,73 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
|
|||||||
*
|
*
|
||||||
* @see Spatial#clone()
|
* @see Spatial#clone()
|
||||||
*/
|
*/
|
||||||
public abstract Spatial deepClone();
|
public Spatial deepClone() {
|
||||||
|
// Setup the cloner for the type of cloning we want to do.
|
||||||
|
Cloner cloner = new Cloner();
|
||||||
|
|
||||||
|
// First, we definitely do not want to clone our own parent
|
||||||
|
cloner.setClonedValue(parent, null);
|
||||||
|
|
||||||
|
Spatial clone = cloner.clone(this);
|
||||||
|
|
||||||
|
// Because we've nulled the parent out we need to make sure
|
||||||
|
// the transforms and stuff get refreshed.
|
||||||
|
clone.setTransformRefresh();
|
||||||
|
clone.setLightListRefresh();
|
||||||
|
clone.setMatParamOverrideRefresh();
|
||||||
|
|
||||||
|
return clone;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called internally by com.jme3.util.clone.Cloner. Do not call directly.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Spatial jmeClone() {
|
||||||
|
try {
|
||||||
|
Spatial clone = (Spatial)super.clone();
|
||||||
|
return clone;
|
||||||
|
} catch (CloneNotSupportedException ex) {
|
||||||
|
throw new AssertionError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called internally by com.jme3.util.clone.Cloner. Do not call directly.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void cloneFields( Cloner cloner, Object original ) {
|
||||||
|
|
||||||
|
// Clone all of the fields that need fix-ups and/or potential
|
||||||
|
// sharing.
|
||||||
|
this.parent = cloner.clone(parent);
|
||||||
|
this.worldBound = cloner.clone(worldBound);
|
||||||
|
this.worldLights = cloner.clone(worldLights);
|
||||||
|
this.localLights = cloner.clone(localLights);
|
||||||
|
this.worldTransform = cloner.clone(worldTransform);
|
||||||
|
this.localTransform = cloner.clone(localTransform);
|
||||||
|
clone.worldOverrides = cloner.clone(worldOverrides);
|
||||||
|
clone.localOverrides = cloner.clone(localOverrides);
|
||||||
|
this.controls = cloner.clone(controls);
|
||||||
|
|
||||||
|
// Cloner doesn't handle maps on its own just yet.
|
||||||
|
// Note: this is more advanced cloning than the old clone() method
|
||||||
|
// did because it just shallow cloned the map. In this case, we want
|
||||||
|
// to avoid all of the nasty cloneForSpatial() fixup style code that
|
||||||
|
// used to inject stuff into the clone's user data. By using cloner
|
||||||
|
// to clone the user data we get this automatically.
|
||||||
|
if( userData != null ) {
|
||||||
|
userData = (HashMap<String, Savable>)userData.clone();
|
||||||
|
for( Map.Entry<String, Savable> e : userData.entrySet() ) {
|
||||||
|
Savable value = e.getValue();
|
||||||
|
if( value instanceof Cloneable ) {
|
||||||
|
// Note: all JmeCloneable objects are also Cloneable so this
|
||||||
|
// catches both cases.
|
||||||
|
e.setValue(cloner.clone(value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void setUserData(String key, Object data) {
|
public void setUserData(String key, Object data) {
|
||||||
if (userData == null) {
|
if (userData == null) {
|
||||||
|
@ -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;
|
||||||
@ -140,7 +142,16 @@ public class LodControl extends AbstractControl implements Cloneable {
|
|||||||
clone.lastLevel = 0;
|
clone.lastLevel = 0;
|
||||||
clone.numTris = numTris != null ? numTris.clone() : null;
|
clone.numTris = numTris != null ? numTris.clone() : null;
|
||||||
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -47,6 +47,7 @@ import com.jme3.scene.VertexBuffer.Type;
|
|||||||
import com.jme3.scene.VertexBuffer.Usage;
|
import com.jme3.scene.VertexBuffer.Usage;
|
||||||
import com.jme3.util.BufferUtils;
|
import com.jme3.util.BufferUtils;
|
||||||
import com.jme3.util.TempVars;
|
import com.jme3.util.TempVars;
|
||||||
|
import com.jme3.util.clone.Cloner;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.FloatBuffer;
|
import java.nio.FloatBuffer;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@ -343,6 +344,18 @@ public class InstancedGeometry extends Geometry {
|
|||||||
return allData.toArray(new VertexBuffer[allData.size()]);
|
return allData.toArray(new VertexBuffer[allData.size()]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called internally by com.jme3.util.clone.Cloner. Do not call directly.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void cloneFields( Cloner cloner, Object original ) {
|
||||||
|
super.cloneFields(cloner, original);
|
||||||
|
|
||||||
|
this.globalInstanceData = cloner.clone(globalInstanceData);
|
||||||
|
this.transformInstanceData = cloner.clone(transformInstanceData);
|
||||||
|
this.geometries = cloner.clone(geometries);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void write(JmeExporter exporter) throws IOException {
|
public void write(JmeExporter exporter) throws IOException {
|
||||||
super.write(exporter);
|
super.write(exporter);
|
||||||
|
@ -44,8 +44,11 @@ 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;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
public class InstancedNode extends GeometryGroupNode {
|
public class InstancedNode extends GeometryGroupNode {
|
||||||
|
|
||||||
@ -57,7 +60,7 @@ public class InstancedNode extends GeometryGroupNode {
|
|||||||
setGeometryStartIndex(geom, startIndex);
|
setGeometryStartIndex(geom, startIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final class InstanceTypeKey implements Cloneable {
|
private static final class InstanceTypeKey implements Cloneable, JmeCloneable {
|
||||||
|
|
||||||
Mesh mesh;
|
Mesh mesh;
|
||||||
Material material;
|
Material material;
|
||||||
@ -104,9 +107,24 @@ public class InstancedNode extends GeometryGroupNode {
|
|||||||
throw new AssertionError();
|
throw new AssertionError();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object jmeClone() {
|
||||||
|
try {
|
||||||
|
return super.clone();
|
||||||
|
} catch( CloneNotSupportedException e ) {
|
||||||
|
throw new AssertionError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void cloneFields( Cloner cloner, Object original ) {
|
||||||
|
this.mesh = cloner.clone(mesh);
|
||||||
|
this.material = cloner.clone(material);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class InstancedNodeControl implements Control {
|
private static class InstancedNodeControl implements Control, JmeCloneable {
|
||||||
|
|
||||||
private InstancedNode node;
|
private InstancedNode node;
|
||||||
|
|
||||||
@ -124,6 +142,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){
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -313,6 +345,29 @@ public class InstancedNode extends GeometryGroupNode {
|
|||||||
return clone;
|
return clone;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called internally by com.jme3.util.clone.Cloner. Do not call directly.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void cloneFields( Cloner cloner, Object original ) {
|
||||||
|
super.cloneFields(cloner, original);
|
||||||
|
|
||||||
|
this.control = cloner.clone(control);
|
||||||
|
this.lookUp = cloner.clone(lookUp);
|
||||||
|
|
||||||
|
HashMap<Geometry, InstancedGeometry> newIgByGeom = new HashMap<Geometry, InstancedGeometry>();
|
||||||
|
for( Map.Entry<Geometry, InstancedGeometry> e : igByGeom.entrySet() ) {
|
||||||
|
newIgByGeom.put(cloner.clone(e.getKey()), cloner.clone(e.getValue()));
|
||||||
|
}
|
||||||
|
this.igByGeom = newIgByGeom;
|
||||||
|
|
||||||
|
HashMap<InstanceTypeKey, InstancedGeometry> newInstancesMap = new HashMap<InstanceTypeKey, InstancedGeometry>();
|
||||||
|
for( Map.Entry<InstanceTypeKey, InstancedGeometry> e : instancesMap.entrySet() ) {
|
||||||
|
newInstancesMap.put(cloner.clone(e.getKey()), cloner.clone(e.getValue()));
|
||||||
|
}
|
||||||
|
this.instancesMap = newInstancesMap;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onTransformChange(Geometry geom) {
|
public void onTransformChange(Geometry geom) {
|
||||||
// Handled automatically
|
// Handled automatically
|
||||||
|
@ -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
|
||||||
*/
|
*/
|
||||||
@ -587,7 +607,10 @@ public abstract class AbstractShadowRenderer implements SceneProcessor, Savable
|
|||||||
postshadowMat.setTexture(shadowMapStringCache[j], shadowMaps[j]);
|
postshadowMat.setTexture(shadowMapStringCache[j], shadowMaps[j]);
|
||||||
}
|
}
|
||||||
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 {
|
||||||
|
@ -32,6 +32,8 @@
|
|||||||
package com.jme3.util;
|
package com.jme3.util;
|
||||||
|
|
||||||
import com.jme3.util.IntMap.Entry;
|
import com.jme3.util.IntMap.Entry;
|
||||||
|
import com.jme3.util.clone.Cloner;
|
||||||
|
import com.jme3.util.clone.JmeCloneable;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.NoSuchElementException;
|
import java.util.NoSuchElementException;
|
||||||
@ -43,7 +45,7 @@ import java.util.NoSuchElementException;
|
|||||||
*
|
*
|
||||||
* @author Nate
|
* @author Nate
|
||||||
*/
|
*/
|
||||||
public final class IntMap<T> implements Iterable<Entry<T>>, Cloneable {
|
public final class IntMap<T> implements Iterable<Entry<T>>, Cloneable, JmeCloneable {
|
||||||
|
|
||||||
private Entry[] table;
|
private Entry[] table;
|
||||||
private final float loadFactor;
|
private final float loadFactor;
|
||||||
@ -93,6 +95,26 @@ public final class IntMap<T> implements Iterable<Entry<T>>, Cloneable {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called internally by com.jme3.util.clone.Cloner. Do not call directly.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Object jmeClone() {
|
||||||
|
try {
|
||||||
|
return super.clone();
|
||||||
|
} catch (CloneNotSupportedException ex) {
|
||||||
|
throw new AssertionError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called internally by com.jme3.util.clone.Cloner. Do not call directly.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void cloneFields( Cloner cloner, Object original ) {
|
||||||
|
this.table = cloner.clone(table);
|
||||||
|
}
|
||||||
|
|
||||||
public boolean containsValue(Object value) {
|
public boolean containsValue(Object value) {
|
||||||
Entry[] table = this.table;
|
Entry[] table = this.table;
|
||||||
for (int i = table.length; i-- > 0;){
|
for (int i = table.length; i-- > 0;){
|
||||||
@ -268,7 +290,7 @@ public final class IntMap<T> implements Iterable<Entry<T>>, Cloneable {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final class Entry<T> implements Cloneable {
|
public static final class Entry<T> implements Cloneable, JmeCloneable {
|
||||||
|
|
||||||
final int key;
|
final int key;
|
||||||
T value;
|
T value;
|
||||||
@ -303,5 +325,20 @@ public final class IntMap<T> implements Iterable<Entry<T>>, Cloneable {
|
|||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object jmeClone() {
|
||||||
|
try {
|
||||||
|
return super.clone();
|
||||||
|
} catch (CloneNotSupportedException ex) {
|
||||||
|
throw new AssertionError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void cloneFields( Cloner cloner, Object original ) {
|
||||||
|
this.value = cloner.clone(value);
|
||||||
|
this.next = cloner.clone(next);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -68,7 +68,7 @@ import java.util.*;
|
|||||||
* @version $Revision$
|
* @version $Revision$
|
||||||
* @author Paul Speed
|
* @author Paul Speed
|
||||||
*/
|
*/
|
||||||
public class SafeArrayList<E> implements List<E> {
|
public class SafeArrayList<E> implements List<E>, Cloneable {
|
||||||
|
|
||||||
// Implementing List directly to avoid accidentally acquiring
|
// Implementing List directly to avoid accidentally acquiring
|
||||||
// incorrect or non-optimal behavior from AbstractList. For
|
// incorrect or non-optimal behavior from AbstractList. For
|
||||||
@ -97,6 +97,24 @@ public class SafeArrayList<E> implements List<E> {
|
|||||||
addAll(c);
|
addAll(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public SafeArrayList<E> clone() {
|
||||||
|
try {
|
||||||
|
SafeArrayList<E> clone = (SafeArrayList<E>)super.clone();
|
||||||
|
|
||||||
|
// Clone whichever backing store is currently active
|
||||||
|
if( backingArray != null ) {
|
||||||
|
clone.backingArray = backingArray.clone();
|
||||||
|
}
|
||||||
|
if( buffer != null ) {
|
||||||
|
clone.buffer = (List<E>)((ArrayList<E>)buffer).clone();
|
||||||
|
}
|
||||||
|
|
||||||
|
return clone;
|
||||||
|
} catch( CloneNotSupportedException e ) {
|
||||||
|
throw new AssertionError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected final <T> T[] createArray(Class<T> type, int size) {
|
protected final <T> T[] createArray(Class<T> type, int size) {
|
||||||
return (T[])java.lang.reflect.Array.newInstance(type, size);
|
return (T[])java.lang.reflect.Array.newInstance(type, size);
|
||||||
}
|
}
|
||||||
|
@ -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 );
|
||||||
|
|
||||||
|
}
|
412
jme3-core/src/main/java/com/jme3/util/clone/Cloner.java
Normal file
412
jme3-core/src/main/java/com/jme3/util/clone/Cloner.java
Normal file
@ -0,0 +1,412 @@
|
|||||||
|
/*
|
||||||
|
* 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.logging.Logger;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
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 {
|
||||||
|
|
||||||
|
static Logger log = Logger.getLogger(Cloner.class.getName());
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( log.isLoggable(Level.FINER) ) {
|
||||||
|
log.finer("cloning:" + object.getClass() + "@" + System.identityHashCode(object));
|
||||||
|
}
|
||||||
|
|
||||||
|
Class<T> type = objectClass(object);
|
||||||
|
|
||||||
|
// Check the index to see if we already have it
|
||||||
|
Object clone = index.get(object);
|
||||||
|
if( clone != null ) {
|
||||||
|
if( log.isLoggable(Level.FINER) ) {
|
||||||
|
log.finer("cloned:" + object.getClass() + "@" + System.identityHashCode(object)
|
||||||
|
+ " as cached:" + clone.getClass() + "@" + System.identityHashCode(clone));
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
|
||||||
|
if( log.isLoggable(Level.FINER) ) {
|
||||||
|
if( result == null ) {
|
||||||
|
log.finer("cloned:" + object.getClass() + "@" + System.identityHashCode(object)
|
||||||
|
+ " as transformed:null");
|
||||||
|
} else {
|
||||||
|
log.finer("clone:" + object.getClass() + "@" + System.identityHashCode(object)
|
||||||
|
+ " as transformed:" + result.getClass() + "@" + System.identityHashCode(result));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
if( log.isLoggable(Level.FINER) ) {
|
||||||
|
log.finer("cloned:" + object.getClass() + "@" + System.identityHashCode(object)
|
||||||
|
+ " as " + clone.getClass() + "@" + System.identityHashCode(clone));
|
||||||
|
}
|
||||||
|
return type.cast(clone);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets a custom CloneFunction for implementations of the specified Java type. Some
|
||||||
|
* inheritance checks are made but no disambiguation is performed.
|
||||||
|
* <p>Note: in the general case, it is better to register against specific classes and
|
||||||
|
* not super-classes or super-interfaces unless you know specifically that they are cloneable.</p>
|
||||||
|
* <p>By default ListCloneFunction is registered for ArrayList, LinkedList, CopyOnWriteArrayList,
|
||||||
|
* Vector, Stack, and JME's SafeArrayList.</p>
|
||||||
|
*/
|
||||||
|
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 ) {
|
||||||
|
CloneFunction<T> result = (CloneFunction<T>)functions.get(type);
|
||||||
|
if( result == null ) {
|
||||||
|
// Do a more exhaustive search
|
||||||
|
for( Map.Entry<Class, CloneFunction> e : functions.entrySet() ) {
|
||||||
|
if( e.getKey().isAssignableFrom(type) ) {
|
||||||
|
result = e.getValue();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if( result != null ) {
|
||||||
|
// Cache it for later
|
||||||
|
functions.put(type, result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Forces an object to be added to the indexing cache such that attempts
|
||||||
|
* to clone the 'original' will always result in the 'clone' being returned.
|
||||||
|
* This can be used to stub out specific values from being cloned or to
|
||||||
|
* force global shared instances to be used even if the object is cloneable
|
||||||
|
* normally.
|
||||||
|
*/
|
||||||
|
public <T> void setClonedValue( T original, T clone ) {
|
||||||
|
index.put(original, clone);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the specified object has already been cloned
|
||||||
|
* by this cloner during this session. Cloned objects are cached
|
||||||
|
* for later use and it's sometimes convenient to know if some
|
||||||
|
* objects have already been cloned.
|
||||||
|
*/
|
||||||
|
public boolean isCloned( Object o ) {
|
||||||
|
return index.containsKey(o);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,58 @@
|
|||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A CloneFunction implementation that simply returns the
|
||||||
|
* 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 Paul Speed
|
||||||
|
*/
|
||||||
|
public class IdentityCloneFunction<T> implements CloneFunction<T> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the object directly.
|
||||||
|
*/
|
||||||
|
public T cloneObject( Cloner cloner, T object ) {
|
||||||
|
return object;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Does nothing.
|
||||||
|
*/
|
||||||
|
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 );
|
||||||
|
}
|
@ -0,0 +1,70 @@
|
|||||||
|
/*
|
||||||
|
* 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.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A CloneFunction implementation that deep clones a list by
|
||||||
|
* creating a new list and cloning its values using the cloner.
|
||||||
|
*
|
||||||
|
* @author Paul Speed
|
||||||
|
*/
|
||||||
|
public class ListCloneFunction<T extends List> implements CloneFunction<T> {
|
||||||
|
|
||||||
|
public T cloneObject( Cloner cloner, T object ) {
|
||||||
|
try {
|
||||||
|
T clone = cloner.javaClone(object);
|
||||||
|
return clone;
|
||||||
|
} catch( CloneNotSupportedException e ) {
|
||||||
|
throw new IllegalArgumentException("Clone not supported for type:" + object.getClass(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -113,6 +113,8 @@ MaterialDef Phong Lighting {
|
|||||||
|
|
||||||
//For instancing
|
//For instancing
|
||||||
Boolean UseInstancing
|
Boolean UseInstancing
|
||||||
|
|
||||||
|
Boolean BackfaceShadows: false
|
||||||
}
|
}
|
||||||
|
|
||||||
Technique {
|
Technique {
|
||||||
@ -212,26 +214,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 {
|
||||||
@ -245,6 +240,7 @@ MaterialDef Phong Lighting {
|
|||||||
POINTLIGHT : LightViewProjectionMatrix5
|
POINTLIGHT : LightViewProjectionMatrix5
|
||||||
NUM_BONES : NumberOfBones
|
NUM_BONES : NumberOfBones
|
||||||
INSTANCING : UseInstancing
|
INSTANCING : UseInstancing
|
||||||
|
BACKFACE_SHADOWS: BackfaceShadows
|
||||||
}
|
}
|
||||||
|
|
||||||
ForcedRenderState {
|
ForcedRenderState {
|
||||||
@ -263,6 +259,7 @@ MaterialDef Phong Lighting {
|
|||||||
WorldMatrix
|
WorldMatrix
|
||||||
ViewProjectionMatrix
|
ViewProjectionMatrix
|
||||||
ViewMatrix
|
ViewMatrix
|
||||||
|
NormalMatrix
|
||||||
}
|
}
|
||||||
|
|
||||||
Defines {
|
Defines {
|
||||||
|
@ -41,8 +41,7 @@ varying vec3 SpecularSum;
|
|||||||
|
|
||||||
#ifdef NORMALMAP
|
#ifdef NORMALMAP
|
||||||
uniform sampler2D m_NormalMap;
|
uniform sampler2D m_NormalMap;
|
||||||
varying vec3 vTangent;
|
varying vec4 vTangent;
|
||||||
varying vec3 vBinormal;
|
|
||||||
#endif
|
#endif
|
||||||
varying vec3 vNormal;
|
varying vec3 vNormal;
|
||||||
|
|
||||||
@ -71,7 +70,7 @@ uniform float m_Shininess;
|
|||||||
void main(){
|
void main(){
|
||||||
#if !defined(VERTEX_LIGHTING)
|
#if !defined(VERTEX_LIGHTING)
|
||||||
#if defined(NORMALMAP)
|
#if defined(NORMALMAP)
|
||||||
mat3 tbnMat = mat3(normalize(vTangent.xyz) , normalize(vBinormal.xyz) , normalize(vNormal.xyz));
|
mat3 tbnMat = mat3(vTangent.xyz, vTangent.w * cross( (vNormal), (vTangent.xyz)), vNormal.xyz);
|
||||||
|
|
||||||
if (!gl_FrontFacing)
|
if (!gl_FrontFacing)
|
||||||
{
|
{
|
||||||
|
@ -39,8 +39,7 @@ attribute vec3 inNormal;
|
|||||||
varying vec3 vPos;
|
varying vec3 vPos;
|
||||||
#ifdef NORMALMAP
|
#ifdef NORMALMAP
|
||||||
attribute vec4 inTangent;
|
attribute vec4 inTangent;
|
||||||
varying vec3 vTangent;
|
varying vec4 vTangent;
|
||||||
varying vec3 vBinormal;
|
|
||||||
#endif
|
#endif
|
||||||
#else
|
#else
|
||||||
#ifdef COLORRAMP
|
#ifdef COLORRAMP
|
||||||
@ -104,8 +103,7 @@ void main(){
|
|||||||
|
|
||||||
|
|
||||||
#if defined(NORMALMAP) && !defined(VERTEX_LIGHTING)
|
#if defined(NORMALMAP) && !defined(VERTEX_LIGHTING)
|
||||||
vTangent = TransformNormal(modelSpaceTan);
|
vTangent = vec4(TransformNormal(modelSpaceTan).xyz,inTangent.w);
|
||||||
vBinormal = cross(wvNormal, vTangent)* inTangent.w;
|
|
||||||
vNormal = wvNormal;
|
vNormal = wvNormal;
|
||||||
vPos = wvPosition;
|
vPos = wvPosition;
|
||||||
#elif !defined(VERTEX_LIGHTING)
|
#elif !defined(VERTEX_LIGHTING)
|
||||||
|
@ -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
|
#endif
|
||||||
|
|
||||||
|
#ifndef BACKFACE_SHADOWS
|
||||||
|
if(nDotL > 0.0){
|
||||||
|
discard;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
float shadow = 1.0;
|
float shadow = 1.0;
|
||||||
|
|
||||||
#ifdef POINTLIGHT
|
#ifdef POINTLIGHT
|
||||||
@ -70,11 +80,11 @@ void main(){
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#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);
|
|
||||||
|
|
||||||
gl_FragColor = vec4(shadow, shadow, shadow, 1.0);
|
shadow = shadow * m_ShadowIntensity + (1.0 - m_ShadowIntensity);
|
||||||
|
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
|
||||||
|
uniform vec3 m_LightDir;
|
||||||
#ifndef PSSM
|
#ifndef PSSM
|
||||||
uniform vec3 m_LightDir;
|
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,16 +57,17 @@ 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;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef POINTLIGHT
|
#ifndef POINTLIGHT
|
||||||
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,14 +18,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
|
||||||
|
|
||||||
@ -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,11 +72,26 @@ 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;
|
}
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// populate the light view matrices array and convert vertex to light viewProj space
|
// populate the light view matrices array and convert vertex to light viewProj space
|
||||||
|
@ -1,22 +1,57 @@
|
|||||||
#ifdef HARDWARE_SHADOWS
|
#if __VERSION__ >= 130
|
||||||
#define SHADOWMAP sampler2DShadow
|
// Because gpu_shader5 is actually where those
|
||||||
#define SHADOWCOMPARE(tex,coord) shadow2DProj(tex, coord).r
|
// gather functions are declared to work on shadowmaps
|
||||||
|
#extension GL_ARB_gpu_shader5 : enable
|
||||||
|
#define IVEC2 ivec2
|
||||||
|
#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
|
||||||
|
#endif
|
||||||
#else
|
#else
|
||||||
#define SHADOWMAP sampler2D
|
#define IVEC2 vec2
|
||||||
#define SHADOWCOMPARE(tex,coord) step(coord.z, texture2DProj(tex, coord).r)
|
#ifdef HARDWARE_SHADOWS
|
||||||
|
#define SHADOWMAP sampler2DShadow
|
||||||
|
#define SHADOWCOMPARE(tex,coord) shadow2DProj(tex, coord).r
|
||||||
|
#else
|
||||||
|
#define SHADOWMAP sampler2D
|
||||||
|
#define SHADOWCOMPARE(tex,coord) step(coord.z, texture2DProj(tex, coord).r)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if FILTER_MODE == 0
|
||||||
|
#define GETSHADOW Shadow_DoShadowCompare
|
||||||
|
#define KERNEL 1.0
|
||||||
|
#elif FILTER_MODE == 1
|
||||||
|
#ifdef HARDWARE_SHADOWS
|
||||||
|
#define GETSHADOW Shadow_DoShadowCompare
|
||||||
|
#else
|
||||||
|
#define GETSHADOW Shadow_DoBilinear_2x2
|
||||||
|
#endif
|
||||||
|
#define KERNEL 1.0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if FILTER_MODE == 0
|
#if FILTER_MODE == 2
|
||||||
#define GETSHADOW Shadow_DoShadowCompare
|
|
||||||
#define KERNEL 1.0
|
|
||||||
#elif FILTER_MODE == 1
|
|
||||||
#ifdef HARDWARE_SHADOWS
|
|
||||||
#define GETSHADOW Shadow_DoShadowCompare
|
|
||||||
#else
|
|
||||||
#define GETSHADOW Shadow_DoBilinear_2x2
|
|
||||||
#endif
|
|
||||||
#define KERNEL 1.0
|
|
||||||
#elif FILTER_MODE == 2
|
|
||||||
#define GETSHADOW Shadow_DoDither_2x2
|
#define 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);
|
}
|
||||||
gather.x = Shadow_DoShadowCompareOffset(tex, projCoord, vec2(0.0, 0.0));
|
|
||||||
gather.y = Shadow_DoShadowCompareOffset(tex, projCoord, vec2(1.0, 0.0));
|
|
||||||
gather.z = Shadow_DoShadowCompareOffset(tex, projCoord, vec2(0.0, 1.0));
|
|
||||||
gather.w = Shadow_DoShadowCompareOffset(tex, projCoord, vec2(1.0, 1.0));
|
|
||||||
|
|
||||||
vec2 f = fract( projCoord.xy * SHADOWMAP_SIZE );
|
vec4 gather = vec4(0.0);
|
||||||
vec2 mx = mix( gather.xz, gather.yw, f.x );
|
#if __VERSION__ >= 130
|
||||||
return mix( mx.x, mx.y, f.y );
|
#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.y = Shadow_DoShadowCompareOffset(tex, projCoord, vec2(1.0, 0.0));
|
||||||
|
gather.z = Shadow_DoShadowCompareOffset(tex, projCoord, vec2(0.0, 1.0));
|
||||||
|
gather.w = Shadow_DoShadowCompareOffset(tex, projCoord, vec2(1.0, 1.0));
|
||||||
|
#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(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 += SHADOWCOMPARE(tex, vec4(projCoord.xy + poissonDisk0 * texelSize, projCoord.zw));
|
||||||
shadow += Shadow_DoShadowCompareOffset(tex, projCoord , poissonDisk1 * texelSize);
|
shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy + poissonDisk1 * texelSize, projCoord.zw));
|
||||||
shadow += Shadow_DoShadowCompareOffset(tex, projCoord , poissonDisk2 * texelSize);
|
shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy + poissonDisk2 * texelSize, projCoord.zw));
|
||||||
shadow += Shadow_DoShadowCompareOffset(tex, projCoord , poissonDisk3 * texelSize);
|
shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy + poissonDisk3 * texelSize, projCoord.zw));
|
||||||
shadow += Shadow_DoShadowCompareOffset(tex, projCoord , poissonDisk4 * texelSize);
|
shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy + poissonDisk4 * texelSize, projCoord.zw));
|
||||||
shadow += Shadow_DoShadowCompareOffset(tex, projCoord , poissonDisk5 * texelSize);
|
shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy + poissonDisk5 * texelSize, projCoord.zw));
|
||||||
shadow += Shadow_DoShadowCompareOffset(tex, projCoord , poissonDisk6 * texelSize);
|
shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy + poissonDisk6 * texelSize, projCoord.zw));
|
||||||
shadow += Shadow_DoShadowCompareOffset(tex, projCoord , poissonDisk7 * texelSize);
|
shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy + poissonDisk7 * texelSize, projCoord.zw));
|
||||||
shadow += Shadow_DoShadowCompareOffset(tex, projCoord , poissonDisk8 * texelSize);
|
shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy + poissonDisk8 * texelSize, projCoord.zw));
|
||||||
shadow += Shadow_DoShadowCompareOffset(tex, projCoord , poissonDisk9 * texelSize);
|
shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy + poissonDisk9 * texelSize, projCoord.zw));
|
||||||
shadow += Shadow_DoShadowCompareOffset(tex, projCoord , poissonDisk10 * texelSize);
|
shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy + poissonDisk10 * texelSize, projCoord.zw));
|
||||||
shadow += Shadow_DoShadowCompareOffset(tex, projCoord , poissonDisk11 * texelSize);
|
shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy + poissonDisk11 * texelSize, projCoord.zw));
|
||||||
|
|
||||||
shadow = shadow * 0.08333333333;//this is divided by 12
|
//this is divided by 12
|
||||||
return shadow;
|
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
|
|
1
jme3-core/src/main/resources/com/jme3/system/.gitignore
vendored
Normal file
1
jme3-core/src/main/resources/com/jme3/system/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
version.properties
|
@ -1,11 +0,0 @@
|
|||||||
# THIS IS AN AUTO-GENERATED FILE..
|
|
||||||
# DO NOT MODIFY!
|
|
||||||
build.date=1900-01-01
|
|
||||||
git.revision=0
|
|
||||||
git.branch=unknown
|
|
||||||
git.hash=
|
|
||||||
git.hash.short=
|
|
||||||
git.tag=
|
|
||||||
name.full=jMonkeyEngine 3.1.0-UNKNOWN
|
|
||||||
version.number=3.1.0
|
|
||||||
version.tag=SNAPSHOT
|
|
@ -178,9 +178,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) {
|
||||||
@ -196,7 +210,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 ")) {
|
||||||
@ -453,6 +467,8 @@ public class J3MLoader implements AssetLoader {
|
|||||||
renderState.setDepthFunc(RenderState.TestFunction.valueOf(split[1]));
|
renderState.setDepthFunc(RenderState.TestFunction.valueOf(split[1]));
|
||||||
}else if (split[0].equals("AlphaFunc")){
|
}else if (split[0].equals("AlphaFunc")){
|
||||||
renderState.setAlphaFunc(RenderState.TestFunction.valueOf(split[1]));
|
renderState.setAlphaFunc(RenderState.TestFunction.valueOf(split[1]));
|
||||||
|
}else if (split[0].equals("LineWidth")){
|
||||||
|
renderState.setLineWidth(Float.parseFloat(split[1]));
|
||||||
} else {
|
} else {
|
||||||
throw new MatParseException(null, split[0], statement);
|
throw new MatParseException(null, split[0], statement);
|
||||||
}
|
}
|
||||||
@ -679,6 +695,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"
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -50,12 +50,12 @@ import javax.swing.SwingUtilities;
|
|||||||
*/
|
*/
|
||||||
public class AppletHarness extends Applet {
|
public class AppletHarness extends Applet {
|
||||||
|
|
||||||
public static final HashMap<Application, Applet> appToApplet
|
public static final HashMap<LegacyApplication, Applet> appToApplet
|
||||||
= new HashMap<Application, Applet>();
|
= new HashMap<LegacyApplication, Applet>();
|
||||||
|
|
||||||
protected JmeCanvasContext context;
|
protected JmeCanvasContext context;
|
||||||
protected Canvas canvas;
|
protected Canvas canvas;
|
||||||
protected Application app;
|
protected LegacyApplication app;
|
||||||
|
|
||||||
protected String appClass;
|
protected String appClass;
|
||||||
protected URL appCfg = null;
|
protected URL appCfg = null;
|
||||||
@ -103,7 +103,7 @@ public class AppletHarness extends Applet {
|
|||||||
JmeSystem.setLowPermissions(true);
|
JmeSystem.setLowPermissions(true);
|
||||||
|
|
||||||
try{
|
try{
|
||||||
Class<? extends Application> clazz = (Class<? extends Application>) Class.forName(appClass);
|
Class<? extends LegacyApplication> clazz = (Class<? extends LegacyApplication>) Class.forName(appClass);
|
||||||
app = clazz.newInstance();
|
app = clazz.newInstance();
|
||||||
}catch (ClassNotFoundException ex){
|
}catch (ClassNotFoundException ex){
|
||||||
ex.printStackTrace();
|
ex.printStackTrace();
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user