Merge branch 'master' of https://github.com/jMonkeyEngine/jmonkeyengine.git
This commit is contained in:
commit
c07ef80d43
.gitignorecommon.gradle
jme3-android/src/main/java/com/jme3/app
jme3-bullet-native/src/native/cpp
jme3-bullet/src/main/java/com/jme3/bullet
jme3-core/src
main
java/com/jme3
animation
app
asset
audio
cinematic/events
effect
ParticleEmitter.java
influencers
DefaultParticleInfluencer.javaEmptyParticleInfluencer.javaNewtonianParticleInfluencer.javaParticleInfluencer.javaRadialParticleInfluencer.java
shapes
font
light
material
MatParam.javaMatParamOverride.javaMatParamTexture.javaMaterial.javaRenderState.javaTechnique.javaTechniqueDef.java
logic
math
renderer
scene
AssetLinkNode.javaBatchNode.javaCameraNode.javaGeometry.javaLightNode.javaMesh.javaNode.javaSpatial.javaVertexBuffer.java
instancing
shader
DefineList.javaGlsl100ShaderGenerator.javaShader.javaShaderGenerator.javaShaderKey.javaUniform.javaUniformBindingManager.javaVarType.java
system
texture
util
resources
Common/MatDefs
com/jme3/system
plugins/java/com/jme3/material/plugins
test/java/com/jme3
asset
material
math
renderer
scene
shader
system
tools/java/jme3tools/shadercheck
jme3-desktop/src/main/java/com/jme3
jme3-examples
141
.gitignore
vendored
141
.gitignore
vendored
@ -1,34 +1,22 @@
|
||||
**/nbproject/private/
|
||||
/.gradle/
|
||||
/.nb-gradle/private/
|
||||
/.nb-gradle/profiles/private/
|
||||
/.nb-gradle/
|
||||
/.idea/
|
||||
/dist/
|
||||
/build/
|
||||
/bin/
|
||||
/netbeans/
|
||||
/sdk/jdks/local/
|
||||
/jme3-core/build/
|
||||
/.classpath
|
||||
/.project
|
||||
/.settings
|
||||
*.dll
|
||||
*.so
|
||||
*.jnilib
|
||||
*.dylib
|
||||
*.iml
|
||||
.DS_Store
|
||||
/jme3-core/src/main/resources/com/jme3/system/version.properties
|
||||
/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-*/build/
|
||||
/jme3-bullet-native/bullet.zip
|
||||
/jme3-bullet-native/bullet-2.82-r2704/
|
||||
/jme3-android-native/openal-soft/
|
||||
@ -38,112 +26,9 @@
|
||||
/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/stb_image.h
|
||||
/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-project-testdata/release/
|
||||
/sdk/JME3TestsTemplateAndroid/src/jme3test/
|
||||
/sdk/JME3TestsTemplate/src/jme3test/
|
||||
/sdk/build/
|
||||
/sdk/jme3-core-baselibs/release/
|
||||
/sdk/jme3-core-libraries/release/
|
||||
/sdk/jme3-project-baselibs/release/
|
||||
/sdk/jme3-project-libraries/release/
|
||||
/sdk/jme3-codepalette/build/
|
||||
/sdk/jme3-core-libraries/build/
|
||||
/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
|
||||
/sdk/dist/
|
||||
!/jme3-bullet-native/libs/native/windows/x86_64/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_64/libbulletjme.dylib
|
||||
!/jme3-bullet-native/libs/native/linux/x86/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/
|
||||
|
@ -51,6 +51,12 @@ javadoc {
|
||||
}
|
||||
}
|
||||
|
||||
test {
|
||||
testLogging {
|
||||
exceptionFormat = 'full'
|
||||
}
|
||||
}
|
||||
|
||||
task sourcesJar(type: Jar, dependsOn: classes, description: 'Creates a jar from the source files.') {
|
||||
classifier = 'sources'
|
||||
from sourceSets*.allSource
|
||||
|
@ -50,7 +50,7 @@ public class AndroidHarness extends Activity implements TouchListener, DialogInt
|
||||
/**
|
||||
* The jme3 application object
|
||||
*/
|
||||
protected Application app = null;
|
||||
protected LegacyApplication app = null;
|
||||
|
||||
/**
|
||||
* 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 class DataObject {
|
||||
protected Application app = null;
|
||||
protected LegacyApplication app = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -241,7 +241,7 @@ public class AndroidHarness extends Activity implements TouchListener, DialogInt
|
||||
try {
|
||||
if (app == null) {
|
||||
@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();
|
||||
}
|
||||
|
||||
|
@ -207,7 +207,7 @@ public class AndroidHarnessFragment extends Fragment implements
|
||||
protected ImageView splashImageView = null;
|
||||
final private String ESCAPE_EVENT = "TouchEscape";
|
||||
private boolean firstDrawFrame = true;
|
||||
private Application app = null;
|
||||
private LegacyApplication app = null;
|
||||
private int viewWidth = 0;
|
||||
private int viewHeight = 0;
|
||||
|
||||
@ -258,7 +258,7 @@ public class AndroidHarnessFragment extends Fragment implements
|
||||
try {
|
||||
if (app == null) {
|
||||
@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();
|
||||
}
|
||||
|
||||
|
@ -40,6 +40,7 @@ jclass jmeClasses::PhysicsSpace;
|
||||
jmethodID jmeClasses::PhysicsSpace_preTick;
|
||||
jmethodID jmeClasses::PhysicsSpace_postTick;
|
||||
jmethodID jmeClasses::PhysicsSpace_addCollisionEvent;
|
||||
jmethodID jmeClasses::PhysicsSpace_notifyCollisionGroupListeners;
|
||||
|
||||
jclass jmeClasses::PhysicsGhostObject;
|
||||
jmethodID jmeClasses::PhysicsGhostObject_addOverlappingObject;
|
||||
@ -137,6 +138,7 @@ void jmeClasses::initJavaClasses(JNIEnv* env) {
|
||||
PhysicsSpace_preTick = env->GetMethodID(PhysicsSpace, "preTick_native", "(F)V");
|
||||
PhysicsSpace_postTick = env->GetMethodID(PhysicsSpace, "postTick_native", "(F)V");
|
||||
PhysicsSpace_addCollisionEvent = env->GetMethodID(PhysicsSpace, "addCollisionEvent_native","(Lcom/jme3/bullet/collision/PhysicsCollisionObject;Lcom/jme3/bullet/collision/PhysicsCollisionObject;J)V");
|
||||
PhysicsSpace_notifyCollisionGroupListeners = env->GetMethodID(PhysicsSpace, "notifyCollisionGroupListeners_native","(Lcom/jme3/bullet/collision/PhysicsCollisionObject;Lcom/jme3/bullet/collision/PhysicsCollisionObject;)Z");
|
||||
if (env->ExceptionCheck()) {
|
||||
env->Throw(env->ExceptionOccurred());
|
||||
return;
|
||||
|
@ -46,6 +46,7 @@ public:
|
||||
static jmethodID PhysicsSpace_addCollisionEvent;
|
||||
static jclass PhysicsGhostObject;
|
||||
static jmethodID PhysicsGhostObject_addOverlappingObject;
|
||||
static jmethodID PhysicsSpace_notifyCollisionGroupListeners;
|
||||
|
||||
static jclass Vector3f;
|
||||
static jmethodID Vector3f_set;
|
||||
|
@ -187,8 +187,28 @@ void jmePhysicsSpace::createPhysicsSpace(jfloat minX, jfloat minY, jfloat minZ,
|
||||
jmeUserPointer *up0 = (jmeUserPointer*) co0 -> getUserPointer();
|
||||
jmeUserPointer *up1 = (jmeUserPointer*) co1 -> getUserPointer();
|
||||
if (up0 != NULL && up1 != NULL) {
|
||||
collides = (up0->group & up1->groups) != 0;
|
||||
collides = collides && (up1->group & up0->groups);
|
||||
collides = (up0->group & up1->groups) != 0 || (up1->group & up0->groups) != 0;
|
||||
|
||||
if(collides){
|
||||
jmePhysicsSpace *dynamicsWorld = (jmePhysicsSpace *)up0->space;
|
||||
JNIEnv* env = dynamicsWorld->getEnv();
|
||||
jobject javaPhysicsSpace = env->NewLocalRef(dynamicsWorld->getJavaPhysicsSpace());
|
||||
jobject javaCollisionObject0 = env->NewLocalRef(up0->javaCollisionObject);
|
||||
jobject javaCollisionObject1 = env->NewLocalRef(up1->javaCollisionObject);
|
||||
|
||||
jboolean notifyResult = env->CallBooleanMethod(javaPhysicsSpace, jmeClasses::PhysicsSpace_notifyCollisionGroupListeners, javaCollisionObject0, javaCollisionObject1);
|
||||
|
||||
env->DeleteLocalRef(javaPhysicsSpace);
|
||||
env->DeleteLocalRef(javaCollisionObject0);
|
||||
env->DeleteLocalRef(javaCollisionObject1);
|
||||
|
||||
if (env->ExceptionCheck()) {
|
||||
env->Throw(env->ExceptionOccurred());
|
||||
return collides;
|
||||
}
|
||||
|
||||
collides = (bool) notifyResult;
|
||||
}
|
||||
|
||||
//add some additional logic here that modified 'collides'
|
||||
return collides;
|
||||
|
@ -336,6 +336,21 @@ public class PhysicsSpace {
|
||||
collisionEvents.add(eventFactory.getEvent(PhysicsCollisionEvent.TYPE_PROCESSED, node, node1, manifoldPointObjectId));
|
||||
}
|
||||
|
||||
private boolean notifyCollisionGroupListeners_native(PhysicsCollisionObject node, PhysicsCollisionObject node1){
|
||||
PhysicsCollisionGroupListener listener = collisionGroupListeners.get(node.getCollisionGroup());
|
||||
PhysicsCollisionGroupListener listener1 = collisionGroupListeners.get(node1.getCollisionGroup());
|
||||
boolean result = true;
|
||||
|
||||
if(listener != null){
|
||||
result = listener.collide(node, node1);
|
||||
}
|
||||
if(listener1 != null && node.getCollisionGroup() != node1.getCollisionGroup()){
|
||||
result = listener1.collide(node, node1) && result;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* updates the physics space
|
||||
*
|
||||
|
@ -129,11 +129,6 @@ public class EffectTrack implements ClonableTrack {
|
||||
return c;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cloneFields( Cloner cloner, Object original ) {
|
||||
this.spatial = cloner.clone(spatial);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void controlRender(RenderManager rm, ViewPort vp) {
|
||||
}
|
||||
|
@ -204,6 +204,9 @@ public class SkeletonControl extends AbstractControl implements Cloneable, JmeCl
|
||||
* @param skeleton the skeleton
|
||||
*/
|
||||
public SkeletonControl(Skeleton skeleton) {
|
||||
if (skeleton == null) {
|
||||
throw new IllegalArgumentException("skeleton cannot be null");
|
||||
}
|
||||
this.skeleton = skeleton;
|
||||
}
|
||||
|
||||
|
@ -33,85 +33,30 @@ 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.input.InputManager;
|
||||
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>Application</code> class represents an instance of a
|
||||
* real-time 3D rendering jME application.
|
||||
*
|
||||
* An <code>Application</code> provides all the tools that are commonly used in jME3
|
||||
* applications.
|
||||
*
|
||||
* jME3 applications *SHOULD NOT EXTEND* this class but extend {@link com.jme3.app.SimpleApplication} instead.
|
||||
*
|
||||
* The <code>Application</code> interface represents the minimum exposed
|
||||
* capabilities of a concrete jME3 application.
|
||||
*/
|
||||
public class Application implements SystemListener {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(Application.class.getName());
|
||||
|
||||
protected AssetManager assetManager;
|
||||
|
||||
protected AudioRenderer audioRenderer;
|
||||
protected Renderer renderer;
|
||||
protected RenderManager renderManager;
|
||||
protected ViewPort viewPort;
|
||||
protected ViewPort guiViewPort;
|
||||
|
||||
protected JmeContext context;
|
||||
protected AppSettings settings;
|
||||
protected Timer timer = new NanoTimer();
|
||||
protected Camera cam;
|
||||
protected Listener listener;
|
||||
|
||||
protected boolean inputEnabled = true;
|
||||
protected 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();
|
||||
}
|
||||
public interface Application {
|
||||
|
||||
/**
|
||||
* Determine the application's behavior when unfocused.
|
||||
*
|
||||
* @return The lost focus behavior of the application.
|
||||
*/
|
||||
public LostFocusBehavior getLostFocusBehavior() {
|
||||
return lostFocusBehavior;
|
||||
}
|
||||
public LostFocusBehavior getLostFocusBehavior();
|
||||
|
||||
/**
|
||||
* Change the application's behavior when unfocused.
|
||||
@ -125,9 +70,7 @@ public class Application implements SystemListener {
|
||||
*
|
||||
* @see LostFocusBehavior
|
||||
*/
|
||||
public void setLostFocusBehavior(LostFocusBehavior lostFocusBehavior) {
|
||||
this.lostFocusBehavior = lostFocusBehavior;
|
||||
}
|
||||
public void setLostFocusBehavior(LostFocusBehavior lostFocusBehavior);
|
||||
|
||||
/**
|
||||
* Returns true if pause on lost focus is enabled, false otherwise.
|
||||
@ -136,9 +79,7 @@ public class Application implements SystemListener {
|
||||
*
|
||||
* @see #getLostFocusBehavior()
|
||||
*/
|
||||
public boolean isPauseOnLostFocus() {
|
||||
return getLostFocusBehavior() == LostFocusBehavior.PauseOnLostFocus;
|
||||
}
|
||||
public boolean isPauseOnLostFocus();
|
||||
|
||||
/**
|
||||
* Enable or disable pause on lost focus.
|
||||
@ -156,49 +97,7 @@ public class Application implements SystemListener {
|
||||
*
|
||||
* @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 = 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);
|
||||
}
|
||||
}
|
||||
public void setPauseOnLostFocus(boolean pauseOnLostFocus);
|
||||
|
||||
/**
|
||||
* Set the display settings to define the display created.
|
||||
@ -210,321 +109,83 @@ public class Application implements SystemListener {
|
||||
*
|
||||
* @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();
|
||||
}
|
||||
}
|
||||
public void setSettings(AppSettings settings);
|
||||
|
||||
/**
|
||||
* 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;
|
||||
public void setTimer(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());
|
||||
}
|
||||
public Timer getTimer();
|
||||
|
||||
/**
|
||||
* @return The {@link AssetManager asset manager} for this application.
|
||||
*/
|
||||
public AssetManager getAssetManager(){
|
||||
return assetManager;
|
||||
}
|
||||
public AssetManager getAssetManager();
|
||||
|
||||
/**
|
||||
* @return the {@link InputManager input manager}.
|
||||
*/
|
||||
public InputManager getInputManager(){
|
||||
return inputManager;
|
||||
}
|
||||
public InputManager getInputManager();
|
||||
|
||||
/**
|
||||
* @return the {@link AppStateManager app state manager}
|
||||
*/
|
||||
public AppStateManager getStateManager() {
|
||||
return stateManager;
|
||||
}
|
||||
public AppStateManager getStateManager();
|
||||
|
||||
/**
|
||||
* @return the {@link RenderManager render manager}
|
||||
*/
|
||||
public RenderManager getRenderManager() {
|
||||
return renderManager;
|
||||
}
|
||||
public RenderManager getRenderManager();
|
||||
|
||||
/**
|
||||
* @return The {@link Renderer renderer} for the application
|
||||
*/
|
||||
public Renderer getRenderer(){
|
||||
return renderer;
|
||||
}
|
||||
public Renderer getRenderer();
|
||||
|
||||
/**
|
||||
* @return The {@link AudioRenderer audio renderer} for the application
|
||||
*/
|
||||
public AudioRenderer getAudioRenderer() {
|
||||
return audioRenderer;
|
||||
}
|
||||
public AudioRenderer getAudioRenderer();
|
||||
|
||||
/**
|
||||
* @return The {@link Listener listener} object for audio
|
||||
*/
|
||||
public Listener getListener() {
|
||||
return listener;
|
||||
}
|
||||
public Listener getListener();
|
||||
|
||||
/**
|
||||
* @return The {@link JmeContext display context} for the application
|
||||
*/
|
||||
public JmeContext getContext(){
|
||||
return context;
|
||||
}
|
||||
public JmeContext getContext();
|
||||
|
||||
/**
|
||||
* @return The {@link Camera camera} for the application
|
||||
* @return The main {@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);
|
||||
}
|
||||
public Camera getCamera();
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
public void start();
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
public void start(boolean 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);
|
||||
}
|
||||
}
|
||||
public void setAppProfiler(AppProfiler 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);
|
||||
}
|
||||
public AppProfiler getAppProfiler();
|
||||
|
||||
/**
|
||||
* Restarts the context, applying any changed settings.
|
||||
@ -533,10 +194,7 @@ public class Application implements SystemListener {
|
||||
* applied immediately; calling this method forces the context
|
||||
* to restart, applying the new settings.
|
||||
*/
|
||||
public void restart(){
|
||||
context.setSettings(settings);
|
||||
context.restart();
|
||||
}
|
||||
public void restart();
|
||||
|
||||
/**
|
||||
* Requests the context to close, shutting down the main loop
|
||||
@ -546,102 +204,14 @@ public class Application implements SystemListener {
|
||||
*
|
||||
* @see #stop(boolean)
|
||||
*/
|
||||
public void stop(){
|
||||
stop(false);
|
||||
}
|
||||
public void stop();
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
public void stop(boolean waitFor);
|
||||
|
||||
/**
|
||||
* Enqueues a task/callable object to execute in the jME3
|
||||
@ -653,11 +223,7 @@ public class Application implements SystemListener {
|
||||
*
|
||||
* @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;
|
||||
}
|
||||
public <V> Future<V> enqueue(Callable<V> callable);
|
||||
|
||||
/**
|
||||
* Enqueues a runnable object to execute in the jME3
|
||||
@ -669,106 +235,13 @@ public class Application implements SystemListener {
|
||||
*
|
||||
* @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();
|
||||
}
|
||||
public void enqueue(Runnable runnable);
|
||||
|
||||
/**
|
||||
* @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;
|
||||
}
|
||||
|
||||
}
|
||||
public ViewPort getGuiViewPort();
|
||||
|
||||
public ViewPort getViewPort();
|
||||
}
|
||||
|
793
jme3-core/src/main/java/com/jme3/app/LegacyApplication.java
Normal file
793
jme3-core/src/main/java/com/jme3/app/LegacyApplication.java
Normal file
@ -0,0 +1,793 @@
|
||||
/*
|
||||
* 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.AppState;
|
||||
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() {
|
||||
this((AppState[])null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new instance of <code>LegacyApplication</code>, preinitialized
|
||||
* with the specified set of app states.
|
||||
*/
|
||||
public LegacyApplication( AppState... initialStates ) {
|
||||
initStateManager();
|
||||
|
||||
if (initialStates != null) {
|
||||
for (AppState a : initialStates) {
|
||||
if (a != null) {
|
||||
stateManager.attach(a);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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){
|
||||
if (renderManager != null) {
|
||||
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
|
||||
* 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_CAMERA_POS = DebugKeysAppState.INPUT_MAPPING_CAMERA_POS;
|
||||
@ -100,15 +100,7 @@ public abstract class SimpleApplication extends Application {
|
||||
}
|
||||
|
||||
public SimpleApplication( AppState... initialStates ) {
|
||||
super();
|
||||
|
||||
if (initialStates != null) {
|
||||
for (AppState a : initialStates) {
|
||||
if (a != null) {
|
||||
stateManager.attach(a);
|
||||
}
|
||||
}
|
||||
}
|
||||
super(initialStates);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -195,7 +195,7 @@ public class StatsAppState extends AbstractAppState {
|
||||
}
|
||||
|
||||
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.getAdditionalRenderState().setBlendMode(BlendMode.Alpha);
|
||||
|
||||
|
@ -123,7 +123,7 @@ public class StatsView extends Node implements Control, JmeCloneable {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object jmeClone() {
|
||||
public StatsView jmeClone() {
|
||||
throw new UnsupportedOperationException("Not yet implemented.");
|
||||
}
|
||||
|
||||
|
@ -41,9 +41,7 @@ import com.jme3.post.FilterPostProcessor;
|
||||
import com.jme3.renderer.Caps;
|
||||
import com.jme3.scene.Spatial;
|
||||
import com.jme3.scene.plugins.OBJLoader;
|
||||
import com.jme3.shader.Shader;
|
||||
import com.jme3.shader.ShaderGenerator;
|
||||
import com.jme3.shader.ShaderKey;
|
||||
import com.jme3.texture.Texture;
|
||||
import com.jme3.texture.plugins.TGALoader;
|
||||
import java.io.IOException;
|
||||
@ -320,13 +318,6 @@ public interface AssetManager {
|
||||
*/
|
||||
public Material loadMaterial(String name);
|
||||
|
||||
/**
|
||||
* Loads shader file(s), shouldn't be used by end-user in most cases.
|
||||
*
|
||||
* @see AssetManager#loadAsset(com.jme3.asset.AssetKey)
|
||||
*/
|
||||
public Shader loadShader(ShaderKey key);
|
||||
|
||||
/**
|
||||
* Load a font file. Font files are in AngelCode text format,
|
||||
* and are with the extension "fnt".
|
||||
|
@ -32,7 +32,6 @@
|
||||
package com.jme3.asset;
|
||||
|
||||
import com.jme3.asset.cache.AssetCache;
|
||||
import com.jme3.asset.cache.SimpleAssetCache;
|
||||
import com.jme3.audio.AudioData;
|
||||
import com.jme3.audio.AudioKey;
|
||||
import com.jme3.font.BitmapFont;
|
||||
@ -42,9 +41,7 @@ import com.jme3.renderer.Caps;
|
||||
import com.jme3.scene.Spatial;
|
||||
import com.jme3.shader.Glsl100ShaderGenerator;
|
||||
import com.jme3.shader.Glsl150ShaderGenerator;
|
||||
import com.jme3.shader.Shader;
|
||||
import com.jme3.shader.ShaderGenerator;
|
||||
import com.jme3.shader.ShaderKey;
|
||||
import com.jme3.system.JmeSystem;
|
||||
import com.jme3.texture.Texture;
|
||||
import java.io.IOException;
|
||||
@ -431,36 +428,6 @@ public class DesktopAssetManager implements AssetManager {
|
||||
return loadFilter(new FilterKey(name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a vertex/fragment shader combo.
|
||||
*
|
||||
* @param key
|
||||
* @return the loaded {@link Shader}
|
||||
*/
|
||||
public Shader loadShader(ShaderKey key){
|
||||
// cache abuse in method
|
||||
// that doesn't use loaders/locators
|
||||
AssetCache cache = handler.getCache(SimpleAssetCache.class);
|
||||
Shader shader = (Shader) cache.getFromCache(key);
|
||||
if (shader == null){
|
||||
if (key.isUsesShaderNodes()) {
|
||||
if(shaderGenerator == null){
|
||||
throw new UnsupportedOperationException("ShaderGenerator was not initialized, make sure assetManager.getGenerator(caps) has been called");
|
||||
}
|
||||
shader = shaderGenerator.generateShader();
|
||||
} else {
|
||||
shader = new Shader();
|
||||
shader.initialize();
|
||||
for (Shader.ShaderType shaderType : key.getUsedShaderPrograms()) {
|
||||
shader.addSource(shaderType,key.getShaderProgramName(shaderType),(String) loadAsset(new AssetKey(key.getShaderProgramName(shaderType))),key.getDefines().getCompiled(),key.getShaderProgramLanguage(shaderType));
|
||||
}
|
||||
}
|
||||
|
||||
cache.addToCache(key, shader);
|
||||
}
|
||||
return shader;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
|
@ -41,6 +41,7 @@ import com.jme3.export.OutputCapsule;
|
||||
import com.jme3.math.Vector3f;
|
||||
import com.jme3.scene.Node;
|
||||
import com.jme3.util.PlaceholderAssets;
|
||||
import com.jme3.util.clone.Cloner;
|
||||
import java.io.IOException;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
@ -724,6 +725,30 @@ public class AudioNode extends Node implements AudioSource {
|
||||
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
|
||||
public void write(JmeExporter ex) throws IOException {
|
||||
super.write(ex);
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2012 jMonkeyEngine
|
||||
* Copyright (c) 2009-2016 jMonkeyEngine
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@ -64,9 +64,9 @@ public class MotionEvent extends AbstractCinematicEvent implements Control, JmeC
|
||||
protected int currentWayPoint;
|
||||
protected float currentValue;
|
||||
protected Vector3f direction = new Vector3f();
|
||||
protected Vector3f lookAt;
|
||||
protected Vector3f lookAt = null;
|
||||
protected Vector3f upVector = Vector3f.UNIT_Y;
|
||||
protected Quaternion rotation;
|
||||
protected Quaternion rotation = null;
|
||||
protected Direction directionType = Direction.None;
|
||||
protected MotionPath path;
|
||||
private boolean isControl = true;
|
||||
@ -120,7 +120,6 @@ public class MotionEvent extends AbstractCinematicEvent implements Control, JmeC
|
||||
*/
|
||||
public MotionEvent(Spatial spatial, MotionPath path) {
|
||||
super();
|
||||
this.spatial = spatial;
|
||||
spatial.addControl(this);
|
||||
this.path = path;
|
||||
}
|
||||
@ -132,7 +131,6 @@ public class MotionEvent extends AbstractCinematicEvent implements Control, JmeC
|
||||
*/
|
||||
public MotionEvent(Spatial spatial, MotionPath path, float initialDuration) {
|
||||
super(initialDuration);
|
||||
this.spatial = spatial;
|
||||
spatial.addControl(this);
|
||||
this.path = path;
|
||||
}
|
||||
@ -144,7 +142,6 @@ public class MotionEvent extends AbstractCinematicEvent implements Control, JmeC
|
||||
*/
|
||||
public MotionEvent(Spatial spatial, MotionPath path, LoopMode loopMode) {
|
||||
super();
|
||||
this.spatial = spatial;
|
||||
spatial.addControl(this);
|
||||
this.path = path;
|
||||
this.loopMode = loopMode;
|
||||
@ -157,7 +154,6 @@ public class MotionEvent extends AbstractCinematicEvent implements Control, JmeC
|
||||
*/
|
||||
public MotionEvent(Spatial spatial, MotionPath path, float initialDuration, LoopMode loopMode) {
|
||||
super(initialDuration);
|
||||
this.spatial = spatial;
|
||||
spatial.addControl(this);
|
||||
this.path = path;
|
||||
this.loopMode = loopMode;
|
||||
@ -213,9 +209,9 @@ public class MotionEvent extends AbstractCinematicEvent implements Control, JmeC
|
||||
public void write(JmeExporter ex) throws IOException {
|
||||
super.write(ex);
|
||||
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(rotation, "rotation", Quaternion.IDENTITY);
|
||||
oc.write(rotation, "rotation", null);
|
||||
oc.write(directionType, "directionType", Direction.None);
|
||||
oc.write(path, "path", null);
|
||||
}
|
||||
@ -224,9 +220,9 @@ public class MotionEvent extends AbstractCinematicEvent implements Control, JmeC
|
||||
public void read(JmeImporter im) throws IOException {
|
||||
super.read(im);
|
||||
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);
|
||||
rotation = (Quaternion) in.readSavable("rotation", Quaternion.IDENTITY);
|
||||
rotation = (Quaternion) in.readSavable("rotation", null);
|
||||
directionType = in.readEnum("directionType", Direction.class, Direction.None);
|
||||
path = (MotionPath) in.readSavable("path", null);
|
||||
}
|
||||
@ -278,14 +274,15 @@ public class MotionEvent extends AbstractCinematicEvent implements Control, JmeC
|
||||
*/
|
||||
@Override
|
||||
public Control cloneForSpatial(Spatial spatial) {
|
||||
MotionEvent control = new MotionEvent(spatial, path);
|
||||
MotionEvent control = new MotionEvent();
|
||||
control.setPath(path);
|
||||
control.playState = playState;
|
||||
control.currentWayPoint = currentWayPoint;
|
||||
control.currentValue = currentValue;
|
||||
control.direction = direction.clone();
|
||||
control.lookAt = lookAt.clone();
|
||||
control.lookAt = lookAt;
|
||||
control.upVector = upVector.clone();
|
||||
control.rotation = rotation.clone();
|
||||
control.rotation = rotation;
|
||||
control.initialDuration = initialDuration;
|
||||
control.speed = speed;
|
||||
control.loopMode = loopMode;
|
||||
@ -302,9 +299,9 @@ public class MotionEvent extends AbstractCinematicEvent implements Control, JmeC
|
||||
control.currentWayPoint = currentWayPoint;
|
||||
control.currentValue = currentValue;
|
||||
control.direction = direction.clone();
|
||||
control.lookAt = lookAt.clone();
|
||||
control.lookAt = lookAt;
|
||||
control.upVector = upVector.clone();
|
||||
control.rotation = rotation.clone();
|
||||
control.rotation = rotation;
|
||||
control.initialDuration = initialDuration;
|
||||
control.speed = speed;
|
||||
control.loopMode = loopMode;
|
||||
|
@ -174,6 +174,13 @@ public class ParticleEmitter extends Geometry {
|
||||
|
||||
@Override
|
||||
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);
|
||||
clone.shape = shape.deepClone();
|
||||
|
||||
@ -211,6 +218,44 @@ public class ParticleEmitter extends Geometry {
|
||||
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) {
|
||||
super(name);
|
||||
setBatchHint(BatchHint.Never);
|
||||
|
@ -39,6 +39,8 @@ import com.jme3.export.JmeImporter;
|
||||
import com.jme3.export.OutputCapsule;
|
||||
import com.jme3.math.FastMath;
|
||||
import com.jme3.math.Vector3f;
|
||||
import com.jme3.util.clone.Cloner;
|
||||
import com.jme3.util.clone.JmeCloneable;
|
||||
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
|
||||
public void setInitialVelocity(Vector3f initialVelocity) {
|
||||
this.initialVelocity.set(initialVelocity);
|
||||
|
@ -36,6 +36,8 @@ import com.jme3.effect.shapes.EmitterShape;
|
||||
import com.jme3.export.JmeExporter;
|
||||
import com.jme3.export.JmeImporter;
|
||||
import com.jme3.math.Vector3f;
|
||||
import com.jme3.util.clone.Cloner;
|
||||
import com.jme3.util.clone.JmeCloneable;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
@ -83,4 +85,23 @@ public class EmptyParticleInfluencer implements ParticleInfluencer {
|
||||
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.math.FastMath;
|
||||
import com.jme3.math.Matrix3f;
|
||||
import com.jme3.util.clone.Cloner;
|
||||
import com.jme3.util.clone.JmeCloneable;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
|
@ -36,12 +36,13 @@ import com.jme3.effect.ParticleEmitter;
|
||||
import com.jme3.effect.shapes.EmitterShape;
|
||||
import com.jme3.export.Savable;
|
||||
import com.jme3.math.Vector3f;
|
||||
import com.jme3.util.clone.JmeCloneable;
|
||||
|
||||
/**
|
||||
* An interface that defines the methods to affect initial velocity of the particles.
|
||||
* @author Marcin Roguski (Kaelthas)
|
||||
*/
|
||||
public interface ParticleInfluencer extends Savable, Cloneable {
|
||||
public interface ParticleInfluencer extends Savable, Cloneable, JmeCloneable {
|
||||
|
||||
/**
|
||||
* This method influences the particle.
|
||||
|
@ -38,6 +38,7 @@ import com.jme3.export.JmeImporter;
|
||||
import com.jme3.export.OutputCapsule;
|
||||
import com.jme3.math.FastMath;
|
||||
import com.jme3.math.Vector3f;
|
||||
import com.jme3.util.clone.Cloner;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
@ -119,6 +120,18 @@ public class RadialParticleInfluencer extends DefaultParticleInfluencer {
|
||||
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
|
||||
public void write(JmeExporter ex) throws IOException {
|
||||
super.write(ex);
|
||||
|
@ -37,6 +37,8 @@ import com.jme3.export.JmeImporter;
|
||||
import com.jme3.export.OutputCapsule;
|
||||
import com.jme3.math.FastMath;
|
||||
import com.jme3.math.Vector3f;
|
||||
import com.jme3.util.clone.Cloner;
|
||||
import com.jme3.util.clone.JmeCloneable;
|
||||
import java.io.IOException;
|
||||
|
||||
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() {
|
||||
return min;
|
||||
}
|
||||
|
@ -40,6 +40,8 @@ import com.jme3.math.Vector3f;
|
||||
import com.jme3.scene.Mesh;
|
||||
import com.jme3.scene.VertexBuffer.Type;
|
||||
import com.jme3.util.BufferUtils;
|
||||
import com.jme3.util.clone.Cloner;
|
||||
import com.jme3.util.clone.JmeCloneable;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
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
|
||||
public void write(JmeExporter ex) throws IOException {
|
||||
OutputCapsule oc = ex.getCapsule(this);
|
||||
|
@ -35,6 +35,8 @@ import com.jme3.export.JmeExporter;
|
||||
import com.jme3.export.JmeImporter;
|
||||
import com.jme3.export.OutputCapsule;
|
||||
import com.jme3.math.Vector3f;
|
||||
import com.jme3.util.clone.Cloner;
|
||||
import com.jme3.util.clone.JmeCloneable;
|
||||
import java.io.IOException;
|
||||
|
||||
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
|
||||
public void getRandomPoint(Vector3f store) {
|
||||
store.set(point);
|
||||
|
@ -33,12 +33,13 @@ package com.jme3.effect.shapes;
|
||||
|
||||
import com.jme3.export.Savable;
|
||||
import com.jme3.math.Vector3f;
|
||||
import com.jme3.util.clone.JmeCloneable;
|
||||
|
||||
/**
|
||||
* This interface declares methods used by all shapes that represent particle emitters.
|
||||
* @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.
|
||||
|
@ -37,6 +37,8 @@ import com.jme3.export.JmeImporter;
|
||||
import com.jme3.export.OutputCapsule;
|
||||
import com.jme3.math.FastMath;
|
||||
import com.jme3.math.Vector3f;
|
||||
import com.jme3.util.clone.Cloner;
|
||||
import com.jme3.util.clone.JmeCloneable;
|
||||
import java.io.IOException;
|
||||
|
||||
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
|
||||
public void getRandomPoint(Vector3f store) {
|
||||
do {
|
||||
|
@ -38,6 +38,7 @@ import com.jme3.material.Material;
|
||||
import com.jme3.math.ColorRGBA;
|
||||
import com.jme3.renderer.RenderManager;
|
||||
import com.jme3.scene.Node;
|
||||
import com.jme3.util.clone.Cloner;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@ -84,6 +85,27 @@ public class BitmapText extends Node {
|
||||
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() {
|
||||
return font;
|
||||
}
|
||||
|
@ -123,6 +123,13 @@ class BitmapTextPage extends Geometry {
|
||||
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) {
|
||||
pageQuads.clear();
|
||||
quads.rewind();
|
||||
|
@ -184,6 +184,22 @@ public abstract class Light implements Savable, Cloneable {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
public boolean isFrustumCheckNeeded() {
|
||||
return frustumCheckNeeded;
|
||||
}
|
||||
|
||||
public void setFrustumCheckNeeded(boolean frustumCheckNeeded) {
|
||||
this.frustumCheckNeeded = frustumCheckNeeded;
|
||||
}
|
||||
|
||||
public boolean isIntersectsFrustum() {
|
||||
return intersectsFrustum;
|
||||
}
|
||||
|
||||
public void setIntersectsFrustum(boolean intersectsFrustum) {
|
||||
this.intersectsFrustum = intersectsFrustum;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the light intersects with the given bounding box.
|
||||
* <p>
|
||||
|
@ -33,6 +33,8 @@ package com.jme3.light;
|
||||
|
||||
import com.jme3.export.*;
|
||||
import com.jme3.scene.Spatial;
|
||||
import com.jme3.util.clone.Cloner;
|
||||
import com.jme3.util.clone.JmeCloneable;
|
||||
import com.jme3.util.SortUtil;
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
@ -43,7 +45,7 @@ import java.util.*;
|
||||
*
|
||||
* @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 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 {
|
||||
OutputCapsule oc = ex.getCapsule(this);
|
||||
// oc.write(owner, "owner", null);
|
||||
|
@ -34,7 +34,6 @@ package com.jme3.material;
|
||||
import com.jme3.asset.TextureKey;
|
||||
import com.jme3.export.*;
|
||||
import com.jme3.math.*;
|
||||
import com.jme3.renderer.Renderer;
|
||||
import com.jme3.shader.VarType;
|
||||
import com.jme3.texture.Texture;
|
||||
import com.jme3.texture.Texture.WrapMode;
|
||||
@ -129,9 +128,6 @@ public class MatParam implements Savable, Cloneable {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
void apply(Renderer r, Technique technique) {
|
||||
technique.updateUniformParam(getPrefixedName(), getVarType(), getValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the material parameter value as it would appear in a J3M
|
||||
|
151
jme3-core/src/main/java/com/jme3/material/MatParamOverride.java
Normal file
151
jme3-core/src/main/java/com/jme3/material/MatParamOverride.java
Normal file
@ -0,0 +1,151 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2016 jMonkeyEngine
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package com.jme3.material;
|
||||
|
||||
import com.jme3.export.InputCapsule;
|
||||
import com.jme3.export.JmeExporter;
|
||||
import com.jme3.export.JmeImporter;
|
||||
import com.jme3.export.OutputCapsule;
|
||||
import com.jme3.scene.Spatial;
|
||||
import com.jme3.shader.VarType;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* <code>MatParamOverride</code> is a mechanism by which
|
||||
* {@link MatParam material parameters} can be overridden on the scene graph.
|
||||
* <p>
|
||||
* A scene branch which has a <code>MatParamOverride</code> applied to it will
|
||||
* cause all material parameters with the same name and type to have their value
|
||||
* replaced with the value set on the <code>MatParamOverride</code>. If those
|
||||
* parameters are mapped to a define, then the define will be overridden as well
|
||||
* using the same rules as the ones used for regular material parameters.
|
||||
* <p>
|
||||
* <code>MatParamOverrides</code> are applied to a {@link Spatial} via the
|
||||
* {@link Spatial#addMatParamOverride(com.jme3.material.MatParamOverride)}
|
||||
* method. They are propagated to child <code>Spatials</code> via
|
||||
* {@link Spatial#updateGeometricState()} similar to how lights are propagated.
|
||||
* <p>
|
||||
* Example:<br>
|
||||
* <pre>
|
||||
* {@code
|
||||
*
|
||||
* Geometry box = new Geometry("Box", new Box(1,1,1));
|
||||
* Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
|
||||
* mat.setColor("Color", ColorRGBA.Blue);
|
||||
* box.setMaterial(mat);
|
||||
* rootNode.attachChild(box);
|
||||
*
|
||||
* // ... later ...
|
||||
* MatParamOverride override = new MatParamOverride(Type.Vector4, "Color", ColorRGBA.Red);
|
||||
* rootNode.addMatParamOverride(override);
|
||||
*
|
||||
* // After adding the override to the root node, the box becomes red.
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* @author Kirill Vainer
|
||||
* @see Spatial#addMatParamOverride(com.jme3.material.MatParamOverride)
|
||||
* @see Spatial#getWorldMatParamOverrides()
|
||||
*/
|
||||
public final class MatParamOverride extends MatParam {
|
||||
|
||||
private boolean enabled = true;
|
||||
|
||||
/**
|
||||
* Serialization only. Do not use.
|
||||
*/
|
||||
public MatParamOverride() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new <code>MatParamOverride</code>.
|
||||
*
|
||||
* Overrides are created enabled by default.
|
||||
*
|
||||
* @param type The type of parameter.
|
||||
* @param name The name of the parameter.
|
||||
* @param value The value to set the material parameter to.
|
||||
*/
|
||||
public MatParamOverride(VarType type, String name, Object value) {
|
||||
super(type, name, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
return super.equals(obj) && this.enabled == ((MatParamOverride) obj).enabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hash = super.hashCode();
|
||||
hash = 59 * hash + (enabled ? 1 : 0);
|
||||
return hash;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the <code>MatParamOverride</code> is enabled or disabled.
|
||||
*
|
||||
* @return true if enabled, false if disabled.
|
||||
* @see #setEnabled(boolean)
|
||||
*/
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable or disable this <code>MatParamOverride</code>.
|
||||
*
|
||||
* When disabled, the override will continue to propagate through the scene
|
||||
* graph like before, but it will have no effect on materials. Overrides are
|
||||
* enabled by default.
|
||||
*
|
||||
* @param enabled Whether to enable or disable this override.
|
||||
*/
|
||||
public void setEnabled(boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(JmeExporter ex) throws IOException {
|
||||
super.write(ex);
|
||||
OutputCapsule oc = ex.getCapsule(this);
|
||||
oc.write(enabled, "enabled", true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(JmeImporter im) throws IOException {
|
||||
super.read(im);
|
||||
InputCapsule ic = im.getCapsule(this);
|
||||
enabled = ic.readBoolean("enabled", true);
|
||||
}
|
||||
}
|
@ -100,12 +100,6 @@ public class MatParamTexture extends MatParam {
|
||||
return unit;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void apply(Renderer r, Technique technique) {
|
||||
TechniqueDef techDef = technique.getDef();
|
||||
r.setTexture(getUnit(), getTextureValue());
|
||||
technique.updateUniformParam(getPrefixedName(), getVarType(), getUnit());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(JmeExporter ex) throws IOException {
|
||||
|
@ -44,18 +44,16 @@ import com.jme3.math.*;
|
||||
import com.jme3.renderer.Caps;
|
||||
import com.jme3.renderer.RenderManager;
|
||||
import com.jme3.renderer.Renderer;
|
||||
import com.jme3.renderer.RendererException;
|
||||
import com.jme3.renderer.queue.RenderQueue.Bucket;
|
||||
import com.jme3.scene.Geometry;
|
||||
import com.jme3.scene.Mesh;
|
||||
import com.jme3.scene.instancing.InstancedGeometry;
|
||||
import com.jme3.shader.Shader;
|
||||
import com.jme3.shader.Uniform;
|
||||
import com.jme3.shader.UniformBindingManager;
|
||||
import com.jme3.shader.VarType;
|
||||
import com.jme3.texture.Image;
|
||||
import com.jme3.texture.Texture;
|
||||
import com.jme3.texture.image.ColorSpace;
|
||||
import com.jme3.util.ListMap;
|
||||
import com.jme3.util.TempVars;
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
import java.util.logging.Level;
|
||||
@ -79,7 +77,6 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
|
||||
private static final Logger logger = Logger.getLogger(Material.class.getName());
|
||||
private static final RenderState additiveLight = new RenderState();
|
||||
private static final RenderState depthOnly = new RenderState();
|
||||
private static final Quaternion nullDirLight = new Quaternion(0, -1, 0, -1);
|
||||
|
||||
static {
|
||||
depthOnly.setDepthTest(true);
|
||||
@ -175,22 +172,29 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
|
||||
* @return The sorting ID used for sorting geometries for rendering.
|
||||
*/
|
||||
public int getSortId() {
|
||||
Technique t = getActiveTechnique();
|
||||
if (sortingId == -1 && t != null && t.getShader() != null) {
|
||||
int texId = -1;
|
||||
if (sortingId == -1 && technique != null) {
|
||||
sortingId = technique.getSortId() << 16;
|
||||
int texturesSortId = 17;
|
||||
for (int i = 0; i < paramValues.size(); i++) {
|
||||
MatParam param = paramValues.getValue(i);
|
||||
if (param instanceof MatParamTexture) {
|
||||
MatParamTexture tex = (MatParamTexture) param;
|
||||
if (tex.getTextureValue() != null && tex.getTextureValue().getImage() != null) {
|
||||
if (texId == -1) {
|
||||
texId = 0;
|
||||
if (!param.getVarType().isTextureType()) {
|
||||
continue;
|
||||
}
|
||||
texId += tex.getTextureValue().getImage().getId() % 0xff;
|
||||
Texture texture = (Texture) param.getValue();
|
||||
if (texture == null) {
|
||||
continue;
|
||||
}
|
||||
Image image = texture.getImage();
|
||||
if (image == null) {
|
||||
continue;
|
||||
}
|
||||
int textureId = image.getId();
|
||||
if (textureId == -1) {
|
||||
textureId = 0;
|
||||
}
|
||||
sortingId = texId + t.getShader().getId() * 1000;
|
||||
texturesSortId = texturesSortId * 23 + textureId;
|
||||
}
|
||||
sortingId |= texturesSortId & 0xFFFF;
|
||||
}
|
||||
return sortingId;
|
||||
}
|
||||
@ -215,6 +219,8 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
|
||||
mat.paramValues.put(entry.getKey(), entry.getValue().clone());
|
||||
}
|
||||
|
||||
mat.sortingId = -1;
|
||||
|
||||
return mat;
|
||||
} catch (CloneNotSupportedException ex) {
|
||||
throw new AssertionError(ex);
|
||||
@ -444,7 +450,7 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
|
||||
*
|
||||
* @see #setParam(java.lang.String, com.jme3.shader.VarType, java.lang.Object)
|
||||
*/
|
||||
public ListMap getParamsMap() {
|
||||
public ListMap<String, MatParam> getParamsMap() {
|
||||
return paramValues;
|
||||
}
|
||||
|
||||
@ -695,257 +701,6 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
|
||||
setParam(name, VarType.Vector4, value);
|
||||
}
|
||||
|
||||
private ColorRGBA getAmbientColor(LightList lightList, boolean removeLights) {
|
||||
ambientLightColor.set(0, 0, 0, 1);
|
||||
for (int j = 0; j < lightList.size(); j++) {
|
||||
Light l = lightList.get(j);
|
||||
if (l instanceof AmbientLight) {
|
||||
ambientLightColor.addLocal(l.getColor());
|
||||
if(removeLights){
|
||||
lightList.remove(l);
|
||||
}
|
||||
}
|
||||
}
|
||||
ambientLightColor.a = 1.0f;
|
||||
return ambientLightColor;
|
||||
}
|
||||
|
||||
private static void renderMeshFromGeometry(Renderer renderer, Geometry geom) {
|
||||
Mesh mesh = geom.getMesh();
|
||||
int lodLevel = geom.getLodLevel();
|
||||
if (geom instanceof InstancedGeometry) {
|
||||
InstancedGeometry instGeom = (InstancedGeometry) geom;
|
||||
int numInstances = instGeom.getActualNumInstances();
|
||||
if (numInstances == 0) {
|
||||
return;
|
||||
}
|
||||
if (renderer.getCaps().contains(Caps.MeshInstancing)) {
|
||||
renderer.renderMesh(mesh, lodLevel, numInstances, instGeom.getAllInstanceData());
|
||||
} else {
|
||||
throw new RendererException("Mesh instancing is not supported by the video hardware");
|
||||
}
|
||||
} else {
|
||||
renderer.renderMesh(mesh, lodLevel, 1, null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Uploads the lights in the light list as two uniform arrays.<br/><br/> *
|
||||
* <p>
|
||||
* <code>uniform vec4 g_LightColor[numLights];</code><br/> //
|
||||
* g_LightColor.rgb is the diffuse/specular color of the light.<br/> //
|
||||
* g_Lightcolor.a is the type of light, 0 = Directional, 1 = Point, <br/> //
|
||||
* 2 = Spot. <br/> <br/>
|
||||
* <code>uniform vec4 g_LightPosition[numLights];</code><br/> //
|
||||
* g_LightPosition.xyz is the position of the light (for point lights)<br/>
|
||||
* // or the direction of the light (for directional lights).<br/> //
|
||||
* g_LightPosition.w is the inverse radius (1/r) of the light (for
|
||||
* attenuation) <br/> </p>
|
||||
*/
|
||||
protected int updateLightListUniforms(Shader shader, Geometry g, LightList lightList, int numLights, RenderManager rm, int startIndex) {
|
||||
if (numLights == 0) { // this shader does not do lighting, ignore.
|
||||
return 0;
|
||||
}
|
||||
|
||||
Uniform lightData = shader.getUniform("g_LightData");
|
||||
lightData.setVector4Length(numLights * 3);//8 lights * max 3
|
||||
Uniform ambientColor = shader.getUniform("g_AmbientLightColor");
|
||||
|
||||
|
||||
if (startIndex != 0) {
|
||||
// apply additive blending for 2nd and future passes
|
||||
rm.getRenderer().applyRenderState(additiveLight);
|
||||
ambientColor.setValue(VarType.Vector4, ColorRGBA.Black);
|
||||
}else{
|
||||
ambientColor.setValue(VarType.Vector4, getAmbientColor(lightList,true));
|
||||
}
|
||||
|
||||
int lightDataIndex = 0;
|
||||
TempVars vars = TempVars.get();
|
||||
Vector4f tmpVec = vars.vect4f1;
|
||||
int curIndex;
|
||||
int endIndex = numLights + startIndex;
|
||||
for (curIndex = startIndex; curIndex < endIndex && curIndex < lightList.size(); curIndex++) {
|
||||
|
||||
|
||||
Light l = lightList.get(curIndex);
|
||||
if(l.getType() == Light.Type.Ambient){
|
||||
endIndex++;
|
||||
continue;
|
||||
}
|
||||
ColorRGBA color = l.getColor();
|
||||
//Color
|
||||
lightData.setVector4InArray(color.getRed(),
|
||||
color.getGreen(),
|
||||
color.getBlue(),
|
||||
l.getType().getId(),
|
||||
lightDataIndex);
|
||||
lightDataIndex++;
|
||||
|
||||
switch (l.getType()) {
|
||||
case Directional:
|
||||
DirectionalLight dl = (DirectionalLight) l;
|
||||
Vector3f dir = dl.getDirection();
|
||||
//Data directly sent in view space to avoid a matrix mult for each pixel
|
||||
tmpVec.set(dir.getX(), dir.getY(), dir.getZ(), 0.0f);
|
||||
rm.getCurrentCamera().getViewMatrix().mult(tmpVec, tmpVec);
|
||||
// tmpVec.divideLocal(tmpVec.w);
|
||||
// tmpVec.normalizeLocal();
|
||||
lightData.setVector4InArray(tmpVec.getX(), tmpVec.getY(), tmpVec.getZ(), -1, lightDataIndex);
|
||||
lightDataIndex++;
|
||||
//PADDING
|
||||
lightData.setVector4InArray(0,0,0,0, lightDataIndex);
|
||||
lightDataIndex++;
|
||||
break;
|
||||
case Point:
|
||||
PointLight pl = (PointLight) l;
|
||||
Vector3f pos = pl.getPosition();
|
||||
float invRadius = pl.getInvRadius();
|
||||
tmpVec.set(pos.getX(), pos.getY(), pos.getZ(), 1.0f);
|
||||
rm.getCurrentCamera().getViewMatrix().mult(tmpVec, tmpVec);
|
||||
//tmpVec.divideLocal(tmpVec.w);
|
||||
lightData.setVector4InArray(tmpVec.getX(), tmpVec.getY(), tmpVec.getZ(), invRadius, lightDataIndex);
|
||||
lightDataIndex++;
|
||||
//PADDING
|
||||
lightData.setVector4InArray(0,0,0,0, lightDataIndex);
|
||||
lightDataIndex++;
|
||||
break;
|
||||
case Spot:
|
||||
SpotLight sl = (SpotLight) l;
|
||||
Vector3f pos2 = sl.getPosition();
|
||||
Vector3f dir2 = sl.getDirection();
|
||||
float invRange = sl.getInvSpotRange();
|
||||
float spotAngleCos = sl.getPackedAngleCos();
|
||||
tmpVec.set(pos2.getX(), pos2.getY(), pos2.getZ(), 1.0f);
|
||||
rm.getCurrentCamera().getViewMatrix().mult(tmpVec, tmpVec);
|
||||
// tmpVec.divideLocal(tmpVec.w);
|
||||
lightData.setVector4InArray(tmpVec.getX(), tmpVec.getY(), tmpVec.getZ(), invRange, lightDataIndex);
|
||||
lightDataIndex++;
|
||||
|
||||
//We transform the spot direction in view space here to save 5 varying later in the lighting shader
|
||||
//one vec4 less and a vec4 that becomes a vec3
|
||||
//the downside is that spotAngleCos decoding happens now in the frag shader.
|
||||
tmpVec.set(dir2.getX(), dir2.getY(), dir2.getZ(), 0.0f);
|
||||
rm.getCurrentCamera().getViewMatrix().mult(tmpVec, tmpVec);
|
||||
tmpVec.normalizeLocal();
|
||||
lightData.setVector4InArray(tmpVec.getX(), tmpVec.getY(), tmpVec.getZ(), spotAngleCos, lightDataIndex);
|
||||
lightDataIndex++;
|
||||
break;
|
||||
default:
|
||||
throw new UnsupportedOperationException("Unknown type of light: " + l.getType());
|
||||
}
|
||||
}
|
||||
vars.release();
|
||||
//Padding of unsued buffer space
|
||||
while(lightDataIndex < numLights * 3) {
|
||||
lightData.setVector4InArray(0f, 0f, 0f, 0f, lightDataIndex);
|
||||
lightDataIndex++;
|
||||
}
|
||||
return curIndex;
|
||||
}
|
||||
|
||||
protected void renderMultipassLighting(Shader shader, Geometry g, LightList lightList, RenderManager rm) {
|
||||
|
||||
Renderer r = rm.getRenderer();
|
||||
Uniform lightDir = shader.getUniform("g_LightDirection");
|
||||
Uniform lightColor = shader.getUniform("g_LightColor");
|
||||
Uniform lightPos = shader.getUniform("g_LightPosition");
|
||||
Uniform ambientColor = shader.getUniform("g_AmbientLightColor");
|
||||
boolean isFirstLight = true;
|
||||
boolean isSecondLight = false;
|
||||
|
||||
for (int i = 0; i < lightList.size(); i++) {
|
||||
Light l = lightList.get(i);
|
||||
if (l instanceof AmbientLight) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isFirstLight) {
|
||||
// set ambient color for first light only
|
||||
ambientColor.setValue(VarType.Vector4, getAmbientColor(lightList, false));
|
||||
isFirstLight = false;
|
||||
isSecondLight = true;
|
||||
} else if (isSecondLight) {
|
||||
ambientColor.setValue(VarType.Vector4, ColorRGBA.Black);
|
||||
// apply additive blending for 2nd and future lights
|
||||
r.applyRenderState(additiveLight);
|
||||
isSecondLight = false;
|
||||
}
|
||||
|
||||
TempVars vars = TempVars.get();
|
||||
Quaternion tmpLightDirection = vars.quat1;
|
||||
Quaternion tmpLightPosition = vars.quat2;
|
||||
ColorRGBA tmpLightColor = vars.color;
|
||||
Vector4f tmpVec = vars.vect4f1;
|
||||
|
||||
ColorRGBA color = l.getColor();
|
||||
tmpLightColor.set(color);
|
||||
tmpLightColor.a = l.getType().getId();
|
||||
lightColor.setValue(VarType.Vector4, tmpLightColor);
|
||||
|
||||
switch (l.getType()) {
|
||||
case Directional:
|
||||
DirectionalLight dl = (DirectionalLight) l;
|
||||
Vector3f dir = dl.getDirection();
|
||||
//FIXME : there is an inconstency here due to backward
|
||||
//compatibility of the lighting shader.
|
||||
//The directional light direction is passed in the
|
||||
//LightPosition uniform. The lighting shader needs to be
|
||||
//reworked though in order to fix this.
|
||||
tmpLightPosition.set(dir.getX(), dir.getY(), dir.getZ(), -1);
|
||||
lightPos.setValue(VarType.Vector4, tmpLightPosition);
|
||||
tmpLightDirection.set(0, 0, 0, 0);
|
||||
lightDir.setValue(VarType.Vector4, tmpLightDirection);
|
||||
break;
|
||||
case Point:
|
||||
PointLight pl = (PointLight) l;
|
||||
Vector3f pos = pl.getPosition();
|
||||
float invRadius = pl.getInvRadius();
|
||||
|
||||
tmpLightPosition.set(pos.getX(), pos.getY(), pos.getZ(), invRadius);
|
||||
lightPos.setValue(VarType.Vector4, tmpLightPosition);
|
||||
tmpLightDirection.set(0, 0, 0, 0);
|
||||
lightDir.setValue(VarType.Vector4, tmpLightDirection);
|
||||
break;
|
||||
case Spot:
|
||||
SpotLight sl = (SpotLight) l;
|
||||
Vector3f pos2 = sl.getPosition();
|
||||
Vector3f dir2 = sl.getDirection();
|
||||
float invRange = sl.getInvSpotRange();
|
||||
float spotAngleCos = sl.getPackedAngleCos();
|
||||
|
||||
tmpLightPosition.set(pos2.getX(), pos2.getY(), pos2.getZ(), invRange);
|
||||
lightPos.setValue(VarType.Vector4, tmpLightPosition);
|
||||
|
||||
//We transform the spot direction in view space here to save 5 varying later in the lighting shader
|
||||
//one vec4 less and a vec4 that becomes a vec3
|
||||
//the downside is that spotAngleCos decoding happens now in the frag shader.
|
||||
tmpVec.set(dir2.getX(), dir2.getY(), dir2.getZ(), 0);
|
||||
rm.getCurrentCamera().getViewMatrix().mult(tmpVec, tmpVec);
|
||||
tmpLightDirection.set(tmpVec.getX(), tmpVec.getY(), tmpVec.getZ(), spotAngleCos);
|
||||
|
||||
lightDir.setValue(VarType.Vector4, tmpLightDirection);
|
||||
|
||||
break;
|
||||
default:
|
||||
throw new UnsupportedOperationException("Unknown type of light: " + l.getType());
|
||||
}
|
||||
vars.release();
|
||||
r.setShader(shader);
|
||||
renderMeshFromGeometry(r, g);
|
||||
}
|
||||
|
||||
if (isFirstLight) {
|
||||
// Either there are no lights at all, or only ambient lights.
|
||||
// Render a dummy "normal light" so we can see the ambient color.
|
||||
ambientColor.setValue(VarType.Vector4, getAmbientColor(lightList, false));
|
||||
lightColor.setValue(VarType.Vector4, ColorRGBA.BlackNoAlpha);
|
||||
lightPos.setValue(VarType.Vector4, nullDirLight);
|
||||
r.setShader(shader);
|
||||
renderMeshFromGeometry(r, g);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Select the technique to use for rendering this material.
|
||||
* <p>
|
||||
@ -974,9 +729,8 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
|
||||
Technique tech = techniques.get(name);
|
||||
// When choosing technique, we choose one that
|
||||
// supports all the caps.
|
||||
EnumSet<Caps> rendererCaps = renderManager.getRenderer().getCaps();
|
||||
if (tech == null) {
|
||||
|
||||
EnumSet<Caps> rendererCaps = renderManager.getRenderer().getCaps();
|
||||
if (name.equals("Default")) {
|
||||
List<TechniqueDef> techDefs = def.getDefaultTechniques();
|
||||
if (techDefs == null || techDefs.isEmpty()) {
|
||||
@ -1025,17 +779,68 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
|
||||
}
|
||||
|
||||
technique = tech;
|
||||
tech.makeCurrent(def.getAssetManager(), true, rendererCaps, renderManager);
|
||||
tech.notifyTechniqueSwitched();
|
||||
|
||||
// shader was changed
|
||||
sortingId = -1;
|
||||
}
|
||||
|
||||
private void autoSelectTechnique(RenderManager rm) {
|
||||
if (technique == null) {
|
||||
selectTechnique("Default", rm);
|
||||
private void updateShaderMaterialParameters(Renderer renderer, Shader shader, List<MatParamOverride> overrides) {
|
||||
int unit = 0;
|
||||
|
||||
if (overrides != null) {
|
||||
for (MatParamOverride override : overrides) {
|
||||
VarType type = override.getVarType();
|
||||
|
||||
MatParam paramDef = def.getMaterialParam(override.getName());
|
||||
if (paramDef == null || paramDef.getVarType() != type || !override.isEnabled()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Uniform uniform = shader.getUniform(override.getPrefixedName());
|
||||
if (override.getValue() != null) {
|
||||
if (type.isTextureType()) {
|
||||
renderer.setTexture(unit, (Texture) override.getValue());
|
||||
uniform.setValue(VarType.Int, unit);
|
||||
unit++;
|
||||
} else {
|
||||
technique.makeCurrent(def.getAssetManager(), false, rm.getRenderer().getCaps(), rm);
|
||||
uniform.setValue(type, override.getValue());
|
||||
}
|
||||
} else {
|
||||
uniform.clearValue();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < paramValues.size(); i++) {
|
||||
MatParam param = paramValues.getValue(i);
|
||||
VarType type = param.getVarType();
|
||||
Uniform uniform = shader.getUniform(param.getPrefixedName());
|
||||
|
||||
if (uniform.isSetByCurrentMaterial()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (type.isTextureType()) {
|
||||
renderer.setTexture(unit, (Texture) param.getValue());
|
||||
uniform.setValue(VarType.Int, unit);
|
||||
unit++;
|
||||
} else {
|
||||
uniform.setValue(type, param.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void updateRenderState(RenderManager renderManager, Renderer renderer, TechniqueDef techniqueDef) {
|
||||
if (renderManager.getForcedRenderState() != null) {
|
||||
renderer.applyRenderState(renderManager.getForcedRenderState());
|
||||
} else {
|
||||
if (techniqueDef.getRenderState() != null) {
|
||||
renderer.applyRenderState(techniqueDef.getRenderState().copyMergedTo(additionalState, mergedRenderState));
|
||||
} else {
|
||||
renderer.applyRenderState(RenderState.DEFAULT.copyMergedTo(additionalState, mergedRenderState));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1046,20 +851,23 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
|
||||
* used for rendering, there won't be any delay since the material has
|
||||
* been already been setup for rendering.
|
||||
*
|
||||
* @param rm The render manager to preload for
|
||||
* @param renderManager The render manager to preload for
|
||||
*/
|
||||
public void preload(RenderManager rm) {
|
||||
autoSelectTechnique(rm);
|
||||
public void preload(RenderManager renderManager) {
|
||||
if (technique == null) {
|
||||
selectTechnique("Default", renderManager);
|
||||
}
|
||||
TechniqueDef techniqueDef = technique.getDef();
|
||||
Renderer renderer = renderManager.getRenderer();
|
||||
EnumSet<Caps> rendererCaps = renderer.getCaps();
|
||||
|
||||
Renderer r = rm.getRenderer();
|
||||
TechniqueDef techDef = technique.getDef();
|
||||
|
||||
Collection<MatParam> params = paramValues.values();
|
||||
for (MatParam param : params) {
|
||||
param.apply(r, technique);
|
||||
if (techniqueDef.isNoRender()) {
|
||||
return;
|
||||
}
|
||||
|
||||
r.setShader(technique.getShader());
|
||||
Shader shader = technique.makeCurrent(renderManager, null, null, rendererCaps);
|
||||
updateShaderMaterialParameters(renderer, shader, null);
|
||||
renderManager.getRenderer().setShader(shader);
|
||||
}
|
||||
|
||||
private void clearUniformsSetByCurrent(Shader shader) {
|
||||
@ -1141,80 +949,46 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
|
||||
* </ul>
|
||||
* </ul>
|
||||
*
|
||||
* @param geom The geometry to render
|
||||
* @param geometry The geometry to render
|
||||
* @param lights Presorted and filtered light list to use for rendering
|
||||
* @param rm The render manager requesting the rendering
|
||||
* @param renderManager The render manager requesting the rendering
|
||||
*/
|
||||
public void render(Geometry geom, LightList lights, RenderManager rm) {
|
||||
autoSelectTechnique(rm);
|
||||
TechniqueDef techDef = technique.getDef();
|
||||
|
||||
if (techDef.isNoRender()) return;
|
||||
|
||||
Renderer r = rm.getRenderer();
|
||||
|
||||
if (rm.getForcedRenderState() != null) {
|
||||
r.applyRenderState(rm.getForcedRenderState());
|
||||
} else {
|
||||
if (techDef.getRenderState() != null) {
|
||||
r.applyRenderState(techDef.getRenderState().copyMergedTo(additionalState, mergedRenderState));
|
||||
} else {
|
||||
r.applyRenderState(RenderState.DEFAULT.copyMergedTo(additionalState, mergedRenderState));
|
||||
}
|
||||
public void render(Geometry geometry, LightList lights, RenderManager renderManager) {
|
||||
if (technique == null) {
|
||||
selectTechnique("Default", renderManager);
|
||||
}
|
||||
|
||||
TechniqueDef techniqueDef = technique.getDef();
|
||||
Renderer renderer = renderManager.getRenderer();
|
||||
EnumSet<Caps> rendererCaps = renderer.getCaps();
|
||||
|
||||
// update camera and world matrices
|
||||
// NOTE: setWorldTransform should have been called already
|
||||
|
||||
// reset unchanged uniform flag
|
||||
clearUniformsSetByCurrent(technique.getShader());
|
||||
rm.updateUniformBindings(technique.getWorldBindUniforms());
|
||||
|
||||
|
||||
// setup textures and uniforms
|
||||
for (int i = 0; i < paramValues.size(); i++) {
|
||||
MatParam param = paramValues.getValue(i);
|
||||
param.apply(r, technique);
|
||||
}
|
||||
|
||||
Shader shader = technique.getShader();
|
||||
|
||||
// send lighting information, if needed
|
||||
switch (techDef.getLightMode()) {
|
||||
case Disable:
|
||||
break;
|
||||
case SinglePass:
|
||||
int nbRenderedLights = 0;
|
||||
resetUniformsNotSetByCurrent(shader);
|
||||
if (lights.size() == 0) {
|
||||
nbRenderedLights = updateLightListUniforms(shader, geom, lights, rm.getSinglePassLightBatchSize(), rm, 0);
|
||||
r.setShader(shader);
|
||||
renderMeshFromGeometry(r, geom);
|
||||
} else {
|
||||
while (nbRenderedLights < lights.size()) {
|
||||
nbRenderedLights = updateLightListUniforms(shader, geom, lights, rm.getSinglePassLightBatchSize(), rm, nbRenderedLights);
|
||||
r.setShader(shader);
|
||||
renderMeshFromGeometry(r, geom);
|
||||
}
|
||||
}
|
||||
return;
|
||||
case FixedPipeline:
|
||||
throw new IllegalArgumentException("OpenGL1 is not supported");
|
||||
case MultiPass:
|
||||
// NOTE: Special case!
|
||||
resetUniformsNotSetByCurrent(shader);
|
||||
renderMultipassLighting(shader, geom, lights, rm);
|
||||
// very important, notice the return statement!
|
||||
if (techniqueDef.isNoRender()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// upload and bind shader
|
||||
// any unset uniforms will be set to 0
|
||||
resetUniformsNotSetByCurrent(shader);
|
||||
r.setShader(shader);
|
||||
// Apply render state
|
||||
updateRenderState(renderManager, renderer, techniqueDef);
|
||||
|
||||
renderMeshFromGeometry(r, geom);
|
||||
// Get world overrides
|
||||
List<MatParamOverride> overrides = geometry.getWorldMatParamOverrides();
|
||||
|
||||
// Select shader to use
|
||||
Shader shader = technique.makeCurrent(renderManager, overrides, lights, rendererCaps);
|
||||
|
||||
// Begin tracking which uniforms were changed by material.
|
||||
clearUniformsSetByCurrent(shader);
|
||||
|
||||
// Set uniform bindings
|
||||
renderManager.updateUniformBindings(shader);
|
||||
|
||||
// Set material parameters
|
||||
updateShaderMaterialParameters(renderer, shader, geometry.getWorldMatParamOverrides());
|
||||
|
||||
// Clear any uniforms not changed by material.
|
||||
resetUniformsNotSetByCurrent(shader);
|
||||
|
||||
// Delegate rendering to the technique
|
||||
technique.render(renderManager, shader, geometry, lights);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1240,6 +1014,14 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
|
||||
oc.writeStringSavableMap(paramValues, "parameters", null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Material[name=" + name +
|
||||
", def=" + def.getName() +
|
||||
", tech=" + technique.getDef().getName() +
|
||||
"]";
|
||||
}
|
||||
|
||||
public void read(JmeImporter im) throws IOException {
|
||||
InputCapsule ic = im.getCapsule(this);
|
||||
|
||||
|
@ -311,6 +311,8 @@ public class RenderState implements Cloneable, Savable {
|
||||
boolean applyPolyOffset = true;
|
||||
boolean stencilTest = false;
|
||||
boolean applyStencilTest = false;
|
||||
float lineWidth = 1;
|
||||
boolean applyLineWidth = false;
|
||||
TestFunction depthFunc = TestFunction.LessOrEqual;
|
||||
//by default depth func will be applied anyway if depth test is applied
|
||||
boolean applyDepthFunc = false;
|
||||
@ -350,6 +352,9 @@ public class RenderState implements Cloneable, Savable {
|
||||
oc.write(backStencilDepthPassOperation, "backStencilDepthPassOperation", StencilOperation.Keep);
|
||||
oc.write(frontStencilFunction, "frontStencilFunction", 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
|
||||
oc.write(applyPointSprite, "applyPointSprite", true);
|
||||
@ -364,8 +369,7 @@ public class RenderState implements Cloneable, Savable {
|
||||
oc.write(applyPolyOffset, "applyPolyOffset", true);
|
||||
oc.write(applyDepthFunc, "applyDepthFunc", true);
|
||||
oc.write(applyAlphaFunc, "applyAlphaFunc", false);
|
||||
oc.write(depthFunc, "depthFunc", TestFunction.LessOrEqual);
|
||||
oc.write(alphaFunc, "alphaFunc", TestFunction.Greater);
|
||||
oc.write(applyLineWidth, "applyLineWidth", true);
|
||||
|
||||
}
|
||||
|
||||
@ -394,6 +398,8 @@ public class RenderState implements Cloneable, Savable {
|
||||
backStencilFunction = ic.readEnum("backStencilFunction", TestFunction.class, TestFunction.Always);
|
||||
depthFunc = ic.readEnum("depthFunc", TestFunction.class, TestFunction.LessOrEqual);
|
||||
alphaFunc = ic.readEnum("alphaFunc", TestFunction.class, TestFunction.Greater);
|
||||
lineWidth = ic.readFloat("lineWidth", 1);
|
||||
|
||||
|
||||
applyPointSprite = ic.readBoolean("applyPointSprite", true);
|
||||
applyWireFrame = ic.readBoolean("applyWireFrame", true);
|
||||
@ -407,6 +413,8 @@ public class RenderState implements Cloneable, Savable {
|
||||
applyPolyOffset = ic.readBoolean("applyPolyOffset", true);
|
||||
applyDepthFunc = ic.readBoolean("applyDepthFunc", true);
|
||||
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;
|
||||
}
|
||||
|
||||
@ -804,7 +816,19 @@ public class RenderState implements Cloneable, Savable {
|
||||
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) {
|
||||
if (lineWidth < 1f) {
|
||||
throw new IllegalArgumentException("lineWidth must be greater than or equal to 1.0");
|
||||
}
|
||||
this.lineWidth = lineWidth;
|
||||
this.applyLineWidth = true;
|
||||
cachedHashCode = -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if stencil test is enabled.
|
||||
@ -1119,6 +1143,14 @@ public class RenderState implements Cloneable, Savable {
|
||||
return alphaFunc;
|
||||
}
|
||||
|
||||
/**
|
||||
* returns the wireframe line width
|
||||
*
|
||||
* @return the line width
|
||||
*/
|
||||
public float getLineWidth() {
|
||||
return lineWidth;
|
||||
}
|
||||
|
||||
|
||||
public boolean isApplyAlphaFallOff() {
|
||||
@ -1169,7 +1201,9 @@ public class RenderState implements Cloneable, Savable {
|
||||
return applyAlphaFunc;
|
||||
}
|
||||
|
||||
|
||||
public boolean isApplyLineWidth() {
|
||||
return applyLineWidth;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
@ -1200,6 +1234,7 @@ public class RenderState implements Cloneable, Savable {
|
||||
hash = 79 * hash + (this.backStencilDepthPassOperation != null ? this.backStencilDepthPassOperation.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 + Float.floatToIntBits(this.lineWidth);
|
||||
cachedHashCode = hash;
|
||||
}
|
||||
return cachedHashCode;
|
||||
@ -1324,6 +1359,11 @@ public class RenderState implements Cloneable, Savable {
|
||||
state.frontStencilFunction = frontStencilFunction;
|
||||
state.backStencilFunction = backStencilFunction;
|
||||
}
|
||||
if (additionalState.applyLineWidth) {
|
||||
state.lineWidth = additionalState.lineWidth;
|
||||
} else {
|
||||
state.lineWidth = lineWidth;
|
||||
}
|
||||
state.cachedHashCode = -1;
|
||||
return state;
|
||||
}
|
||||
@ -1351,6 +1391,7 @@ public class RenderState implements Cloneable, Savable {
|
||||
backStencilFunction = state.backStencilFunction;
|
||||
depthFunc = state.depthFunc;
|
||||
alphaFunc = state.alphaFunc;
|
||||
lineWidth = state.lineWidth;
|
||||
|
||||
applyPointSprite = true;
|
||||
applyWireFrame = true;
|
||||
@ -1364,6 +1405,7 @@ public class RenderState implements Cloneable, Savable {
|
||||
applyPolyOffset = true;
|
||||
applyDepthFunc = true;
|
||||
applyAlphaFunc = false;
|
||||
applyLineWidth = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -1393,6 +1435,7 @@ public class RenderState implements Cloneable, Savable {
|
||||
+ "\napplyPolyOffset=" + applyPolyOffset
|
||||
+ "\noffsetFactor=" + offsetFactor
|
||||
+ "\noffsetUnits=" + offsetUnits
|
||||
+ "\nlineWidth=" + lineWidth
|
||||
+ "\n]";
|
||||
}
|
||||
}
|
||||
|
@ -31,27 +31,30 @@
|
||||
*/
|
||||
package com.jme3.material;
|
||||
|
||||
import com.jme3.material.logic.TechniqueDefLogic;
|
||||
import com.jme3.asset.AssetManager;
|
||||
import com.jme3.light.LightList;
|
||||
import com.jme3.material.TechniqueDef.LightMode;
|
||||
import com.jme3.renderer.Caps;
|
||||
import com.jme3.renderer.RenderManager;
|
||||
import com.jme3.shader.*;
|
||||
import com.jme3.scene.Geometry;
|
||||
import com.jme3.shader.DefineList;
|
||||
import com.jme3.shader.Shader;
|
||||
import com.jme3.shader.VarType;
|
||||
import com.jme3.util.ListMap;
|
||||
import java.util.ArrayList;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* Represents a technique instance.
|
||||
*/
|
||||
public class Technique /* implements Savable */ {
|
||||
public final class Technique {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(Technique.class.getName());
|
||||
private TechniqueDef def;
|
||||
private Material owner;
|
||||
private ArrayList<Uniform> worldBindUniforms;
|
||||
private DefineList defines;
|
||||
private Shader shader;
|
||||
private boolean needReload = true;
|
||||
private final TechniqueDef def;
|
||||
private final Material owner;
|
||||
private final DefineList paramDefines;
|
||||
private final DefineList dynamicDefines;
|
||||
|
||||
/**
|
||||
* Creates a new technique instance that implements the given
|
||||
@ -63,14 +66,8 @@ public class Technique /* implements Savable */ {
|
||||
public Technique(Material owner, TechniqueDef def) {
|
||||
this.owner = owner;
|
||||
this.def = def;
|
||||
this.worldBindUniforms = new ArrayList<Uniform>();
|
||||
this.defines = new DefineList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialization only. Do not use.
|
||||
*/
|
||||
public Technique() {
|
||||
this.paramDefines = def.createDefineList();
|
||||
this.dynamicDefines = def.createDefineList();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -84,158 +81,118 @@ public class Technique /* implements Savable */ {
|
||||
return def;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the shader currently used by this technique instance.
|
||||
* <p>
|
||||
* Shaders are typically loaded dynamically when the technique is first
|
||||
* used, therefore, this variable will most likely be null most of the time.
|
||||
*
|
||||
* @return the shader currently used by this technique instance.
|
||||
*/
|
||||
public Shader getShader() {
|
||||
return shader;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of uniforms that implements the world parameters
|
||||
* that were requested by the material definition.
|
||||
*
|
||||
* @return a list of uniforms implementing the world parameters.
|
||||
*/
|
||||
public List<Uniform> getWorldBindUniforms() {
|
||||
return worldBindUniforms;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by the material to tell the technique a parameter was modified.
|
||||
* Specify <code>null</code> for value if the param is to be cleared.
|
||||
*/
|
||||
void notifyParamChanged(String paramName, VarType type, Object value) {
|
||||
// Check if there's a define binding associated with this
|
||||
// parameter.
|
||||
String defineName = def.getShaderParamDefine(paramName);
|
||||
if (defineName != null) {
|
||||
// There is a define. Change it on the define list.
|
||||
// The "needReload" variable will determine
|
||||
// if the shader will be reloaded when the material
|
||||
// is rendered.
|
||||
final void notifyParamChanged(String paramName, VarType type, Object value) {
|
||||
Integer defineId = def.getShaderParamDefineId(paramName);
|
||||
|
||||
if (value == null) {
|
||||
// Clear the define.
|
||||
needReload = defines.remove(defineName) || needReload;
|
||||
} else {
|
||||
// Set the define.
|
||||
needReload = defines.set(defineName, type, value) || needReload;
|
||||
}
|
||||
}
|
||||
if (defineId == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
void updateUniformParam(String paramName, VarType type, Object value) {
|
||||
if (paramName == null) {
|
||||
throw new IllegalArgumentException();
|
||||
paramDefines.set(defineId, type, value);
|
||||
}
|
||||
|
||||
Uniform u = shader.getUniform(paramName);
|
||||
switch (type) {
|
||||
case TextureBuffer:
|
||||
case Texture2D: // fall intentional
|
||||
case Texture3D:
|
||||
case TextureArray:
|
||||
case TextureCubeMap:
|
||||
case Int:
|
||||
u.setValue(VarType.Int, value);
|
||||
break;
|
||||
default:
|
||||
u.setValue(type, value);
|
||||
break;
|
||||
/**
|
||||
* Called by the material to tell the technique that it has been made
|
||||
* current.
|
||||
* The technique updates dynamic defines based on the
|
||||
* currently set material parameters.
|
||||
*/
|
||||
final void notifyTechniqueSwitched() {
|
||||
ListMap<String, MatParam> paramMap = owner.getParamsMap();
|
||||
paramDefines.clear();
|
||||
for (int i = 0; i < paramMap.size(); i++) {
|
||||
MatParam param = paramMap.getValue(i);
|
||||
notifyParamChanged(param.getName(), param.getVarType(), param.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the technique must be reloaded.
|
||||
* <p>
|
||||
* If a technique needs to reload, then the {@link Material} should
|
||||
* call {@link #makeCurrent(com.jme3.asset.AssetManager) } on this
|
||||
* technique.
|
||||
* Called by the material to determine which shader to use for rendering.
|
||||
*
|
||||
* @return true if the technique must be reloaded.
|
||||
*/
|
||||
public boolean isNeedReload() {
|
||||
return needReload;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares the technique for use by loading the shader and setting
|
||||
* the proper defines based on material parameters.
|
||||
* The {@link TechniqueDefLogic} is used to determine the shader to use
|
||||
* based on the {@link LightMode}.
|
||||
*
|
||||
* @param assetManager The asset manager to use for loading shaders.
|
||||
* @param renderManager The render manager for which the shader is to be selected.
|
||||
* @param rendererCaps The renderer capabilities which the shader should support.
|
||||
* @return A compatible shader.
|
||||
*/
|
||||
public void makeCurrent(AssetManager assetManager, boolean techniqueSwitched, EnumSet<Caps> rendererCaps, RenderManager rm) {
|
||||
if (techniqueSwitched) {
|
||||
if (defines.update(owner.getParamsMap(), def)) {
|
||||
needReload = true;
|
||||
Shader makeCurrent(RenderManager renderManager, List<MatParamOverride> overrides,
|
||||
LightList lights, EnumSet<Caps> rendererCaps) {
|
||||
TechniqueDefLogic logic = def.getLogic();
|
||||
AssetManager assetManager = owner.getMaterialDef().getAssetManager();
|
||||
|
||||
dynamicDefines.clear();
|
||||
dynamicDefines.setAll(paramDefines);
|
||||
|
||||
if (overrides != null) {
|
||||
for (MatParamOverride override : overrides) {
|
||||
if (!override.isEnabled()) {
|
||||
continue;
|
||||
}
|
||||
Integer defineId = def.getShaderParamDefineId(override.name);
|
||||
if (defineId != null) {
|
||||
if (def.getDefineIdType(defineId) == override.type) {
|
||||
dynamicDefines.set(defineId, override.type, override.value);
|
||||
}
|
||||
}
|
||||
if (getDef().getLightMode() == TechniqueDef.LightMode.SinglePass) {
|
||||
defines.set("SINGLE_PASS_LIGHTING", VarType.Boolean, true);
|
||||
defines.set("NB_LIGHTS", VarType.Int, rm.getSinglePassLightBatchSize() * 3);
|
||||
} else {
|
||||
defines.set("SINGLE_PASS_LIGHTING", VarType.Boolean, null);
|
||||
}
|
||||
}
|
||||
|
||||
if (needReload) {
|
||||
loadShader(assetManager,rendererCaps);
|
||||
}
|
||||
}
|
||||
|
||||
private void loadShader(AssetManager manager,EnumSet<Caps> rendererCaps) {
|
||||
|
||||
ShaderKey key = new ShaderKey(getAllDefines(),def.getShaderProgramLanguages(),def.getShaderProgramNames());
|
||||
|
||||
if (getDef().isUsingShaderNodes()) {
|
||||
manager.getShaderGenerator(rendererCaps).initialize(this);
|
||||
key.setUsesShaderNodes(true);
|
||||
}
|
||||
shader = manager.loadShader(key);
|
||||
|
||||
// register the world bound uniforms
|
||||
worldBindUniforms.clear();
|
||||
if (def.getWorldBindings() != null) {
|
||||
for (UniformBinding binding : def.getWorldBindings()) {
|
||||
Uniform uniform = shader.getUniform("g_" + binding.name());
|
||||
uniform.setBinding(binding);
|
||||
worldBindUniforms.add(uniform);
|
||||
}
|
||||
}
|
||||
needReload = false;
|
||||
return logic.makeCurrent(assetManager, renderManager, rendererCaps, lights, dynamicDefines);
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the define list
|
||||
* @return the complete define list
|
||||
* Render the technique according to its {@link TechniqueDefLogic}.
|
||||
*
|
||||
* @param renderManager The render manager to perform the rendering against.
|
||||
* @param shader The shader that was selected in
|
||||
* {@link #makeCurrent(com.jme3.renderer.RenderManager, java.util.EnumSet)}.
|
||||
* @param geometry The geometry to render
|
||||
* @param lights Lights which influence the geometry.
|
||||
*/
|
||||
void render(RenderManager renderManager, Shader shader, Geometry geometry, LightList lights) {
|
||||
TechniqueDefLogic logic = def.getLogic();
|
||||
logic.render(renderManager, shader, geometry, lights);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@link DefineList} for dynamic defines.
|
||||
*
|
||||
* Dynamic defines are used to implement material parameter -> define
|
||||
* bindings as well as {@link TechniqueDefLogic} specific functionality.
|
||||
*
|
||||
* @return all dynamic defines.
|
||||
*/
|
||||
public DefineList getDynamicDefines() {
|
||||
return dynamicDefines;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return nothing.
|
||||
*
|
||||
* @deprecated Preset defines are precompiled into
|
||||
* {@link TechniqueDef#getShaderPrologue()}, whereas
|
||||
* dynamic defines are available via {@link #getParamDefines()}.
|
||||
*/
|
||||
@Deprecated
|
||||
public DefineList getAllDefines() {
|
||||
DefineList allDefines = new DefineList();
|
||||
allDefines.addFrom(def.getShaderPresetDefines());
|
||||
allDefines.addFrom(defines);
|
||||
return allDefines;
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/*
|
||||
public void write(JmeExporter ex) throws IOException {
|
||||
OutputCapsule oc = ex.getCapsule(this);
|
||||
oc.write(def, "def", null);
|
||||
oc.writeSavableArrayList(worldBindUniforms, "worldBindUniforms", null);
|
||||
oc.write(defines, "defines", null);
|
||||
oc.write(shader, "shader", null);
|
||||
}
|
||||
|
||||
public void read(JmeImporter im) throws IOException {
|
||||
InputCapsule ic = im.getCapsule(this);
|
||||
def = (TechniqueDef) ic.readSavable("def", null);
|
||||
worldBindUniforms = ic.readSavableArrayList("worldBindUniforms", null);
|
||||
defines = (DefineList) ic.readSavable("defines", null);
|
||||
shader = (Shader) ic.readSavable("shader", null);
|
||||
}
|
||||
/**
|
||||
* Compute the sort ID. Similar to {@link Object#hashCode()} but used
|
||||
* for sorting geometries for rendering.
|
||||
*
|
||||
* @return the sort ID for this technique instance.
|
||||
*/
|
||||
public int getSortId() {
|
||||
int hash = 17;
|
||||
hash = hash * 23 + def.getSortId();
|
||||
hash = hash * 23 + paramDefines.hashCode();
|
||||
return hash;
|
||||
}
|
||||
}
|
||||
|
@ -31,9 +31,12 @@
|
||||
*/
|
||||
package com.jme3.material;
|
||||
|
||||
import com.jme3.material.logic.TechniqueDefLogic;
|
||||
import com.jme3.asset.AssetManager;
|
||||
import com.jme3.export.*;
|
||||
import com.jme3.renderer.Caps;
|
||||
import com.jme3.shader.*;
|
||||
import com.jme3.shader.Shader.ShaderType;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
@ -93,11 +96,17 @@ public class TechniqueDef implements Savable {
|
||||
|
||||
private EnumSet<Caps> requiredCaps = EnumSet.noneOf(Caps.class);
|
||||
private String name;
|
||||
private int sortId;
|
||||
|
||||
private EnumMap<Shader.ShaderType,String> shaderLanguages;
|
||||
private EnumMap<Shader.ShaderType,String> shaderNames;
|
||||
|
||||
private DefineList presetDefines;
|
||||
private String shaderPrologue;
|
||||
private ArrayList<String> defineNames;
|
||||
private ArrayList<VarType> defineTypes;
|
||||
private HashMap<String, Integer> paramToDefineId;
|
||||
private final HashMap<DefineList, Shader> definesToShaderMap;
|
||||
|
||||
private boolean usesNodes = false;
|
||||
private List<ShaderNode> shaderNodes;
|
||||
private ShaderGenerationInfo shaderGenerationInfo;
|
||||
@ -108,8 +117,8 @@ public class TechniqueDef implements Savable {
|
||||
|
||||
private LightMode lightMode = LightMode.Disable;
|
||||
private ShadowMode shadowMode = ShadowMode.Disable;
|
||||
private TechniqueDefLogic logic;
|
||||
|
||||
private HashMap<String, String> defineParams;
|
||||
private ArrayList<UniformBinding> worldBinds;
|
||||
|
||||
/**
|
||||
@ -120,8 +129,9 @@ public class TechniqueDef implements Savable {
|
||||
* @param name The name of the technique, should be set to <code>null</code>
|
||||
* for default techniques.
|
||||
*/
|
||||
public TechniqueDef(String name){
|
||||
public TechniqueDef(String name, int sortId){
|
||||
this();
|
||||
this.sortId = sortId;
|
||||
this.name = name == null ? "Default" : name;
|
||||
}
|
||||
|
||||
@ -131,6 +141,18 @@ public class TechniqueDef implements Savable {
|
||||
public TechniqueDef() {
|
||||
shaderLanguages = new EnumMap<Shader.ShaderType, String>(Shader.ShaderType.class);
|
||||
shaderNames = new EnumMap<Shader.ShaderType, String>(Shader.ShaderType.class);
|
||||
defineNames = new ArrayList<String>();
|
||||
defineTypes = new ArrayList<VarType>();
|
||||
paramToDefineId = new HashMap<String, Integer>();
|
||||
definesToShaderMap = new HashMap<DefineList, Shader>();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return A unique sort ID.
|
||||
* No other technique definition can have the same ID.
|
||||
*/
|
||||
public int getSortId() {
|
||||
return sortId;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -163,6 +185,14 @@ public class TechniqueDef implements Savable {
|
||||
this.lightMode = lightMode;
|
||||
}
|
||||
|
||||
public void setLogic(TechniqueDefLogic logic) {
|
||||
this.logic = logic;
|
||||
}
|
||||
|
||||
public TechniqueDefLogic getLogic() {
|
||||
return logic;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the shadow mode.
|
||||
* @return the shadow mode.
|
||||
@ -224,14 +254,6 @@ public class TechniqueDef implements Savable {
|
||||
return noRender;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated jME3 always requires shaders now
|
||||
*/
|
||||
@Deprecated
|
||||
public boolean isUsingShaders(){
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this technique uses Shader Nodes, false otherwise.
|
||||
*
|
||||
@ -273,6 +295,187 @@ public class TechniqueDef implements Savable {
|
||||
requiredCaps.add(fragCap);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a string which is prepended to every shader used by this technique.
|
||||
*
|
||||
* Typically this is used for preset defines.
|
||||
*
|
||||
* @param shaderPrologue The prologue to append before the technique's shaders.
|
||||
*/
|
||||
public void setShaderPrologue(String shaderPrologue) {
|
||||
this.shaderPrologue = shaderPrologue;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the shader prologue which is prepended to every shader.
|
||||
*/
|
||||
public String getShaderPrologue() {
|
||||
return shaderPrologue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the define name which the given material parameter influences.
|
||||
*
|
||||
* @param paramName The parameter name to look up
|
||||
* @return The define name
|
||||
*
|
||||
* @see #addShaderParamDefine(java.lang.String, java.lang.String)
|
||||
*/
|
||||
public String getShaderParamDefine(String paramName){
|
||||
Integer defineId = paramToDefineId.get(paramName);
|
||||
if (defineId != null) {
|
||||
return defineNames.get(defineId);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the define ID for a given material parameter.
|
||||
*
|
||||
* @param paramName The parameter name to look up
|
||||
* @return The define ID, or null if not found.
|
||||
*/
|
||||
public Integer getShaderParamDefineId(String paramName) {
|
||||
return paramToDefineId.get(paramName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the type of a particular define.
|
||||
*
|
||||
* @param defineId The define ID to lookup.
|
||||
* @return The type of the define, or null if not found.
|
||||
*/
|
||||
public VarType getDefineIdType(int defineId) {
|
||||
return defineId < defineTypes.size() ? defineTypes.get(defineId) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a define linked to a material parameter.
|
||||
* <p>
|
||||
* Any time the material parameter on the parent material is altered,
|
||||
* the appropriate define on the technique will be modified as well.
|
||||
* When set, the material parameter will be mapped to an integer define,
|
||||
* typically <code>1</code> if it is set, unless it is an integer or a float,
|
||||
* in which case it will converted into an integer.
|
||||
*
|
||||
* @param paramName The name of the material parameter to link to.
|
||||
* @param paramType The type of the material parameter to link to.
|
||||
* @param defineName The name of the define parameter, e.g. USE_LIGHTING
|
||||
*/
|
||||
public void addShaderParamDefine(String paramName, VarType paramType, String defineName){
|
||||
int defineId = defineNames.size();
|
||||
|
||||
if (defineId >= DefineList.MAX_DEFINES) {
|
||||
throw new IllegalStateException("Cannot have more than " +
|
||||
DefineList.MAX_DEFINES + " defines on a technique.");
|
||||
}
|
||||
|
||||
paramToDefineId.put(paramName, defineId);
|
||||
defineNames.add(defineName);
|
||||
defineTypes.add(paramType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an unmapped define which can only be set by define ID.
|
||||
*
|
||||
* Unmapped defines are used by technique renderers to
|
||||
* configure the shader internally before rendering.
|
||||
*
|
||||
* @param defineName The define name to create
|
||||
* @return The define ID of the created define
|
||||
*/
|
||||
public int addShaderUnmappedDefine(String defineName, VarType defineType) {
|
||||
int defineId = defineNames.size();
|
||||
|
||||
if (defineId >= DefineList.MAX_DEFINES) {
|
||||
throw new IllegalStateException("Cannot have more than " +
|
||||
DefineList.MAX_DEFINES + " defines on a technique.");
|
||||
}
|
||||
|
||||
defineNames.add(defineName);
|
||||
defineTypes.add(defineType);
|
||||
return defineId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the names of all defines declared on this technique definition.
|
||||
*
|
||||
* The defines are returned in order of declaration.
|
||||
*
|
||||
* @return the names of all defines declared.
|
||||
*/
|
||||
public String[] getDefineNames() {
|
||||
return defineNames.toArray(new String[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the types of all defines declared on this technique definition.
|
||||
*
|
||||
* The types are returned in order of declaration.
|
||||
*
|
||||
* @return the types of all defines declared.
|
||||
*/
|
||||
public VarType[] getDefineTypes() {
|
||||
return defineTypes.toArray(new VarType[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a define list with the size matching the number
|
||||
* of defines on this technique.
|
||||
*
|
||||
* @return a define list with the size matching the number
|
||||
* of defines on this technique.
|
||||
*/
|
||||
public DefineList createDefineList() {
|
||||
return new DefineList(defineNames.size());
|
||||
}
|
||||
|
||||
private Shader loadShader(AssetManager assetManager, EnumSet<Caps> rendererCaps, DefineList defines) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(shaderPrologue);
|
||||
defines.generateSource(sb, defineNames, defineTypes);
|
||||
String definesSourceCode = sb.toString();
|
||||
|
||||
Shader shader;
|
||||
if (isUsingShaderNodes()) {
|
||||
ShaderGenerator shaderGenerator = assetManager.getShaderGenerator(rendererCaps);
|
||||
if (shaderGenerator == null) {
|
||||
throw new UnsupportedOperationException("ShaderGenerator was not initialized, "
|
||||
+ "make sure assetManager.getGenerator(caps) has been called");
|
||||
}
|
||||
shaderGenerator.initialize(this);
|
||||
shader = shaderGenerator.generateShader(definesSourceCode);
|
||||
} else {
|
||||
shader = new Shader();
|
||||
for (ShaderType type : ShaderType.values()) {
|
||||
String language = shaderLanguages.get(type);
|
||||
String shaderSourceAssetName = shaderNames.get(type);
|
||||
if (language == null || shaderSourceAssetName == null) {
|
||||
continue;
|
||||
}
|
||||
String shaderSourceCode = (String) assetManager.loadAsset(shaderSourceAssetName);
|
||||
shader.addSource(type, shaderSourceAssetName, shaderSourceCode, definesSourceCode, language);
|
||||
}
|
||||
}
|
||||
|
||||
if (getWorldBindings() != null) {
|
||||
for (UniformBinding binding : getWorldBindings()) {
|
||||
shader.addUniformBinding(binding);
|
||||
}
|
||||
}
|
||||
|
||||
return shader;
|
||||
}
|
||||
|
||||
public Shader getShader(AssetManager assetManager, EnumSet<Caps> rendererCaps, DefineList defines) {
|
||||
Shader shader = definesToShaderMap.get(defines);
|
||||
if (shader == null) {
|
||||
shader = loadShader(assetManager, rendererCaps, defines);
|
||||
definesToShaderMap.put(defines.deepClone(), shader);
|
||||
}
|
||||
return shader;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the shaders that this technique definition will use.
|
||||
@ -301,71 +504,6 @@ public class TechniqueDef implements Savable {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the define name which the given material parameter influences.
|
||||
*
|
||||
* @param paramName The parameter name to look up
|
||||
* @return The define name
|
||||
*
|
||||
* @see #addShaderParamDefine(java.lang.String, java.lang.String)
|
||||
*/
|
||||
public String getShaderParamDefine(String paramName){
|
||||
if (defineParams == null) {
|
||||
return null;
|
||||
}
|
||||
return defineParams.get(paramName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a define linked to a material parameter.
|
||||
* <p>
|
||||
* Any time the material parameter on the parent material is altered,
|
||||
* the appropriate define on the technique will be modified as well.
|
||||
* See the method
|
||||
* {@link DefineList#set(java.lang.String, com.jme3.shader.VarType, java.lang.Object) }
|
||||
* on the exact details of how the material parameter changes the define.
|
||||
*
|
||||
* @param paramName The name of the material parameter to link to.
|
||||
* @param defineName The name of the define parameter, e.g. USE_LIGHTING
|
||||
*/
|
||||
public void addShaderParamDefine(String paramName, String defineName){
|
||||
if (defineParams == null) {
|
||||
defineParams = new HashMap<String, String>();
|
||||
}
|
||||
defineParams.put(paramName, defineName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link DefineList} for the preset defines.
|
||||
*
|
||||
* @return the {@link DefineList} for the preset defines.
|
||||
*
|
||||
* @see #addShaderPresetDefine(java.lang.String, com.jme3.shader.VarType, java.lang.Object)
|
||||
*/
|
||||
public DefineList getShaderPresetDefines() {
|
||||
return presetDefines;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a preset define.
|
||||
* <p>
|
||||
* Preset defines do not depend upon any parameters to be activated,
|
||||
* they are always passed to the shader as long as this technique is used.
|
||||
*
|
||||
* @param defineName The name of the define parameter, e.g. USE_LIGHTING
|
||||
* @param type The type of the define. See
|
||||
* {@link DefineList#set(java.lang.String, com.jme3.shader.VarType, java.lang.Object) }
|
||||
* to see why it matters.
|
||||
*
|
||||
* @param value The value of the define
|
||||
*/
|
||||
public void addShaderPresetDefine(String defineName, VarType type, Object value){
|
||||
if (presetDefines == null) {
|
||||
presetDefines = new DefineList();
|
||||
}
|
||||
presetDefines.set(defineName, type, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the fragment shader used by the technique, or null
|
||||
* if no fragment shader is specified.
|
||||
@ -467,7 +605,7 @@ public class TechniqueDef implements Savable {
|
||||
oc.write(shaderLanguages.get(Shader.ShaderType.TessellationControl), "tsctrlLanguage", null);
|
||||
oc.write(shaderLanguages.get(Shader.ShaderType.TessellationEvaluation), "tsevalLanguage", null);
|
||||
|
||||
oc.write(presetDefines, "presetDefines", null);
|
||||
oc.write(shaderPrologue, "shaderPrologue", null);
|
||||
oc.write(lightMode, "lightMode", LightMode.Disable);
|
||||
oc.write(shadowMode, "shadowMode", ShadowMode.Disable);
|
||||
oc.write(renderState, "renderState", null);
|
||||
@ -490,7 +628,7 @@ public class TechniqueDef implements Savable {
|
||||
shaderNames.put(Shader.ShaderType.Geometry,ic.readString("geomName", null));
|
||||
shaderNames.put(Shader.ShaderType.TessellationControl,ic.readString("tsctrlName", null));
|
||||
shaderNames.put(Shader.ShaderType.TessellationEvaluation,ic.readString("tsevalName", null));
|
||||
presetDefines = (DefineList) ic.readSavable("presetDefines", null);
|
||||
shaderPrologue = ic.readString("shaderPrologue", null);
|
||||
lightMode = ic.readEnum("lightMode", LightMode.class, LightMode.Disable);
|
||||
shadowMode = ic.readEnum("shadowMode", ShadowMode.class, ShadowMode.Disable);
|
||||
renderState = (RenderState) ic.readSavable("renderState", null);
|
||||
@ -547,9 +685,14 @@ public class TechniqueDef implements Savable {
|
||||
this.shaderGenerationInfo = shaderGenerationInfo;
|
||||
}
|
||||
|
||||
//todo: make toString return something usefull
|
||||
@Override
|
||||
public String toString() {
|
||||
return "TechniqueDef{" + "requiredCaps=" + requiredCaps + ", name=" + name /*+ ", vertName=" + vertName + ", fragName=" + fragName + ", vertLanguage=" + vertLanguage + ", fragLanguage=" + fragLanguage */+ ", presetDefines=" + presetDefines + ", usesNodes=" + usesNodes + ", shaderNodes=" + shaderNodes + ", shaderGenerationInfo=" + shaderGenerationInfo + ", renderState=" + renderState + ", forcedRenderState=" + forcedRenderState + ", lightMode=" + lightMode + ", shadowMode=" + shadowMode + ", defineParams=" + defineParams + ", worldBinds=" + worldBinds + ", noRender=" + noRender + '}';
|
||||
return "TechniqueDef[name=" + name
|
||||
+ ", requiredCaps=" + requiredCaps
|
||||
+ ", noRender=" + noRender
|
||||
+ ", lightMode=" + lightMode
|
||||
+ ", usesNodes=" + usesNodes
|
||||
+ ", renderState=" + renderState
|
||||
+ ", forcedRenderState=" + forcedRenderState + "]";
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2015 jMonkeyEngine
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package com.jme3.material.logic;
|
||||
|
||||
import com.jme3.asset.AssetManager;
|
||||
import com.jme3.light.AmbientLight;
|
||||
import com.jme3.light.Light;
|
||||
import com.jme3.light.LightList;
|
||||
import com.jme3.material.TechniqueDef;
|
||||
import com.jme3.math.ColorRGBA;
|
||||
import com.jme3.renderer.Caps;
|
||||
import com.jme3.renderer.RenderManager;
|
||||
import com.jme3.renderer.Renderer;
|
||||
import com.jme3.scene.Geometry;
|
||||
import com.jme3.scene.Mesh;
|
||||
import com.jme3.scene.instancing.InstancedGeometry;
|
||||
import com.jme3.shader.DefineList;
|
||||
import com.jme3.shader.Shader;
|
||||
import java.util.EnumSet;
|
||||
|
||||
public class DefaultTechniqueDefLogic implements TechniqueDefLogic {
|
||||
|
||||
protected final TechniqueDef techniqueDef;
|
||||
|
||||
public DefaultTechniqueDefLogic(TechniqueDef techniqueDef) {
|
||||
this.techniqueDef = techniqueDef;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Shader makeCurrent(AssetManager assetManager, RenderManager renderManager,
|
||||
EnumSet<Caps> rendererCaps, LightList lights, DefineList defines) {
|
||||
return techniqueDef.getShader(assetManager, rendererCaps, defines);
|
||||
}
|
||||
|
||||
public static void renderMeshFromGeometry(Renderer renderer, Geometry geom) {
|
||||
Mesh mesh = geom.getMesh();
|
||||
int lodLevel = geom.getLodLevel();
|
||||
if (geom instanceof InstancedGeometry) {
|
||||
InstancedGeometry instGeom = (InstancedGeometry) geom;
|
||||
renderer.renderMesh(mesh, lodLevel, instGeom.getActualNumInstances(),
|
||||
instGeom.getAllInstanceData());
|
||||
} else {
|
||||
renderer.renderMesh(mesh, lodLevel, 1, null);
|
||||
}
|
||||
}
|
||||
|
||||
protected static ColorRGBA getAmbientColor(LightList lightList, boolean removeLights, ColorRGBA ambientLightColor) {
|
||||
ambientLightColor.set(0, 0, 0, 1);
|
||||
for (int j = 0; j < lightList.size(); j++) {
|
||||
Light l = lightList.get(j);
|
||||
if (l instanceof AmbientLight) {
|
||||
ambientLightColor.addLocal(l.getColor());
|
||||
if (removeLights) {
|
||||
lightList.remove(l);
|
||||
}
|
||||
}
|
||||
}
|
||||
ambientLightColor.a = 1.0f;
|
||||
return ambientLightColor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(RenderManager renderManager, Shader shader, Geometry geometry, LightList lights) {
|
||||
Renderer renderer = renderManager.getRenderer();
|
||||
renderer.setShader(shader);
|
||||
renderMeshFromGeometry(renderer, geometry);
|
||||
}
|
||||
}
|
@ -0,0 +1,178 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2015 jMonkeyEngine
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package com.jme3.material.logic;
|
||||
|
||||
import com.jme3.asset.AssetManager;
|
||||
import com.jme3.light.AmbientLight;
|
||||
import com.jme3.light.DirectionalLight;
|
||||
import com.jme3.light.Light;
|
||||
import com.jme3.light.LightList;
|
||||
import com.jme3.light.PointLight;
|
||||
import com.jme3.light.SpotLight;
|
||||
import com.jme3.material.RenderState;
|
||||
import com.jme3.material.TechniqueDef;
|
||||
import com.jme3.math.ColorRGBA;
|
||||
import com.jme3.math.FastMath;
|
||||
import com.jme3.math.Quaternion;
|
||||
import com.jme3.math.Vector3f;
|
||||
import com.jme3.math.Vector4f;
|
||||
import com.jme3.renderer.Caps;
|
||||
import com.jme3.renderer.RenderManager;
|
||||
import com.jme3.renderer.Renderer;
|
||||
import com.jme3.scene.Geometry;
|
||||
import com.jme3.shader.DefineList;
|
||||
import com.jme3.shader.Shader;
|
||||
import com.jme3.shader.Uniform;
|
||||
import com.jme3.shader.VarType;
|
||||
import com.jme3.util.TempVars;
|
||||
import java.util.EnumSet;
|
||||
|
||||
public final class MultiPassLightingLogic extends DefaultTechniqueDefLogic {
|
||||
|
||||
private static final RenderState ADDITIVE_LIGHT = new RenderState();
|
||||
private static final Quaternion NULL_DIR_LIGHT = new Quaternion(0, -1, 0, -1);
|
||||
|
||||
private final ColorRGBA ambientLightColor = new ColorRGBA(0, 0, 0, 1);
|
||||
|
||||
static {
|
||||
ADDITIVE_LIGHT.setBlendMode(RenderState.BlendMode.AlphaAdditive);
|
||||
ADDITIVE_LIGHT.setDepthWrite(false);
|
||||
}
|
||||
|
||||
public MultiPassLightingLogic(TechniqueDef techniqueDef) {
|
||||
super(techniqueDef);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(RenderManager renderManager, Shader shader, Geometry geometry, LightList lights) {
|
||||
Renderer r = renderManager.getRenderer();
|
||||
Uniform lightDir = shader.getUniform("g_LightDirection");
|
||||
Uniform lightColor = shader.getUniform("g_LightColor");
|
||||
Uniform lightPos = shader.getUniform("g_LightPosition");
|
||||
Uniform ambientColor = shader.getUniform("g_AmbientLightColor");
|
||||
boolean isFirstLight = true;
|
||||
boolean isSecondLight = false;
|
||||
|
||||
getAmbientColor(lights, false, ambientLightColor);
|
||||
|
||||
for (int i = 0; i < lights.size(); i++) {
|
||||
Light l = lights.get(i);
|
||||
if (l instanceof AmbientLight) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isFirstLight) {
|
||||
// set ambient color for first light only
|
||||
ambientColor.setValue(VarType.Vector4, ambientLightColor);
|
||||
isFirstLight = false;
|
||||
isSecondLight = true;
|
||||
} else if (isSecondLight) {
|
||||
ambientColor.setValue(VarType.Vector4, ColorRGBA.Black);
|
||||
// apply additive blending for 2nd and future lights
|
||||
r.applyRenderState(ADDITIVE_LIGHT);
|
||||
isSecondLight = false;
|
||||
}
|
||||
|
||||
TempVars vars = TempVars.get();
|
||||
Quaternion tmpLightDirection = vars.quat1;
|
||||
Quaternion tmpLightPosition = vars.quat2;
|
||||
ColorRGBA tmpLightColor = vars.color;
|
||||
Vector4f tmpVec = vars.vect4f1;
|
||||
|
||||
ColorRGBA color = l.getColor();
|
||||
tmpLightColor.set(color);
|
||||
tmpLightColor.a = l.getType().getId();
|
||||
lightColor.setValue(VarType.Vector4, tmpLightColor);
|
||||
|
||||
switch (l.getType()) {
|
||||
case Directional:
|
||||
DirectionalLight dl = (DirectionalLight) l;
|
||||
Vector3f dir = dl.getDirection();
|
||||
//FIXME : there is an inconstency here due to backward
|
||||
//compatibility of the lighting shader.
|
||||
//The directional light direction is passed in the
|
||||
//LightPosition uniform. The lighting shader needs to be
|
||||
//reworked though in order to fix this.
|
||||
tmpLightPosition.set(dir.getX(), dir.getY(), dir.getZ(), -1);
|
||||
lightPos.setValue(VarType.Vector4, tmpLightPosition);
|
||||
tmpLightDirection.set(0, 0, 0, 0);
|
||||
lightDir.setValue(VarType.Vector4, tmpLightDirection);
|
||||
break;
|
||||
case Point:
|
||||
PointLight pl = (PointLight) l;
|
||||
Vector3f pos = pl.getPosition();
|
||||
float invRadius = pl.getInvRadius();
|
||||
|
||||
tmpLightPosition.set(pos.getX(), pos.getY(), pos.getZ(), invRadius);
|
||||
lightPos.setValue(VarType.Vector4, tmpLightPosition);
|
||||
tmpLightDirection.set(0, 0, 0, 0);
|
||||
lightDir.setValue(VarType.Vector4, tmpLightDirection);
|
||||
break;
|
||||
case Spot:
|
||||
SpotLight sl = (SpotLight) l;
|
||||
Vector3f pos2 = sl.getPosition();
|
||||
Vector3f dir2 = sl.getDirection();
|
||||
float invRange = sl.getInvSpotRange();
|
||||
float spotAngleCos = sl.getPackedAngleCos();
|
||||
|
||||
tmpLightPosition.set(pos2.getX(), pos2.getY(), pos2.getZ(), invRange);
|
||||
lightPos.setValue(VarType.Vector4, tmpLightPosition);
|
||||
|
||||
//We transform the spot direction in view space here to save 5 varying later in the lighting shader
|
||||
//one vec4 less and a vec4 that becomes a vec3
|
||||
//the downside is that spotAngleCos decoding happens now in the frag shader.
|
||||
tmpVec.set(dir2.getX(), dir2.getY(), dir2.getZ(), 0);
|
||||
renderManager.getCurrentCamera().getViewMatrix().mult(tmpVec, tmpVec);
|
||||
tmpLightDirection.set(tmpVec.getX(), tmpVec.getY(), tmpVec.getZ(), spotAngleCos);
|
||||
|
||||
lightDir.setValue(VarType.Vector4, tmpLightDirection);
|
||||
|
||||
break;
|
||||
default:
|
||||
throw new UnsupportedOperationException("Unknown type of light: " + l.getType());
|
||||
}
|
||||
vars.release();
|
||||
r.setShader(shader);
|
||||
renderMeshFromGeometry(r, geometry);
|
||||
}
|
||||
|
||||
if (isFirstLight) {
|
||||
// Either there are no lights at all, or only ambient lights.
|
||||
// Render a dummy "normal light" so we can see the ambient color.
|
||||
ambientColor.setValue(VarType.Vector4, getAmbientColor(lights, false, ambientLightColor));
|
||||
lightColor.setValue(VarType.Vector4, ColorRGBA.BlackNoAlpha);
|
||||
lightPos.setValue(VarType.Vector4, NULL_DIR_LIGHT);
|
||||
r.setShader(shader);
|
||||
renderMeshFromGeometry(r, geometry);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,218 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2015 jMonkeyEngine
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package com.jme3.material.logic;
|
||||
|
||||
import com.jme3.asset.AssetManager;
|
||||
import com.jme3.light.DirectionalLight;
|
||||
import com.jme3.light.Light;
|
||||
import com.jme3.light.LightList;
|
||||
import com.jme3.light.PointLight;
|
||||
import com.jme3.light.SpotLight;
|
||||
import com.jme3.material.RenderState;
|
||||
import com.jme3.material.RenderState.BlendMode;
|
||||
import com.jme3.material.TechniqueDef;
|
||||
import com.jme3.math.ColorRGBA;
|
||||
import com.jme3.math.Vector3f;
|
||||
import com.jme3.math.Vector4f;
|
||||
import com.jme3.renderer.Caps;
|
||||
import com.jme3.renderer.RenderManager;
|
||||
import com.jme3.renderer.Renderer;
|
||||
import com.jme3.scene.Geometry;
|
||||
import com.jme3.shader.DefineList;
|
||||
import com.jme3.shader.Shader;
|
||||
import com.jme3.shader.Uniform;
|
||||
import com.jme3.shader.VarType;
|
||||
import com.jme3.util.TempVars;
|
||||
import java.util.EnumSet;
|
||||
|
||||
public final class SinglePassLightingLogic extends DefaultTechniqueDefLogic {
|
||||
|
||||
private static final String DEFINE_SINGLE_PASS_LIGHTING = "SINGLE_PASS_LIGHTING";
|
||||
private static final String DEFINE_NB_LIGHTS = "NB_LIGHTS";
|
||||
private static final RenderState ADDITIVE_LIGHT = new RenderState();
|
||||
|
||||
private final ColorRGBA ambientLightColor = new ColorRGBA(0, 0, 0, 1);
|
||||
|
||||
static {
|
||||
ADDITIVE_LIGHT.setBlendMode(BlendMode.AlphaAdditive);
|
||||
ADDITIVE_LIGHT.setDepthWrite(false);
|
||||
}
|
||||
|
||||
private final int singlePassLightingDefineId;
|
||||
private final int nbLightsDefineId;
|
||||
|
||||
public SinglePassLightingLogic(TechniqueDef techniqueDef) {
|
||||
super(techniqueDef);
|
||||
singlePassLightingDefineId = techniqueDef.addShaderUnmappedDefine(DEFINE_SINGLE_PASS_LIGHTING, VarType.Boolean);
|
||||
nbLightsDefineId = techniqueDef.addShaderUnmappedDefine(DEFINE_NB_LIGHTS, VarType.Int);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Shader makeCurrent(AssetManager assetManager, RenderManager renderManager,
|
||||
EnumSet<Caps> rendererCaps, LightList lights, DefineList defines) {
|
||||
defines.set(nbLightsDefineId, renderManager.getSinglePassLightBatchSize() * 3);
|
||||
defines.set(singlePassLightingDefineId, true);
|
||||
return super.makeCurrent(assetManager, renderManager, rendererCaps, lights, defines);
|
||||
}
|
||||
|
||||
/**
|
||||
* Uploads the lights in the light list as two uniform arrays.<br/><br/> *
|
||||
* <p>
|
||||
* <code>uniform vec4 g_LightColor[numLights];</code><br/> //
|
||||
* g_LightColor.rgb is the diffuse/specular color of the light.<br/> //
|
||||
* g_Lightcolor.a is the type of light, 0 = Directional, 1 = Point, <br/> //
|
||||
* 2 = Spot. <br/> <br/>
|
||||
* <code>uniform vec4 g_LightPosition[numLights];</code><br/> //
|
||||
* g_LightPosition.xyz is the position of the light (for point lights)<br/>
|
||||
* // or the direction of the light (for directional lights).<br/> //
|
||||
* g_LightPosition.w is the inverse radius (1/r) of the light (for
|
||||
* attenuation) <br/> </p>
|
||||
*/
|
||||
protected int updateLightListUniforms(Shader shader, Geometry g, LightList lightList, int numLights, RenderManager rm, int startIndex) {
|
||||
if (numLights == 0) { // this shader does not do lighting, ignore.
|
||||
return 0;
|
||||
}
|
||||
|
||||
Uniform lightData = shader.getUniform("g_LightData");
|
||||
lightData.setVector4Length(numLights * 3);//8 lights * max 3
|
||||
Uniform ambientColor = shader.getUniform("g_AmbientLightColor");
|
||||
|
||||
|
||||
if (startIndex != 0) {
|
||||
// apply additive blending for 2nd and future passes
|
||||
rm.getRenderer().applyRenderState(ADDITIVE_LIGHT);
|
||||
ambientColor.setValue(VarType.Vector4, ColorRGBA.Black);
|
||||
} else {
|
||||
ambientColor.setValue(VarType.Vector4, getAmbientColor(lightList, true, ambientLightColor));
|
||||
}
|
||||
|
||||
int lightDataIndex = 0;
|
||||
TempVars vars = TempVars.get();
|
||||
Vector4f tmpVec = vars.vect4f1;
|
||||
int curIndex;
|
||||
int endIndex = numLights + startIndex;
|
||||
for (curIndex = startIndex; curIndex < endIndex && curIndex < lightList.size(); curIndex++) {
|
||||
|
||||
Light l = lightList.get(curIndex);
|
||||
if (l.getType() == Light.Type.Ambient) {
|
||||
endIndex++;
|
||||
continue;
|
||||
}
|
||||
ColorRGBA color = l.getColor();
|
||||
//Color
|
||||
lightData.setVector4InArray(color.getRed(),
|
||||
color.getGreen(),
|
||||
color.getBlue(),
|
||||
l.getType().getId(),
|
||||
lightDataIndex);
|
||||
lightDataIndex++;
|
||||
|
||||
switch (l.getType()) {
|
||||
case Directional:
|
||||
DirectionalLight dl = (DirectionalLight) l;
|
||||
Vector3f dir = dl.getDirection();
|
||||
//Data directly sent in view space to avoid a matrix mult for each pixel
|
||||
tmpVec.set(dir.getX(), dir.getY(), dir.getZ(), 0.0f);
|
||||
rm.getCurrentCamera().getViewMatrix().mult(tmpVec, tmpVec);
|
||||
// tmpVec.divideLocal(tmpVec.w);
|
||||
// tmpVec.normalizeLocal();
|
||||
lightData.setVector4InArray(tmpVec.getX(), tmpVec.getY(), tmpVec.getZ(), -1, lightDataIndex);
|
||||
lightDataIndex++;
|
||||
//PADDING
|
||||
lightData.setVector4InArray(0, 0, 0, 0, lightDataIndex);
|
||||
lightDataIndex++;
|
||||
break;
|
||||
case Point:
|
||||
PointLight pl = (PointLight) l;
|
||||
Vector3f pos = pl.getPosition();
|
||||
float invRadius = pl.getInvRadius();
|
||||
tmpVec.set(pos.getX(), pos.getY(), pos.getZ(), 1.0f);
|
||||
rm.getCurrentCamera().getViewMatrix().mult(tmpVec, tmpVec);
|
||||
//tmpVec.divideLocal(tmpVec.w);
|
||||
lightData.setVector4InArray(tmpVec.getX(), tmpVec.getY(), tmpVec.getZ(), invRadius, lightDataIndex);
|
||||
lightDataIndex++;
|
||||
//PADDING
|
||||
lightData.setVector4InArray(0, 0, 0, 0, lightDataIndex);
|
||||
lightDataIndex++;
|
||||
break;
|
||||
case Spot:
|
||||
SpotLight sl = (SpotLight) l;
|
||||
Vector3f pos2 = sl.getPosition();
|
||||
Vector3f dir2 = sl.getDirection();
|
||||
float invRange = sl.getInvSpotRange();
|
||||
float spotAngleCos = sl.getPackedAngleCos();
|
||||
tmpVec.set(pos2.getX(), pos2.getY(), pos2.getZ(), 1.0f);
|
||||
rm.getCurrentCamera().getViewMatrix().mult(tmpVec, tmpVec);
|
||||
// tmpVec.divideLocal(tmpVec.w);
|
||||
lightData.setVector4InArray(tmpVec.getX(), tmpVec.getY(), tmpVec.getZ(), invRange, lightDataIndex);
|
||||
lightDataIndex++;
|
||||
|
||||
//We transform the spot direction in view space here to save 5 varying later in the lighting shader
|
||||
//one vec4 less and a vec4 that becomes a vec3
|
||||
//the downside is that spotAngleCos decoding happens now in the frag shader.
|
||||
tmpVec.set(dir2.getX(), dir2.getY(), dir2.getZ(), 0.0f);
|
||||
rm.getCurrentCamera().getViewMatrix().mult(tmpVec, tmpVec);
|
||||
tmpVec.normalizeLocal();
|
||||
lightData.setVector4InArray(tmpVec.getX(), tmpVec.getY(), tmpVec.getZ(), spotAngleCos, lightDataIndex);
|
||||
lightDataIndex++;
|
||||
break;
|
||||
default:
|
||||
throw new UnsupportedOperationException("Unknown type of light: " + l.getType());
|
||||
}
|
||||
}
|
||||
vars.release();
|
||||
//Padding of unsued buffer space
|
||||
while(lightDataIndex < numLights * 3) {
|
||||
lightData.setVector4InArray(0f, 0f, 0f, 0f, lightDataIndex);
|
||||
lightDataIndex++;
|
||||
}
|
||||
return curIndex;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(RenderManager renderManager, Shader shader, Geometry geometry, LightList lights) {
|
||||
int nbRenderedLights = 0;
|
||||
Renderer renderer = renderManager.getRenderer();
|
||||
int batchSize = renderManager.getSinglePassLightBatchSize();
|
||||
if (lights.size() == 0) {
|
||||
updateLightListUniforms(shader, geometry, lights, batchSize, renderManager, 0);
|
||||
renderer.setShader(shader);
|
||||
renderMeshFromGeometry(renderer, geometry);
|
||||
} else {
|
||||
while (nbRenderedLights < lights.size()) {
|
||||
nbRenderedLights = updateLightListUniforms(shader, geometry, lights, batchSize, renderManager, nbRenderedLights);
|
||||
renderer.setShader(shader);
|
||||
renderMeshFromGeometry(renderer, geometry);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,157 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2015 jMonkeyEngine
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package com.jme3.material.logic;
|
||||
|
||||
import com.jme3.asset.AssetManager;
|
||||
import com.jme3.light.DirectionalLight;
|
||||
import com.jme3.light.Light;
|
||||
import com.jme3.light.Light.Type;
|
||||
import com.jme3.light.LightList;
|
||||
import com.jme3.light.PointLight;
|
||||
import com.jme3.light.SpotLight;
|
||||
import com.jme3.material.TechniqueDef;
|
||||
import com.jme3.math.ColorRGBA;
|
||||
import com.jme3.math.Vector3f;
|
||||
import com.jme3.renderer.Caps;
|
||||
import com.jme3.renderer.RenderManager;
|
||||
import com.jme3.renderer.Renderer;
|
||||
import com.jme3.scene.Geometry;
|
||||
import com.jme3.shader.DefineList;
|
||||
import com.jme3.shader.Shader;
|
||||
import com.jme3.shader.Uniform;
|
||||
import com.jme3.shader.VarType;
|
||||
import java.util.ArrayList;
|
||||
import java.util.EnumSet;
|
||||
|
||||
/**
|
||||
* Rendering logic for static pass.
|
||||
*
|
||||
* @author Kirill Vainer
|
||||
*/
|
||||
public final class StaticPassLightingLogic extends DefaultTechniqueDefLogic {
|
||||
|
||||
private static final String DEFINE_NUM_DIR_LIGHTS = "NUM_DIR_LIGHTS";
|
||||
private static final String DEFINE_NUM_POINT_LIGHTS = "NUM_POINT_LIGHTS";
|
||||
private static final String DEFINE_NUM_SPOT_LIGHTS = "NUM_SPOT_LIGHTS";
|
||||
|
||||
private final int numDirLightsDefineId;
|
||||
private final int numPointLightsDefineId;
|
||||
private final int numSpotLightsDefineId;
|
||||
|
||||
private final ArrayList<DirectionalLight> tempDirLights = new ArrayList<DirectionalLight>();
|
||||
private final ArrayList<PointLight> tempPointLights = new ArrayList<PointLight>();
|
||||
private final ArrayList<SpotLight> tempSpotLights = new ArrayList<SpotLight>();
|
||||
|
||||
private final ColorRGBA ambientLightColor = new ColorRGBA(0, 0, 0, 1);
|
||||
|
||||
public StaticPassLightingLogic(TechniqueDef techniqueDef) {
|
||||
super(techniqueDef);
|
||||
|
||||
numDirLightsDefineId = techniqueDef.addShaderUnmappedDefine(DEFINE_NUM_DIR_LIGHTS, VarType.Int);
|
||||
numPointLightsDefineId = techniqueDef.addShaderUnmappedDefine(DEFINE_NUM_POINT_LIGHTS, VarType.Int);
|
||||
numSpotLightsDefineId = techniqueDef.addShaderUnmappedDefine(DEFINE_NUM_SPOT_LIGHTS, VarType.Int);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Shader makeCurrent(AssetManager assetManager, RenderManager renderManager,
|
||||
EnumSet<Caps> rendererCaps, LightList lights, DefineList defines) {
|
||||
|
||||
// TODO: if it ever changes that render isn't called
|
||||
// right away with the same geometry after makeCurrent, it would be
|
||||
// a problem.
|
||||
// Do a radix sort.
|
||||
tempDirLights.clear();
|
||||
tempPointLights.clear();
|
||||
tempSpotLights.clear();
|
||||
for (Light light : lights) {
|
||||
switch (light.getType()) {
|
||||
case Directional:
|
||||
tempDirLights.add((DirectionalLight) light);
|
||||
break;
|
||||
case Point:
|
||||
tempPointLights.add((PointLight) light);
|
||||
break;
|
||||
case Spot:
|
||||
tempSpotLights.add((SpotLight) light);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
defines.set(numDirLightsDefineId, tempDirLights.size());
|
||||
defines.set(numPointLightsDefineId, tempPointLights.size());
|
||||
defines.set(numSpotLightsDefineId, tempSpotLights.size());
|
||||
|
||||
return techniqueDef.getShader(assetManager, rendererCaps, defines);
|
||||
}
|
||||
|
||||
private void updateLightListUniforms(Shader shader, LightList lights) {
|
||||
Uniform ambientColor = shader.getUniform("g_AmbientLightColor");
|
||||
ambientColor.setValue(VarType.Vector4, getAmbientColor(lights, true, ambientLightColor));
|
||||
|
||||
Uniform lightData = shader.getUniform("g_LightData");
|
||||
|
||||
int index = 0;
|
||||
for (DirectionalLight light : tempDirLights) {
|
||||
ColorRGBA color = light.getColor();
|
||||
Vector3f dir = light.getDirection();
|
||||
lightData.setVector4InArray(color.r, color.g, color.b, 1f, index++);
|
||||
lightData.setVector4InArray(dir.x, dir.y, dir.z, 1f, index++);
|
||||
}
|
||||
|
||||
for (PointLight light : tempPointLights) {
|
||||
ColorRGBA color = light.getColor();
|
||||
Vector3f pos = light.getPosition();
|
||||
lightData.setVector4InArray(color.r, color.g, color.b, 1f, index++);
|
||||
lightData.setVector4InArray(pos.x, pos.y, pos.z, 1f, index++);
|
||||
}
|
||||
|
||||
for (SpotLight light : tempSpotLights) {
|
||||
ColorRGBA color = light.getColor();
|
||||
Vector3f pos = light.getPosition();
|
||||
Vector3f dir = light.getDirection();
|
||||
float invRange = light.getInvSpotRange();
|
||||
float spotAngleCos = light.getPackedAngleCos();
|
||||
lightData.setVector4InArray(color.r, color.g, color.b, 1f, index++);
|
||||
lightData.setVector4InArray(pos.x, pos.y, pos.z, invRange, index++);
|
||||
lightData.setVector4InArray(dir.x, dir.y, dir.z, spotAngleCos, index++);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(RenderManager renderManager, Shader shader, Geometry geometry, LightList lights) {
|
||||
Renderer renderer = renderManager.getRenderer();
|
||||
updateLightListUniforms(shader, lights);
|
||||
renderer.setShader(shader);
|
||||
renderMeshFromGeometry(renderer, geometry);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2015 jMonkeyEngine
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package com.jme3.material.logic;
|
||||
|
||||
import com.jme3.asset.AssetManager;
|
||||
import com.jme3.light.LightList;
|
||||
import com.jme3.material.TechniqueDef.LightMode;
|
||||
import com.jme3.renderer.Caps;
|
||||
import com.jme3.renderer.RenderManager;
|
||||
import com.jme3.scene.Geometry;
|
||||
import com.jme3.shader.DefineList;
|
||||
import com.jme3.shader.Shader;
|
||||
import com.jme3.shader.Uniform;
|
||||
import com.jme3.shader.UniformBinding;
|
||||
import com.jme3.texture.Texture;
|
||||
import java.util.EnumSet;
|
||||
|
||||
/**
|
||||
* <code>TechniqueDefLogic</code> is used to customize how
|
||||
* a material should be rendered.
|
||||
*
|
||||
* Typically used to implement {@link LightMode lighting modes}.
|
||||
* Implementations can register
|
||||
* {@link TechniqueDef#addShaderUnmappedDefine(java.lang.String) unmapped defines}
|
||||
* in their constructor and then later set them based on the geometry
|
||||
* or light environment being rendered.
|
||||
*
|
||||
* @author Kirill Vainer
|
||||
*/
|
||||
public interface TechniqueDefLogic {
|
||||
|
||||
/**
|
||||
* Determine the shader to use for the given geometry / material combination.
|
||||
*
|
||||
* @param assetManager The asset manager to use for loading shader source code,
|
||||
* shader nodes, and and lookup textures.
|
||||
* @param renderManager The render manager for which rendering is to be performed.
|
||||
* @param rendererCaps Renderer capabilities. The returned shader must
|
||||
* support these capabilities.
|
||||
* @param lights The lights with which the geometry shall be rendered. This
|
||||
* list must not include culled lights.
|
||||
* @param defines The define list used by the technique, any
|
||||
* {@link TechniqueDef#addShaderUnmappedDefine(java.lang.String) unmapped defines}
|
||||
* should be set here to change shader behavior.
|
||||
*
|
||||
* @return The shader to use for rendering.
|
||||
*/
|
||||
public Shader makeCurrent(AssetManager assetManager, RenderManager renderManager,
|
||||
EnumSet<Caps> rendererCaps, LightList lights, DefineList defines);
|
||||
|
||||
/**
|
||||
* Requests that the <code>TechniqueDefLogic</code> renders the given geometry.
|
||||
*
|
||||
* Fixed material functionality such as {@link RenderState},
|
||||
* {@link MatParam material parameters}, and
|
||||
* {@link UniformBinding uniform bindings}
|
||||
* have already been applied by the material, however,
|
||||
* {@link RenderState}, {@link Uniform uniforms}, {@link Texture textures},
|
||||
* can still be overriden.
|
||||
*
|
||||
* @param renderManager The render manager to perform the rendering against.
|
||||
* * @param shader The shader that was selected by this logic in
|
||||
* {@link #makeCurrent(com.jme3.asset.AssetManager, com.jme3.renderer.RenderManager, java.util.EnumSet, com.jme3.shader.DefineList)}.
|
||||
* @param geometry The geometry to render
|
||||
* @param lights Lights which influence the geometry.
|
||||
*/
|
||||
public void render(RenderManager renderManager, Shader shader, Geometry geometry, LightList lights);
|
||||
}
|
@ -90,7 +90,7 @@ public class Spline implements Savable {
|
||||
type = splineType;
|
||||
this.curveTension = curveTension;
|
||||
this.cycle = cycle;
|
||||
this.computeTotalLentgh();
|
||||
this.computeTotalLength();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -116,7 +116,7 @@ public class Spline implements Savable {
|
||||
this.controlPoints.addAll(controlPoints);
|
||||
this.curveTension = curveTension;
|
||||
this.cycle = cycle;
|
||||
this.computeTotalLentgh();
|
||||
this.computeTotalLength();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -144,7 +144,7 @@ public class Spline implements Savable {
|
||||
this.weights[i] = controlPoint.w;
|
||||
}
|
||||
CurveAndSurfaceMath.prepareNurbsKnots(knots, basisFunctionDegree);
|
||||
this.computeTotalLentgh();
|
||||
this.computeTotalLength();
|
||||
}
|
||||
|
||||
private void initCatmullRomWayPoints(List<Vector3f> list) {
|
||||
@ -186,7 +186,7 @@ public class Spline implements Savable {
|
||||
controlPoints.add(controlPoints.get(0).clone());
|
||||
}
|
||||
if (controlPoints.size() > 1) {
|
||||
this.computeTotalLentgh();
|
||||
this.computeTotalLength();
|
||||
}
|
||||
}
|
||||
|
||||
@ -197,7 +197,7 @@ public class Spline implements Savable {
|
||||
public void removeControlPoint(Vector3f controlPoint) {
|
||||
controlPoints.remove(controlPoint);
|
||||
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.
|
||||
*/
|
||||
private void computeTotalLentgh() {
|
||||
private void computeTotalLength() {
|
||||
totalLength = 0;
|
||||
float l = 0;
|
||||
if (segmentsLength == null) {
|
||||
@ -317,7 +317,7 @@ public class Spline implements Savable {
|
||||
public void setCurveTension(float curveTension) {
|
||||
this.curveTension = curveTension;
|
||||
if(type==SplineType.CatmullRom && !getControlPoints().isEmpty()) {
|
||||
this.computeTotalLentgh();
|
||||
this.computeTotalLength();
|
||||
}
|
||||
}
|
||||
|
||||
@ -342,7 +342,7 @@ public class Spline implements Savable {
|
||||
controlPoints.add(controlPoints.get(0));
|
||||
}
|
||||
this.cycle = cycle;
|
||||
this.computeTotalLentgh();
|
||||
this.computeTotalLength();
|
||||
} else {
|
||||
this.cycle = cycle;
|
||||
}
|
||||
@ -369,7 +369,7 @@ public class Spline implements Savable {
|
||||
*/
|
||||
public void setType(SplineType type) {
|
||||
this.type = type;
|
||||
this.computeTotalLentgh();
|
||||
this.computeTotalLength();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -435,10 +435,14 @@ public class Spline implements Savable {
|
||||
OutputCapsule oc = ex.getCapsule(this);
|
||||
oc.writeSavableArrayList((ArrayList) controlPoints, "controlPoints", null);
|
||||
oc.write(type, "type", SplineType.CatmullRom);
|
||||
float list[] = new float[segmentsLength.size()];
|
||||
|
||||
float list[] = null;
|
||||
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(totalLength, "totalLength", 0);
|
||||
@ -454,7 +458,7 @@ public class Spline implements Savable {
|
||||
public void read(JmeImporter im) throws IOException {
|
||||
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);
|
||||
if (list != null) {
|
||||
segmentsLength = new ArrayList<Float>();
|
||||
|
@ -101,7 +101,7 @@ public class RenderContext {
|
||||
public float pointSize = 1;
|
||||
|
||||
/**
|
||||
* @see Mesh#setLineWidth(float)
|
||||
* @see RenderState#setLineWidth(float)
|
||||
*/
|
||||
public float lineWidth = 1;
|
||||
|
||||
|
@ -34,8 +34,12 @@ package com.jme3.renderer;
|
||||
import com.jme3.light.DefaultLightFilter;
|
||||
import com.jme3.light.LightFilter;
|
||||
import com.jme3.light.LightList;
|
||||
import com.jme3.material.*;
|
||||
import com.jme3.math.Matrix4f;
|
||||
import com.jme3.material.Material;
|
||||
import com.jme3.material.MaterialDef;
|
||||
import com.jme3.material.RenderState;
|
||||
import com.jme3.material.Technique;
|
||||
import com.jme3.material.TechniqueDef;
|
||||
import com.jme3.math.*;
|
||||
import com.jme3.post.SceneProcessor;
|
||||
import com.jme3.profile.AppProfiler;
|
||||
import com.jme3.profile.AppStep;
|
||||
@ -45,13 +49,12 @@ import com.jme3.renderer.queue.RenderQueue;
|
||||
import com.jme3.renderer.queue.RenderQueue.Bucket;
|
||||
import com.jme3.renderer.queue.RenderQueue.ShadowMode;
|
||||
import com.jme3.scene.*;
|
||||
import com.jme3.shader.Uniform;
|
||||
import com.jme3.shader.Shader;
|
||||
import com.jme3.shader.UniformBinding;
|
||||
import com.jme3.shader.UniformBindingManager;
|
||||
import com.jme3.system.NullRenderer;
|
||||
import com.jme3.system.Timer;
|
||||
import com.jme3.util.SafeArrayList;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
@ -480,8 +483,8 @@ public class RenderManager {
|
||||
* Updates the given list of uniforms with {@link UniformBinding uniform bindings}
|
||||
* based on the current world state.
|
||||
*/
|
||||
public void updateUniformBindings(List<Uniform> params) {
|
||||
uniformBindingManager.updateUniformBindings(params);
|
||||
public void updateUniformBindings(Shader shader) {
|
||||
uniformBindingManager.updateUniformBindings(shader);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -531,6 +534,7 @@ public class RenderManager {
|
||||
lightList = filteredLightList;
|
||||
}
|
||||
|
||||
|
||||
//if forcedTechnique we try to force it for render,
|
||||
//if it does not exists in the mat def, we check for forcedMaterial and render the geom if not null
|
||||
//else the geom is not rendered
|
||||
@ -612,7 +616,9 @@ public class RenderManager {
|
||||
|
||||
gm.getMaterial().preload(this);
|
||||
Mesh mesh = gm.getMesh();
|
||||
if (mesh != null) {
|
||||
if (mesh != null
|
||||
&& mesh.getVertexCount() != 0
|
||||
&& mesh.getTriangleCount() != 0) {
|
||||
for (VertexBuffer vb : mesh.getBufferList().getArray()) {
|
||||
if (vb.getData() != null && vb.getUsage() != VertexBuffer.Usage.CpuOnly) {
|
||||
renderer.updateBufferData(vb);
|
||||
@ -637,8 +643,10 @@ public class RenderManager {
|
||||
* <p>
|
||||
* In addition to enqueuing the visible geometries, this method
|
||||
* also scenes which cast or receive shadows, by putting them into the
|
||||
* RenderQueue's {@link RenderQueue#renderShadowQueue(GeometryList, RenderManager, Camera, boolean) shadow queue}.
|
||||
* Each Spatial which has its {@link Spatial#setShadowMode(com.jme3.renderer.queue.RenderQueue.ShadowMode) shadow mode}
|
||||
* RenderQueue's
|
||||
* {@link RenderQueue#addToShadowQueue(com.jme3.scene.Geometry, com.jme3.renderer.queue.RenderQueue.ShadowMode)
|
||||
* shadow queue}. Each Spatial which has its
|
||||
* {@link Spatial#setShadowMode(com.jme3.renderer.queue.RenderQueue.ShadowMode) shadow mode}
|
||||
* set to not off, will be put into the appropriate shadow queue, note that
|
||||
* this process does not check for frustum culling on any
|
||||
* {@link ShadowMode#Cast shadow casters}, as they don't have to be
|
||||
@ -985,7 +993,8 @@ public class RenderManager {
|
||||
* (see {@link #renderTranslucentQueue(com.jme3.renderer.ViewPort) })</li>
|
||||
* <li>If any objects remained in the render queue, they are removed
|
||||
* from the queue. This is generally objects added to the
|
||||
* {@link RenderQueue#renderShadowQueue(GeometryList, RenderManager, Camera, boolean) shadow queue}
|
||||
* {@link RenderQueue#renderShadowQueue(com.jme3.renderer.queue.RenderQueue.ShadowMode, com.jme3.renderer.RenderManager, com.jme3.renderer.Camera, boolean)
|
||||
* shadow queue}
|
||||
* which were not rendered because of a missing shadow renderer.</li>
|
||||
* </ul>
|
||||
*
|
||||
|
@ -58,6 +58,7 @@ public interface GL3 extends GL2 {
|
||||
public void glBindFragDataLocation(int param1, int param2, String param3); /// GL3+
|
||||
public void glBindVertexArray(int param1); /// GL3+
|
||||
public void glDeleteVertexArrays(IntBuffer arrays); /// GL3+
|
||||
public void glFramebufferTextureLayer(int param1, int param2, int param3, int param4, int param5); /// GL3+
|
||||
public void glGenVertexArrays(IntBuffer param1); /// GL3+
|
||||
public String glGetString(int param1, int param2); /// GL3+
|
||||
}
|
||||
|
@ -94,4 +94,10 @@ public class GLDebugDesktop extends GLDebugES implements GL2, GL3, GL4 {
|
||||
gl4.glPatchParameter(count);
|
||||
checkError();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void glFramebufferTextureLayer(int param1, int param2, int param3, int param4, int param5) {
|
||||
gl3.glFramebufferTextureLayer(param1, param2, param3, param4, param5);
|
||||
checkError();
|
||||
}
|
||||
}
|
||||
|
@ -474,6 +474,17 @@ public final class GLRenderer implements Renderer {
|
||||
{
|
||||
sb.append("\t").append(cap.toString()).append("\n");
|
||||
}
|
||||
|
||||
sb.append("\nHardware limits: \n");
|
||||
for (Limits limit : Limits.values()) {
|
||||
Integer value = limits.get(limit);
|
||||
if (value == null) {
|
||||
value = 0;
|
||||
}
|
||||
sb.append("\t").append(limit.name()).append(" = ")
|
||||
.append(value).append("\n");
|
||||
}
|
||||
|
||||
logger.log(Level.FINE, sb.toString());
|
||||
}
|
||||
|
||||
@ -779,6 +790,10 @@ public final class GLRenderer implements Renderer {
|
||||
gl.glDisable(GL.GL_STENCIL_TEST);
|
||||
}
|
||||
}
|
||||
if (context.lineWidth != state.getLineWidth()) {
|
||||
gl.glLineWidth(state.getLineWidth());
|
||||
context.lineWidth = state.getLineWidth();
|
||||
}
|
||||
}
|
||||
|
||||
private int convertStencilOperation(StencilOperation stencilOp) {
|
||||
@ -960,12 +975,12 @@ public final class GLRenderer implements Renderer {
|
||||
gl.glUniform1i(loc, b.booleanValue() ? GL.GL_TRUE : GL.GL_FALSE);
|
||||
break;
|
||||
case Matrix3:
|
||||
fb = (FloatBuffer) uniform.getValue();
|
||||
fb = uniform.getMultiData();
|
||||
assert fb.remaining() == 9;
|
||||
gl.glUniformMatrix3(loc, false, fb);
|
||||
break;
|
||||
case Matrix4:
|
||||
fb = (FloatBuffer) uniform.getValue();
|
||||
fb = uniform.getMultiData();
|
||||
assert fb.remaining() == 16;
|
||||
gl.glUniformMatrix4(loc, false, fb);
|
||||
break;
|
||||
@ -974,23 +989,23 @@ public final class GLRenderer implements Renderer {
|
||||
gl.glUniform1(loc, ib);
|
||||
break;
|
||||
case FloatArray:
|
||||
fb = (FloatBuffer) uniform.getValue();
|
||||
fb = uniform.getMultiData();
|
||||
gl.glUniform1(loc, fb);
|
||||
break;
|
||||
case Vector2Array:
|
||||
fb = (FloatBuffer) uniform.getValue();
|
||||
fb = uniform.getMultiData();
|
||||
gl.glUniform2(loc, fb);
|
||||
break;
|
||||
case Vector3Array:
|
||||
fb = (FloatBuffer) uniform.getValue();
|
||||
fb = uniform.getMultiData();
|
||||
gl.glUniform3(loc, fb);
|
||||
break;
|
||||
case Vector4Array:
|
||||
fb = (FloatBuffer) uniform.getValue();
|
||||
fb = uniform.getMultiData();
|
||||
gl.glUniform4(loc, fb);
|
||||
break;
|
||||
case Matrix4Array:
|
||||
fb = (FloatBuffer) uniform.getValue();
|
||||
fb = uniform.getMultiData();
|
||||
gl.glUniformMatrix4(loc, false, fb);
|
||||
break;
|
||||
case Int:
|
||||
@ -1438,11 +1453,19 @@ public final class GLRenderer implements Renderer {
|
||||
setupTextureParams(0, tex);
|
||||
}
|
||||
|
||||
if (rb.getLayer() < 0){
|
||||
glfbo.glFramebufferTexture2DEXT(GLFbo.GL_FRAMEBUFFER_EXT,
|
||||
convertAttachmentSlot(rb.getSlot()),
|
||||
convertTextureType(tex.getType(), image.getMultiSamples(), rb.getFace()),
|
||||
image.getId(),
|
||||
0);
|
||||
} else {
|
||||
gl3.glFramebufferTextureLayer(GLFbo.GL_FRAMEBUFFER_EXT,
|
||||
convertAttachmentSlot(rb.getSlot()),
|
||||
image.getId(),
|
||||
0,
|
||||
rb.getLayer());
|
||||
}
|
||||
}
|
||||
|
||||
public void updateFrameBufferAttachment(FrameBuffer fb, RenderBuffer rb) {
|
||||
@ -2677,12 +2700,15 @@ public final class GLRenderer implements Renderer {
|
||||
}
|
||||
|
||||
public void renderMesh(Mesh mesh, int lod, int count, VertexBuffer[] instanceData) {
|
||||
if (mesh.getVertexCount() == 0) {
|
||||
if (mesh.getVertexCount() == 0 || mesh.getTriangleCount() == 0 || count == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (count > 1 && !caps.contains(Caps.MeshInstancing)) {
|
||||
throw new RendererException("Mesh instancing is not supported by the video hardware");
|
||||
}
|
||||
|
||||
if (context.lineWidth != mesh.getLineWidth()) {
|
||||
if (mesh.getLineWidth() != 1f && context.lineWidth != mesh.getLineWidth()) {
|
||||
gl.glLineWidth(mesh.getLineWidth());
|
||||
context.lineWidth = mesh.getLineWidth();
|
||||
}
|
||||
|
@ -99,6 +99,16 @@ public class GeometryList implements Iterable<Geometry>{
|
||||
return size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the element at the given index.
|
||||
*
|
||||
* @param index The index to set
|
||||
* @param value The value
|
||||
*/
|
||||
public void set(int index, Geometry value) {
|
||||
geometries[index] = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the element at the given index.
|
||||
*
|
||||
|
@ -69,11 +69,12 @@ public class OpaqueComparator implements GeometryComparator {
|
||||
return spat.queueDistance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compare(Geometry o1, Geometry o2) {
|
||||
Material m1 = o1.getMaterial();
|
||||
Material m2 = o2.getMaterial();
|
||||
|
||||
int compareResult = m2.getSortId() - m1.getSortId();
|
||||
int compareResult = Integer.compare(m1.getSortId(), m2.getSortId());
|
||||
if (compareResult == 0){
|
||||
// use the same shader.
|
||||
// sort front-to-back then.
|
||||
|
@ -39,6 +39,7 @@ import com.jme3.export.JmeExporter;
|
||||
import com.jme3.export.JmeImporter;
|
||||
import com.jme3.export.OutputCapsule;
|
||||
import com.jme3.export.binary.BinaryImporter;
|
||||
import com.jme3.util.clone.Cloner;
|
||||
import com.jme3.util.SafeArrayList;
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
@ -70,6 +71,20 @@ public class AssetLinkNode extends Node {
|
||||
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
|
||||
* 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.util.SafeArrayList;
|
||||
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.
|
||||
@ -383,7 +385,7 @@ public class BatchNode extends GeometryGroupNode {
|
||||
maxVertCount = geom.getVertexCount();
|
||||
}
|
||||
Mesh.Mode listMode;
|
||||
float listLineWidth = 1f;
|
||||
//float listLineWidth = 1f;
|
||||
int components;
|
||||
switch (geom.getMesh().getMode()) {
|
||||
case Points:
|
||||
@ -394,7 +396,7 @@ public class BatchNode extends GeometryGroupNode {
|
||||
case LineStrip:
|
||||
case Lines:
|
||||
listMode = Mesh.Mode.Lines;
|
||||
listLineWidth = geom.getMesh().getLineWidth();
|
||||
//listLineWidth = geom.getMesh().getLineWidth();
|
||||
components = 2;
|
||||
break;
|
||||
case TriangleFan:
|
||||
@ -426,19 +428,20 @@ public class BatchNode extends GeometryGroupNode {
|
||||
+ " primitive types: " + mode + " != " + listMode);
|
||||
}
|
||||
mode = listMode;
|
||||
if (mode == Mesh.Mode.Lines) {
|
||||
if (lineWidth != 1f && listLineWidth != lineWidth) {
|
||||
throw new UnsupportedOperationException("When using Mesh Line mode, cannot combine meshes with different line width "
|
||||
+ lineWidth + " != " + listLineWidth);
|
||||
}
|
||||
lineWidth = listLineWidth;
|
||||
}
|
||||
//Not needed anymore as lineWidth is now in RenderState and will be taken into account when merging according to the material
|
||||
// if (mode == Mesh.Mode.Lines) {
|
||||
// if (lineWidth != 1f && listLineWidth != lineWidth) {
|
||||
// throw new UnsupportedOperationException("When using Mesh Line mode, cannot combine meshes with different line width "
|
||||
// + lineWidth + " != " + listLineWidth);
|
||||
// }
|
||||
// lineWidth = listLineWidth;
|
||||
// }
|
||||
compsForBuf[VertexBuffer.Type.Index.ordinal()] = components;
|
||||
}
|
||||
|
||||
outMesh.setMaxNumWeights(maxWeights);
|
||||
outMesh.setMode(mode);
|
||||
outMesh.setLineWidth(lineWidth);
|
||||
//outMesh.setLineWidth(lineWidth);
|
||||
if (totalVerts >= 65536) {
|
||||
// make sure we create an UnsignedInt buffer so we can fit all of the meshes
|
||||
formatForBuf[VertexBuffer.Type.Index.ordinal()] = VertexBuffer.Format.UnsignedInt;
|
||||
@ -661,7 +664,7 @@ public class BatchNode extends GeometryGroupNode {
|
||||
vars.release();
|
||||
}
|
||||
|
||||
protected class Batch {
|
||||
protected class Batch implements JmeCloneable {
|
||||
/**
|
||||
* update the batchesByGeom map for this batch with the given List of geometries
|
||||
* @param list
|
||||
@ -678,6 +681,21 @@ public class BatchNode extends GeometryGroupNode {
|
||||
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) {
|
||||
@ -704,6 +722,26 @@ public class BatchNode extends GeometryGroupNode {
|
||||
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
|
||||
public int collideWith(Collidable other, CollisionResults results) {
|
||||
int total = 0;
|
||||
|
@ -36,6 +36,7 @@ import com.jme3.export.JmeImporter;
|
||||
import com.jme3.renderer.Camera;
|
||||
import com.jme3.scene.control.CameraControl;
|
||||
import com.jme3.scene.control.CameraControl.ControlDirection;
|
||||
import com.jme3.util.clone.Cloner;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
@ -94,6 +95,19 @@ public class CameraNode extends Node {
|
||||
// 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
|
||||
public void read(JmeImporter im) throws IOException {
|
||||
super.read(im);
|
||||
|
@ -43,6 +43,8 @@ import com.jme3.material.Material;
|
||||
import com.jme3.math.Matrix4f;
|
||||
import com.jme3.renderer.Camera;
|
||||
import com.jme3.scene.VertexBuffer.Type;
|
||||
import com.jme3.util.clone.Cloner;
|
||||
import com.jme3.util.clone.IdentityCloneFunction;
|
||||
import com.jme3.util.TempVars;
|
||||
import java.io.IOException;
|
||||
import java.util.Queue;
|
||||
@ -491,6 +493,13 @@ public class Geometry extends Spatial {
|
||||
*/
|
||||
@Override
|
||||
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);
|
||||
|
||||
// This geometry is managed,
|
||||
@ -534,11 +543,58 @@ public class Geometry extends Spatial {
|
||||
*/
|
||||
@Override
|
||||
public Spatial deepClone() {
|
||||
return super.deepClone();
|
||||
}
|
||||
|
||||
public Spatial oldDeepClone() {
|
||||
Geometry geomClone = clone(true);
|
||||
geomClone.mesh = mesh.deepClone();
|
||||
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
|
||||
public void write(JmeExporter ex) throws IOException {
|
||||
super.write(ex);
|
||||
|
@ -36,6 +36,7 @@ import com.jme3.export.JmeImporter;
|
||||
import com.jme3.light.Light;
|
||||
import com.jme3.scene.control.LightControl;
|
||||
import com.jme3.scene.control.LightControl.ControlDirection;
|
||||
import com.jme3.util.clone.Cloner;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
@ -94,6 +95,19 @@ public class LightNode extends Node {
|
||||
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
|
||||
public void read(JmeImporter im) throws IOException {
|
||||
super.read(im);
|
||||
|
@ -37,6 +37,7 @@ import com.jme3.collision.Collidable;
|
||||
import com.jme3.collision.CollisionResults;
|
||||
import com.jme3.collision.bih.BIHTree;
|
||||
import com.jme3.export.*;
|
||||
import com.jme3.material.Material;
|
||||
import com.jme3.material.RenderState;
|
||||
import com.jme3.math.Matrix4f;
|
||||
import com.jme3.math.Triangle;
|
||||
@ -50,6 +51,8 @@ import com.jme3.util.BufferUtils;
|
||||
import com.jme3.util.IntMap;
|
||||
import com.jme3.util.IntMap.Entry;
|
||||
import com.jme3.util.SafeArrayList;
|
||||
import com.jme3.util.clone.Cloner;
|
||||
import com.jme3.util.clone.JmeCloneable;
|
||||
import java.io.IOException;
|
||||
import java.nio.*;
|
||||
import java.util.ArrayList;
|
||||
@ -65,13 +68,13 @@ import java.util.ArrayList;
|
||||
* Points can also be used for {@link RenderState#setPointSprite(boolean) point
|
||||
* sprite} mode.</li>
|
||||
* <li>Lines - 2 vertices represent a line segment, with the width specified
|
||||
* via {@link Mesh#setLineWidth(float) }.</li>
|
||||
* via {@link Material#getAdditionalRenderState()} and {@link RenderState#setLineWidth(float)}.</li>
|
||||
* <li>Triangles - 3 vertices represent a solid triangle primitive. </li>
|
||||
* </ul>
|
||||
*
|
||||
* @author Kirill Vainer
|
||||
*/
|
||||
public class Mesh implements Savable, Cloneable {
|
||||
public class Mesh implements Savable, Cloneable, JmeCloneable {
|
||||
|
||||
/**
|
||||
* The mode of the Mesh specifies both the type of primitive represented
|
||||
@ -86,7 +89,7 @@ public class Mesh implements Savable, Cloneable {
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
Lines(true),
|
||||
@ -94,7 +97,7 @@ public class Mesh implements Savable, Cloneable {
|
||||
/**
|
||||
* A primitive is a line segment. The first two vertices specify
|
||||
* a single line, while subsequent vertices are combined with the
|
||||
* previous vertex to make a line. {@link Mesh#setLineWidth(float) } can
|
||||
* previous vertex to make a line. {@link Material#getAdditionalRenderState()} and {@link RenderState#setLineWidth(float)} can
|
||||
* be used to set the width of the lines.
|
||||
*/
|
||||
LineStrip(false),
|
||||
@ -102,7 +105,7 @@ public class Mesh implements Savable, Cloneable {
|
||||
/**
|
||||
* Identical to {@link #LineStrip} except that at the end
|
||||
* the last vertex is connected with the first to form a line.
|
||||
* {@link Mesh#setLineWidth(float) } can be used
|
||||
* {@link Material#getAdditionalRenderState()} and {@link RenderState#setLineWidth(float)} can be used
|
||||
* to set the width of the lines.
|
||||
*/
|
||||
LineLoop(false),
|
||||
@ -298,6 +301,37 @@ public class Mesh implements Savable, Cloneable {
|
||||
return clone;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called internally by com.jme3.util.clone.Cloner. Do not call directly.
|
||||
*/
|
||||
@Override
|
||||
public Mesh jmeClone() {
|
||||
try {
|
||||
Mesh clone = (Mesh)super.clone();
|
||||
clone.vertexArrayID = -1;
|
||||
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);
|
||||
this.elementLengths = cloner.clone(elementLengths);
|
||||
this.modeStart = cloner.clone(modeStart);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the {@link Type#BindPosePosition}, {@link Type#BindPoseNormal},
|
||||
* and {@link Type#BindPoseTangent}
|
||||
@ -583,7 +617,9 @@ public class Mesh implements Savable, Cloneable {
|
||||
* Returns the line width for line meshes.
|
||||
*
|
||||
* @return the line width
|
||||
* @deprecated use {@link Material#getAdditionalRenderState()} and {@link RenderState#getLineWidth()}
|
||||
*/
|
||||
@Deprecated
|
||||
public float getLineWidth() {
|
||||
return lineWidth;
|
||||
}
|
||||
@ -594,8 +630,13 @@ public class Mesh implements Savable, Cloneable {
|
||||
* the default value is 1.0.
|
||||
*
|
||||
* @param lineWidth The line width
|
||||
* @deprecated use {@link Material#getAdditionalRenderState()} and {@link RenderState#setLineWidth(float)}
|
||||
*/
|
||||
@Deprecated
|
||||
public void setLineWidth(float lineWidth) {
|
||||
if (lineWidth < 1f) {
|
||||
throw new IllegalArgumentException("lineWidth must be greater than or equal to 1.0");
|
||||
}
|
||||
this.lineWidth = lineWidth;
|
||||
}
|
||||
|
||||
|
@ -40,6 +40,7 @@ import com.jme3.export.Savable;
|
||||
import com.jme3.material.Material;
|
||||
import com.jme3.util.SafeArrayList;
|
||||
import com.jme3.util.TempVars;
|
||||
import com.jme3.util.clone.Cloner;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@ -74,7 +75,6 @@ public class Node extends Spatial {
|
||||
* requiresUpdate() method.
|
||||
*/
|
||||
private SafeArrayList<Spatial> updateList = null;
|
||||
|
||||
/**
|
||||
* False if the update list requires rebuilding. This is Node.class
|
||||
* specific and therefore not included as part of the Spatial update flags.
|
||||
@ -99,7 +99,6 @@ public class Node extends Spatial {
|
||||
*/
|
||||
public Node(String name) {
|
||||
super(name);
|
||||
|
||||
// For backwards compatibility, only clear the "requires
|
||||
// update" flag if we are not a subclass of Node.
|
||||
// This prevents subclass from silently failing to receive
|
||||
@ -140,10 +139,21 @@ public class Node extends Spatial {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setMatParamOverrideRefresh() {
|
||||
super.setMatParamOverrideRefresh();
|
||||
for (Spatial child : children.getArray()) {
|
||||
if ((child.refreshFlags & RF_MATPARAM_OVERRIDE) != 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
child.setMatParamOverrideRefresh();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void updateWorldBound(){
|
||||
super.updateWorldBound();
|
||||
|
||||
// for a node, the world bound is a combination of all it's children
|
||||
// bounds
|
||||
BoundingVolume resultBound = null;
|
||||
@ -238,19 +248,19 @@ public class Node extends Spatial {
|
||||
// This branch has no geometric state that requires updates.
|
||||
return;
|
||||
}
|
||||
|
||||
if ((refreshFlags & RF_LIGHTLIST) != 0){
|
||||
updateWorldLightList();
|
||||
}
|
||||
|
||||
if ((refreshFlags & RF_TRANSFORM) != 0){
|
||||
// combine with parent transforms- same for all spatial
|
||||
// subclasses.
|
||||
updateWorldTransforms();
|
||||
}
|
||||
if ((refreshFlags & RF_MATPARAM_OVERRIDE) != 0) {
|
||||
updateMatParamOverrides();
|
||||
}
|
||||
|
||||
refreshFlags &= ~RF_CHILD_LIGHTLIST;
|
||||
|
||||
if (!children.isEmpty()) {
|
||||
// the important part- make sure child geometric state is refreshed
|
||||
// first before updating own world bound. This saves
|
||||
@ -286,7 +296,6 @@ public class Node extends Spatial {
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>getVertexCount</code> returns the number of vertices contained
|
||||
* in all sub-branches of this node that contain geometry.
|
||||
@ -320,7 +329,6 @@ public class Node extends Spatial {
|
||||
public int attachChild(Spatial child) {
|
||||
return attachChildAt(child, children.size());
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* <code>attachChildAt</code> attaches a child to this node at an index. This node
|
||||
@ -344,20 +352,18 @@ public class Node extends Spatial {
|
||||
}
|
||||
child.setParent(this);
|
||||
children.add(index, child);
|
||||
|
||||
// XXX: Not entirely correct? Forces bound update up the
|
||||
// tree stemming from the attached child. Also forces
|
||||
// transform update down the tree-
|
||||
child.setTransformRefresh();
|
||||
child.setLightListRefresh();
|
||||
child.setMatParamOverrideRefresh();
|
||||
if (logger.isLoggable(Level.FINE)) {
|
||||
logger.log(Level.FINE,"Child ({0}) attached to this node ({1})",
|
||||
new Object[]{child.getName(), getName()});
|
||||
}
|
||||
|
||||
invalidateUpdateList();
|
||||
}
|
||||
|
||||
return children.size();
|
||||
}
|
||||
|
||||
@ -432,6 +438,7 @@ public class Node extends Spatial {
|
||||
child.setTransformRefresh();
|
||||
// lights are also inherited from parent
|
||||
child.setLightListRefresh();
|
||||
child.setMatParamOverrideRefresh();
|
||||
|
||||
invalidateUpdateList();
|
||||
}
|
||||
@ -518,7 +525,6 @@ public class Node extends Spatial {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* determines if the provided Spatial is contained in the children list of
|
||||
* this node.
|
||||
@ -566,39 +572,32 @@ public class Node extends Spatial {
|
||||
|
||||
public int collideWith(Collidable other, CollisionResults results){
|
||||
int total = 0;
|
||||
|
||||
// optimization: try collideWith BoundingVolume to avoid possibly redundant tests on children
|
||||
// number 4 in condition is somewhat arbitrary. When there is only one child, the boundingVolume test is redundant at all.
|
||||
// The idea is when there are few children, it can be too expensive to test boundingVolume first.
|
||||
/*
|
||||
I'm removing this change until some issues can be addressed and I really
|
||||
think it needs to be implemented a better way anyway.
|
||||
|
||||
First, it causes issues for anyone doing collideWith() with BoundingVolumes
|
||||
and expecting it to trickle down to the children. For example, children
|
||||
with BoundingSphere bounding volumes and collideWith(BoundingSphere). Doing
|
||||
a collision check at the parent level then has to do a BoundingSphere to BoundingBox
|
||||
collision which isn't resolved. (Having to come up with a collision point in that
|
||||
case is tricky and the first sign that this is the wrong approach.)
|
||||
|
||||
Second, the rippling changes this caused to 'optimize' collideWith() for this
|
||||
special use-case are another sign that this approach was a bit dodgy. The whole
|
||||
idea of calculating a full collision just to see if the two shapes collide at all
|
||||
is very wasteful.
|
||||
|
||||
A proper implementation should support a simpler boolean check that doesn't do
|
||||
all of that calculation. For example, if 'other' is also a BoundingVolume (ie: 99.9%
|
||||
of all non-Ray cases) then a direct BV to BV intersects() test can be done. So much
|
||||
faster. And if 'other' _is_ a Ray then the BV.intersects(Ray) call can be done.
|
||||
|
||||
I don't have time to do it right now but I'll at least un-break a bunch of peoples'
|
||||
code until it can be 'optimized' properly. Hopefully it's not too late to back out
|
||||
the other dodgy ripples this caused. -pspeed (hindsight-expert ;))
|
||||
|
||||
Note: the code itself is relatively simple to implement but I don't have time to
|
||||
a) test it, and b) see if '> 4' is still a decent check for it. Could be it's fast
|
||||
enough to do all the time for > 1.
|
||||
|
||||
if (children.size() > 4)
|
||||
{
|
||||
BoundingVolume bv = this.getWorldBound();
|
||||
@ -691,12 +690,21 @@ public class Node extends Spatial {
|
||||
// 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;
|
||||
}
|
||||
|
||||
@Override
|
||||
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();
|
||||
nodeClone.children = new SafeArrayList<Spatial>(Spatial.class);
|
||||
for (Spatial child : children){
|
||||
@ -707,6 +715,20 @@ public class Node extends Spatial {
|
||||
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
|
||||
public void write(JmeExporter e) throws IOException {
|
||||
super.write(e);
|
||||
@ -718,7 +740,6 @@ public class Node extends Spatial {
|
||||
// XXX: Load children before loading itself!!
|
||||
// This prevents empty children list if controls query
|
||||
// it in Control.setSpatial().
|
||||
|
||||
children = new SafeArrayList( Spatial.class,
|
||||
e.getCapsule(this).readSavableArrayList("children", null) );
|
||||
|
||||
@ -728,7 +749,6 @@ public class Node extends Spatial {
|
||||
child.parent = this;
|
||||
}
|
||||
}
|
||||
|
||||
super.read(e);
|
||||
}
|
||||
|
||||
@ -749,7 +769,6 @@ public class Node extends Spatial {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void depthFirstTraversal(SceneGraphVisitor visitor) {
|
||||
for (Spatial child : children.getArray()) {
|
||||
@ -757,7 +776,6 @@ public class Node extends Spatial {
|
||||
}
|
||||
visitor.visit(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void breadthFirstTraversal(SceneGraphVisitor visitor, Queue<Spatial> queue) {
|
||||
queue.addAll(children);
|
||||
|
@ -38,6 +38,7 @@ import com.jme3.collision.Collidable;
|
||||
import com.jme3.export.*;
|
||||
import com.jme3.light.Light;
|
||||
import com.jme3.light.LightList;
|
||||
import com.jme3.material.MatParamOverride;
|
||||
import com.jme3.material.Material;
|
||||
import com.jme3.math.*;
|
||||
import com.jme3.renderer.Camera;
|
||||
@ -47,6 +48,9 @@ import com.jme3.renderer.queue.RenderQueue;
|
||||
import com.jme3.renderer.queue.RenderQueue.Bucket;
|
||||
import com.jme3.renderer.queue.RenderQueue.ShadowMode;
|
||||
import com.jme3.scene.control.Control;
|
||||
import com.jme3.util.clone.Cloner;
|
||||
import com.jme3.util.clone.IdentityCloneFunction;
|
||||
import com.jme3.util.clone.JmeCloneable;
|
||||
import com.jme3.util.SafeArrayList;
|
||||
import com.jme3.util.TempVars;
|
||||
import java.io.IOException;
|
||||
@ -63,7 +67,7 @@ import java.util.logging.Logger;
|
||||
* @author Joshua Slack
|
||||
* @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());
|
||||
|
||||
@ -119,7 +123,8 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
|
||||
protected static final int RF_TRANSFORM = 0x01, // need light resort + combine transforms
|
||||
RF_BOUND = 0x02,
|
||||
RF_LIGHTLIST = 0x04, // changes in light lists
|
||||
RF_CHILD_LIGHTLIST = 0x08; // some child need geometry update
|
||||
RF_CHILD_LIGHTLIST = 0x08, // some child need geometry update
|
||||
RF_MATPARAM_OVERRIDE = 0x10;
|
||||
|
||||
protected CullHint cullHint = CullHint.Inherit;
|
||||
protected BatchHint batchHint = BatchHint.Inherit;
|
||||
@ -132,6 +137,10 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
|
||||
*/
|
||||
protected LightList localLights;
|
||||
protected transient LightList worldLights;
|
||||
|
||||
protected ArrayList<MatParamOverride> localOverrides;
|
||||
protected ArrayList<MatParamOverride> worldOverrides;
|
||||
|
||||
/**
|
||||
* This spatial's name.
|
||||
*/
|
||||
@ -192,13 +201,14 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
|
||||
*/
|
||||
protected Spatial(String name) {
|
||||
this.name = name;
|
||||
|
||||
localTransform = new Transform();
|
||||
worldTransform = new Transform();
|
||||
|
||||
localLights = new LightList(this);
|
||||
worldLights = new LightList(this);
|
||||
|
||||
localOverrides = new ArrayList<>();
|
||||
worldOverrides = new ArrayList<>();
|
||||
refreshFlags |= RF_BOUND;
|
||||
}
|
||||
|
||||
@ -219,7 +229,6 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
|
||||
boolean requiresUpdates() {
|
||||
return requiresUpdates | !controls.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclasses can call this with true to denote that they require
|
||||
* updateLogicalState() to be called even if they contain no controls.
|
||||
@ -269,35 +278,33 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
|
||||
|
||||
protected void setLightListRefresh() {
|
||||
refreshFlags |= RF_LIGHTLIST;
|
||||
|
||||
// Make sure next updateGeometricState() visits this branch
|
||||
// to update lights.
|
||||
Spatial p = parent;
|
||||
while (p != null) {
|
||||
//if (p.refreshFlags != 0) {
|
||||
// any refresh flag is sufficient,
|
||||
// as each propagates to the root Node
|
||||
|
||||
// 2015/2/8:
|
||||
// This is not true, because using e.g. getWorldBound()
|
||||
// or getWorldTransform() activates a "partial refresh"
|
||||
// which does not update the lights but does clear
|
||||
// the refresh flags on the ancestors!
|
||||
|
||||
// return;
|
||||
//}
|
||||
|
||||
if ((p.refreshFlags & RF_CHILD_LIGHTLIST) != 0) {
|
||||
// The parent already has this flag,
|
||||
// so must all ancestors.
|
||||
return;
|
||||
}
|
||||
|
||||
p.refreshFlags |= RF_CHILD_LIGHTLIST;
|
||||
p = p.parent;
|
||||
}
|
||||
}
|
||||
|
||||
protected void setMatParamOverrideRefresh() {
|
||||
refreshFlags |= RF_MATPARAM_OVERRIDE;
|
||||
Spatial p = parent;
|
||||
while (p != null) {
|
||||
if ((p.refreshFlags & RF_MATPARAM_OVERRIDE) != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
p.refreshFlags |= RF_MATPARAM_OVERRIDE;
|
||||
p = p.parent;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate that the bounding of this spatial has changed and that
|
||||
* a refresh is required.
|
||||
@ -315,7 +322,6 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
|
||||
p = p.parent;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* (Internal use only) Forces a refresh of the given types of data.
|
||||
*
|
||||
@ -421,6 +427,29 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
|
||||
return worldLights;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the local material parameter overrides.
|
||||
*
|
||||
* @return The list of local material parameter overrides.
|
||||
*/
|
||||
public List<MatParamOverride> getLocalMatParamOverrides() {
|
||||
return localOverrides;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the world material parameter overrides.
|
||||
*
|
||||
* Note that this list is only updated on a call to
|
||||
* {@link #updateGeometricState()}. After update, the world overrides list
|
||||
* will contain the {@link #getParent() parent's} world overrides combined
|
||||
* with this spatial's {@link #getLocalMatParamOverrides() local overrides}.
|
||||
*
|
||||
* @return The list of world material parameter overrides.
|
||||
*/
|
||||
public List<MatParamOverride> getWorldMatParamOverrides() {
|
||||
return worldOverrides;
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>getWorldRotation</code> retrieves the absolute rotation of the
|
||||
* Spatial.
|
||||
@ -522,10 +551,8 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
|
||||
TempVars vars = TempVars.get();
|
||||
|
||||
Vector3f compVecA = vars.vect4;
|
||||
|
||||
compVecA.set(position).subtractLocal(worldTranslation);
|
||||
getLocalRotation().lookAt(compVecA, upVector);
|
||||
|
||||
if ( getParent() != null ) {
|
||||
Quaternion rot=vars.quat1;
|
||||
rot = rot.set(parent.getWorldRotation()).inverseLocal().multLocal(getLocalRotation());
|
||||
@ -552,13 +579,61 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
|
||||
worldLights.update(localLights, null);
|
||||
refreshFlags &= ~RF_LIGHTLIST;
|
||||
} else {
|
||||
if ((parent.refreshFlags & RF_LIGHTLIST) == 0) {
|
||||
assert (parent.refreshFlags & RF_LIGHTLIST) == 0;
|
||||
worldLights.update(localLights, parent.worldLights);
|
||||
refreshFlags &= ~RF_LIGHTLIST;
|
||||
}
|
||||
}
|
||||
|
||||
protected void updateMatParamOverrides() {
|
||||
refreshFlags &= ~RF_MATPARAM_OVERRIDE;
|
||||
|
||||
worldOverrides.clear();
|
||||
if (parent == null) {
|
||||
worldOverrides.addAll(localOverrides);
|
||||
} else {
|
||||
assert false;
|
||||
assert (parent.refreshFlags & RF_MATPARAM_OVERRIDE) == 0;
|
||||
worldOverrides.addAll(localOverrides);
|
||||
worldOverrides.addAll(parent.worldOverrides);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a local material parameter override.
|
||||
*
|
||||
* @param override The override to add.
|
||||
* @see MatParamOverride
|
||||
*/
|
||||
public void addMatParamOverride(MatParamOverride override) {
|
||||
if (override == null) {
|
||||
throw new IllegalArgumentException("override cannot be null");
|
||||
}
|
||||
localOverrides.add(override);
|
||||
setMatParamOverrideRefresh();
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a local material parameter override if it exists.
|
||||
*
|
||||
* @param override The override to remove.
|
||||
* @see MatParamOverride
|
||||
*/
|
||||
public void removeMatParamOverride(MatParamOverride override) {
|
||||
if (localOverrides.remove(override)) {
|
||||
setMatParamOverrideRefresh();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all local material parameter overrides.
|
||||
*
|
||||
* @see #addMatParamOverride(com.jme3.material.MatParamOverride)
|
||||
*/
|
||||
public void clearMatParamOverrides() {
|
||||
if (!localOverrides.isEmpty()) {
|
||||
setMatParamOverrideRefresh();
|
||||
}
|
||||
localOverrides.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -693,7 +768,6 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
|
||||
controls.add(control);
|
||||
control.setSpatial(this);
|
||||
boolean after = requiresUpdates();
|
||||
|
||||
// If the requirement to be updated has changed
|
||||
// then we need to let the parent node know so it
|
||||
// can rebuild its update list.
|
||||
@ -717,7 +791,6 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
|
||||
}
|
||||
}
|
||||
boolean after = requiresUpdates();
|
||||
|
||||
// If the requirement to be updated has changed
|
||||
// then we need to let the parent node know so it
|
||||
// can rebuild its update list.
|
||||
@ -743,14 +816,12 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
|
||||
}
|
||||
|
||||
boolean after = requiresUpdates();
|
||||
|
||||
// If the requirement to be updated has changed
|
||||
// then we need to let the parent node know so it
|
||||
// can rebuild its update list.
|
||||
if( parent != null && before != after ) {
|
||||
parent.invalidateUpdateList();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -835,6 +906,9 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
|
||||
if ((refreshFlags & RF_BOUND) != 0) {
|
||||
updateWorldBound();
|
||||
}
|
||||
if ((refreshFlags & RF_MATPARAM_OVERRIDE) != 0) {
|
||||
updateMatParamOverrides();
|
||||
}
|
||||
|
||||
assert refreshFlags == 0;
|
||||
}
|
||||
@ -1261,12 +1335,43 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
|
||||
* Note that meshes of geometries are not cloned explicitly, they
|
||||
* are shared if static, or specially cloned if animated.
|
||||
*
|
||||
* All controls will be cloned using the Control.cloneForSpatial method
|
||||
* on the clone.
|
||||
*
|
||||
* @see Mesh#cloneForAnim()
|
||||
*/
|
||||
public Spatial clone( boolean cloneMaterial ) {
|
||||
|
||||
// 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 {
|
||||
Spatial clone = (Spatial) super.clone();
|
||||
if (worldBound != null) {
|
||||
@ -1279,6 +1384,13 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
|
||||
clone.localLights.setOwner(clone);
|
||||
clone.worldLights.setOwner(clone);
|
||||
|
||||
clone.worldOverrides = new ArrayList<MatParamOverride>();
|
||||
clone.localOverrides = new ArrayList<MatParamOverride>();
|
||||
|
||||
for (MatParamOverride override : localOverrides) {
|
||||
clone.localOverrides.add((MatParamOverride) override.clone());
|
||||
}
|
||||
|
||||
// No need to force cloned to update.
|
||||
// This node already has the refresh flags
|
||||
// set below so it will have to update anyway.
|
||||
@ -1300,6 +1412,7 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
|
||||
clone.setBoundRefresh();
|
||||
clone.setTransformRefresh();
|
||||
clone.setLightListRefresh();
|
||||
clone.setMatParamOverrideRefresh();
|
||||
|
||||
clone.controls = new SafeArrayList<Control>(Control.class);
|
||||
for (int i = 0; i < controls.size(); i++) {
|
||||
@ -1342,7 +1455,73 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
|
||||
*
|
||||
* @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);
|
||||
this.worldOverrides = cloner.clone(worldOverrides);
|
||||
this.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) {
|
||||
if (userData == null) {
|
||||
@ -1419,6 +1598,7 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
|
||||
capsule.write(shadowMode, "shadow_mode", ShadowMode.Inherit);
|
||||
capsule.write(localTransform, "transform", Transform.IDENTITY);
|
||||
capsule.write(localLights, "lights", null);
|
||||
capsule.writeSavableArrayList(localOverrides, "overrides", null);
|
||||
|
||||
// Shallow clone the controls array to convert its type.
|
||||
capsule.writeSavableArrayList(new ArrayList(controls), "controlsList", null);
|
||||
@ -1442,6 +1622,12 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
|
||||
localLights = (LightList) ic.readSavable("lights", null);
|
||||
localLights.setOwner(this);
|
||||
|
||||
localOverrides = ic.readSavableArrayList("overrides", null);
|
||||
if (localOverrides == null) {
|
||||
localOverrides = new ArrayList<>();
|
||||
}
|
||||
worldOverrides = new ArrayList<>();
|
||||
|
||||
//changed for backward compatibility with j3o files generated before the AnimControl/SkeletonControl split
|
||||
//the AnimControl creates the SkeletonControl for old files and add it to the spatial.
|
||||
//The SkeletonControl must be the last in the stack so we add the list of all other control before it.
|
||||
|
@ -522,6 +522,7 @@ public class VertexBuffer extends NativeObject implements Savable, Cloneable {
|
||||
// throw new UnsupportedOperationException("Data has already been sent. Cannot set usage.");
|
||||
|
||||
this.usage = usage;
|
||||
this.setUpdateNeeded();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -47,6 +47,7 @@ import com.jme3.scene.VertexBuffer.Type;
|
||||
import com.jme3.scene.VertexBuffer.Usage;
|
||||
import com.jme3.util.BufferUtils;
|
||||
import com.jme3.util.TempVars;
|
||||
import com.jme3.util.clone.Cloner;
|
||||
import java.io.IOException;
|
||||
import java.nio.FloatBuffer;
|
||||
import java.util.ArrayList;
|
||||
@ -343,6 +344,18 @@ public class InstancedGeometry extends Geometry {
|
||||
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
|
||||
public void write(JmeExporter exporter) throws IOException {
|
||||
super.write(exporter);
|
||||
|
@ -48,6 +48,7 @@ import com.jme3.util.clone.Cloner;
|
||||
import com.jme3.util.clone.JmeCloneable;
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class InstancedNode extends GeometryGroupNode {
|
||||
|
||||
@ -59,7 +60,7 @@ public class InstancedNode extends GeometryGroupNode {
|
||||
setGeometryStartIndex(geom, startIndex);
|
||||
}
|
||||
|
||||
private static final class InstanceTypeKey implements Cloneable {
|
||||
private static final class InstanceTypeKey implements Cloneable, JmeCloneable {
|
||||
|
||||
Mesh mesh;
|
||||
Material material;
|
||||
@ -106,6 +107,21 @@ public class InstancedNode extends GeometryGroupNode {
|
||||
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, JmeCloneable {
|
||||
@ -329,6 +345,29 @@ public class InstancedNode extends GeometryGroupNode {
|
||||
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
|
||||
public void onTransformChange(Geometry geom) {
|
||||
// Handled automatically
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2012 jMonkeyEngine
|
||||
* Copyright (c) 2009-2015 jMonkeyEngine
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@ -31,256 +31,149 @@
|
||||
*/
|
||||
package com.jme3.shader;
|
||||
|
||||
import com.jme3.export.*;
|
||||
import com.jme3.material.MatParam;
|
||||
import com.jme3.material.TechniqueDef;
|
||||
import com.jme3.util.ListMap;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
/**
|
||||
* The new define list.
|
||||
*
|
||||
* @author Kirill Vainer
|
||||
*/
|
||||
public final class DefineList {
|
||||
|
||||
public final class DefineList implements Savable, Cloneable {
|
||||
public static final int MAX_DEFINES = 64;
|
||||
|
||||
private static final String ONE = "1";
|
||||
private long hash;
|
||||
private final int[] vals;
|
||||
|
||||
private TreeMap<String, String> defines = new TreeMap<String, String>();
|
||||
private String compiled = null;
|
||||
private int cachedHashCode = 0;
|
||||
|
||||
public void write(JmeExporter ex) throws IOException{
|
||||
OutputCapsule oc = ex.getCapsule(this);
|
||||
|
||||
String[] keys = new String[defines.size()];
|
||||
String[] vals = new String[defines.size()];
|
||||
|
||||
int i = 0;
|
||||
for (Map.Entry<String, String> define : defines.entrySet()){
|
||||
keys[i] = define.getKey();
|
||||
vals[i] = define.getValue();
|
||||
i++;
|
||||
public DefineList(int numValues) {
|
||||
if (numValues < 0 || numValues > MAX_DEFINES) {
|
||||
throw new IllegalArgumentException("numValues must be between 0 and 64");
|
||||
}
|
||||
vals = new int[numValues];
|
||||
}
|
||||
|
||||
oc.write(keys, "keys", null);
|
||||
oc.write(vals, "vals", null);
|
||||
private DefineList(DefineList original) {
|
||||
this.hash = original.hash;
|
||||
this.vals = new int[original.vals.length];
|
||||
System.arraycopy(original.vals, 0, vals, 0, vals.length);
|
||||
}
|
||||
|
||||
public void read(JmeImporter im) throws IOException{
|
||||
InputCapsule ic = im.getCapsule(this);
|
||||
public void set(int id, int val) {
|
||||
assert 0 <= id && id < 64;
|
||||
if (val != 0) {
|
||||
hash |= (1L << id);
|
||||
} else {
|
||||
hash &= ~(1L << id);
|
||||
}
|
||||
vals[id] = val;
|
||||
}
|
||||
|
||||
String[] keys = ic.readStringArray("keys", null);
|
||||
String[] vals = ic.readStringArray("vals", null);
|
||||
for (int i = 0; i < keys.length; i++){
|
||||
defines.put(keys[i], vals[i]);
|
||||
public void set(int id, float val) {
|
||||
set(id, Float.floatToIntBits(val));
|
||||
}
|
||||
|
||||
public void set(int id, boolean val) {
|
||||
set(id, val ? 1 : 0);
|
||||
}
|
||||
|
||||
public void set(int id, VarType type, Object value) {
|
||||
if (value == null) {
|
||||
set(id, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case Int:
|
||||
set(id, (Integer) value);
|
||||
break;
|
||||
case Float:
|
||||
set(id, (Float) value);
|
||||
break;
|
||||
case Boolean:
|
||||
set(id, ((Boolean) value));
|
||||
break;
|
||||
default:
|
||||
set(id, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public void setAll(DefineList other) {
|
||||
for (int i = 0; i < other.vals.length; i++) {
|
||||
if (other.vals[i] != 0) {
|
||||
vals[i] = other.vals[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
defines.clear();
|
||||
compiled = "";
|
||||
cachedHashCode = 0;
|
||||
hash = 0;
|
||||
Arrays.fill(vals, 0);
|
||||
}
|
||||
|
||||
public String get(String key){
|
||||
return defines.get(key);
|
||||
public boolean getBoolean(int id) {
|
||||
return vals[id] != 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DefineList clone() {
|
||||
try {
|
||||
DefineList clone = (DefineList) super.clone();
|
||||
clone.cachedHashCode = 0;
|
||||
clone.compiled = null;
|
||||
clone.defines = (TreeMap<String, String>) defines.clone();
|
||||
return clone;
|
||||
} catch (CloneNotSupportedException ex) {
|
||||
throw new AssertionError();
|
||||
}
|
||||
public float getFloat(int id) {
|
||||
return Float.intBitsToFloat(vals[id]);
|
||||
}
|
||||
|
||||
public boolean set(String key, VarType type, Object val){
|
||||
if (val == null){
|
||||
defines.remove(key);
|
||||
compiled = null;
|
||||
cachedHashCode = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
switch (type){
|
||||
case Boolean:
|
||||
if (((Boolean) val).booleanValue()) {
|
||||
// same literal, != will work
|
||||
if (defines.put(key, ONE) != ONE) {
|
||||
compiled = null;
|
||||
cachedHashCode = 0;
|
||||
return true;
|
||||
}
|
||||
} else if (defines.containsKey(key)) {
|
||||
defines.remove(key);
|
||||
compiled = null;
|
||||
cachedHashCode = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
break;
|
||||
case Float:
|
||||
case Int:
|
||||
String newValue = val.toString();
|
||||
String original = defines.put(key, newValue);
|
||||
if (!val.equals(original)) {
|
||||
compiled = null;
|
||||
cachedHashCode = 0;
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// same literal, != will work
|
||||
if (defines.put(key, ONE) != ONE) {
|
||||
compiled = null;
|
||||
cachedHashCode = 0;
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean remove(String key){
|
||||
if (defines.remove(key) != null) {
|
||||
compiled = null;
|
||||
cachedHashCode = 0;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void addFrom(DefineList other){
|
||||
if (other == null) {
|
||||
return;
|
||||
}
|
||||
compiled = null;
|
||||
cachedHashCode = 0;
|
||||
defines.putAll(other.defines);
|
||||
}
|
||||
|
||||
public String getCompiled(){
|
||||
if (compiled == null){
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (Map.Entry<String, String> entry : defines.entrySet()){
|
||||
sb.append("#define ").append(entry.getKey()).append(" ");
|
||||
sb.append(entry.getValue()).append('\n');
|
||||
}
|
||||
compiled = sb.toString();
|
||||
}
|
||||
return compiled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
final DefineList other = (DefineList) obj;
|
||||
return defines.equals(other.defines);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update defines if the define list changed based on material parameters.
|
||||
* @param params
|
||||
* @param def
|
||||
* @return true if defines was updated
|
||||
*/
|
||||
public boolean update(ListMap params, TechniqueDef def){
|
||||
if(equalsParams(params, def)){
|
||||
return false;
|
||||
}
|
||||
// Defines were changed, update define list
|
||||
clear();
|
||||
for(int i=0;i<params.size();i++) {
|
||||
MatParam param = (MatParam)params.getValue(i);
|
||||
String defineName = def.getShaderParamDefine(param.getName());
|
||||
if (defineName != null) {
|
||||
set(defineName, param.getVarType(), param.getValue());
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean equalsParams(ListMap params, TechniqueDef def) {
|
||||
|
||||
int size = 0;
|
||||
|
||||
for(int i = 0; i < params.size() ; i++ ) {
|
||||
MatParam param = (MatParam)params.getValue(i);
|
||||
String key = def.getShaderParamDefine(param.getName());
|
||||
if (key != null) {
|
||||
Object val = param.getValue();
|
||||
if (val != null) {
|
||||
|
||||
switch (param.getVarType()) {
|
||||
case Boolean: {
|
||||
String current = defines.get(key);
|
||||
if (((Boolean) val).booleanValue()) {
|
||||
if (current == null || current != ONE) {
|
||||
return false;
|
||||
}
|
||||
size++;
|
||||
} else {
|
||||
if (current != null) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case Float:
|
||||
case Int: {
|
||||
String newValue = val.toString();
|
||||
String current = defines.get(key);
|
||||
if (!newValue.equals(current)) {
|
||||
return false;
|
||||
}
|
||||
size++;
|
||||
}
|
||||
break;
|
||||
default: {
|
||||
if (!defines.containsKey(key)) {
|
||||
return false;
|
||||
}
|
||||
size++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if (size != defines.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
public int getInt(int id) {
|
||||
return vals[id];
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
if (cachedHashCode == 0) {
|
||||
cachedHashCode = defines.hashCode();
|
||||
}
|
||||
return cachedHashCode;
|
||||
return (int)((hash >> 32) ^ hash);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString(){
|
||||
StringBuilder sb = new StringBuilder();
|
||||
int i = 0;
|
||||
for (Map.Entry<String, String> entry : defines.entrySet()) {
|
||||
sb.append(entry.getKey()).append("=").append(entry.getValue());
|
||||
if (i != defines.size() - 1) {
|
||||
sb.append(", ");
|
||||
public boolean equals(Object other) {
|
||||
DefineList o = (DefineList) other;
|
||||
if (hash == o.hash) {
|
||||
for (int i = 0; i < vals.length; i++) {
|
||||
if (vals[i] != o.vals[i]) return false;
|
||||
}
|
||||
i++;
|
||||
return true;
|
||||
}
|
||||
return sb.toString();
|
||||
return false;
|
||||
}
|
||||
|
||||
public DefineList deepClone() {
|
||||
return new DefineList(this);
|
||||
}
|
||||
|
||||
public void generateSource(StringBuilder sb, List<String> defineNames, List<VarType> defineTypes) {
|
||||
for (int i = 0; i < vals.length; i++) {
|
||||
if (vals[i] != 0) {
|
||||
String defineName = defineNames.get(i);
|
||||
|
||||
sb.append("#define ");
|
||||
sb.append(defineName);
|
||||
sb.append(" ");
|
||||
|
||||
if (defineTypes != null && defineTypes.get(i) == VarType.Float) {
|
||||
float val = Float.intBitsToFloat(vals[i]);
|
||||
if (Float.isInfinite(val) || Float.isNaN(val)) {
|
||||
throw new IllegalArgumentException(
|
||||
"GLSL does not support NaN "
|
||||
+ "or Infinite float literals");
|
||||
}
|
||||
sb.append(val);
|
||||
} else {
|
||||
sb.append(vals[i]);
|
||||
}
|
||||
|
||||
sb.append("\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public String generateSource(List<String> defineNames, List<VarType> defineTypes) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
generateSource(sb, defineNames, defineTypes);
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
@ -258,7 +258,7 @@ public class Glsl100ShaderGenerator extends ShaderGenerator {
|
||||
}
|
||||
|
||||
for (ShaderNodeVariable var : shaderNode.getDefinition().getOutputs()) {
|
||||
ShaderNodeVariable v = new ShaderNodeVariable(var.getType(), shaderNode.getName(), var.getName());
|
||||
ShaderNodeVariable v = new ShaderNodeVariable(var.getType(), shaderNode.getName(), var.getName(), var.getMultiplicity());
|
||||
if (!declaredInputs.contains(shaderNode.getName() + "_" + var.getName())) {
|
||||
if (!isVarying(info, v)) {
|
||||
declareVariable(source, v);
|
||||
@ -397,6 +397,11 @@ public class Glsl100ShaderGenerator extends ShaderGenerator {
|
||||
source.append(mapping.getLeftVariable().getNameSpace());
|
||||
source.append("_");
|
||||
source.append(mapping.getLeftVariable().getName());
|
||||
if (mapping.getLeftVariable().getMultiplicity() != null){
|
||||
source.append("[");
|
||||
source.append(mapping.getLeftVariable().getMultiplicity());
|
||||
source.append("]");
|
||||
}
|
||||
|
||||
//left swizzle, the variable can't be declared and assigned on the same line.
|
||||
if (mapping.getLeftSwizzling().length() > 0) {
|
||||
|
@ -45,44 +45,63 @@ public final class Shader extends NativeObject {
|
||||
/**
|
||||
* A list of all shader sources currently attached.
|
||||
*/
|
||||
private ArrayList<ShaderSource> shaderSourceList;
|
||||
private final ArrayList<ShaderSource> shaderSourceList;
|
||||
|
||||
/**
|
||||
* Maps uniform name to the uniform variable.
|
||||
*/
|
||||
private ListMap<String, Uniform> uniforms;
|
||||
private final ListMap<String, Uniform> uniforms;
|
||||
|
||||
/**
|
||||
* Uniforms bound to {@link UniformBinding}s.
|
||||
*
|
||||
* Managed by the {@link UniformBindingManager}.
|
||||
*/
|
||||
private final ArrayList<Uniform> boundUniforms;
|
||||
|
||||
/**
|
||||
* Maps attribute name to the location of the attribute in the shader.
|
||||
*/
|
||||
private IntMap<Attribute> attribs;
|
||||
private final IntMap<Attribute> attribs;
|
||||
|
||||
/**
|
||||
* Type of shader. The shader will control the pipeline of it's type.
|
||||
*/
|
||||
public static enum ShaderType {
|
||||
|
||||
/**
|
||||
* Control fragment rasterization. (e.g color of pixel).
|
||||
*/
|
||||
Fragment,
|
||||
|
||||
Fragment("frag"),
|
||||
/**
|
||||
* Control vertex processing. (e.g transform of model to clip space)
|
||||
*/
|
||||
Vertex,
|
||||
Vertex("vert"),
|
||||
/**
|
||||
* Control geometry assembly. (e.g compile a triangle list from input
|
||||
* data)
|
||||
*/
|
||||
Geometry("geom"),
|
||||
/**
|
||||
* Controls tesselation factor (e.g how often a input patch should be
|
||||
* subdivided)
|
||||
*/
|
||||
TessellationControl("tsctrl"),
|
||||
/**
|
||||
* Controls tesselation transform (e.g similar to the vertex shader, but
|
||||
* required to mix inputs manual)
|
||||
*/
|
||||
TessellationEvaluation("tseval");
|
||||
|
||||
/**
|
||||
* Control geometry assembly. (e.g compile a triangle list from input data)
|
||||
*/
|
||||
Geometry,
|
||||
/**
|
||||
* Controls tesselation factor (e.g how often a input patch should be subdivided)
|
||||
*/
|
||||
TessellationControl,
|
||||
/**
|
||||
* Controls tesselation transform (e.g similar to the vertex shader, but required to mix inputs manual)
|
||||
*/
|
||||
TessellationEvaluation;
|
||||
private String extension;
|
||||
|
||||
public String getExtension() {
|
||||
return extension;
|
||||
}
|
||||
|
||||
private ShaderType(String extension) {
|
||||
this.extension = extension;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -195,22 +214,16 @@ public final class Shader extends NativeObject {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the shader for use, must be called after the
|
||||
* constructor without arguments is used.
|
||||
*/
|
||||
public void initialize() {
|
||||
shaderSourceList = new ArrayList<ShaderSource>();
|
||||
uniforms = new ListMap<String, Uniform>();
|
||||
attribs = new IntMap<Attribute>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new shader, {@link #initialize() } must be called
|
||||
* after this constructor for the shader to be usable.
|
||||
*/
|
||||
public Shader(){
|
||||
super();
|
||||
shaderSourceList = new ArrayList<ShaderSource>();
|
||||
uniforms = new ListMap<String, Uniform>();
|
||||
attribs = new IntMap<Attribute>();
|
||||
boundUniforms = new ArrayList<Uniform>();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -225,6 +238,10 @@ public final class Shader extends NativeObject {
|
||||
for (ShaderSource source : s.shaderSourceList){
|
||||
shaderSourceList.add( (ShaderSource)source.createDestructableClone() );
|
||||
}
|
||||
|
||||
uniforms = null;
|
||||
boundUniforms = null;
|
||||
attribs = null;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -248,6 +265,18 @@ public final class Shader extends NativeObject {
|
||||
setUpdateNeeded();
|
||||
}
|
||||
|
||||
public void addUniformBinding(UniformBinding binding){
|
||||
String uniformName = "g_" + binding.name();
|
||||
Uniform uniform = uniforms.get(uniformName);
|
||||
if (uniform == null) {
|
||||
uniform = new Uniform();
|
||||
uniform.name = uniformName;
|
||||
uniform.binding = binding;
|
||||
uniforms.put(uniformName, uniform);
|
||||
boundUniforms.add(uniform);
|
||||
}
|
||||
}
|
||||
|
||||
public Uniform getUniform(String name){
|
||||
assert name.startsWith("m_") || name.startsWith("g_");
|
||||
Uniform uniform = uniforms.get(name);
|
||||
@ -278,6 +307,10 @@ public final class Shader extends NativeObject {
|
||||
return uniforms;
|
||||
}
|
||||
|
||||
public ArrayList<Uniform> getBoundUniforms() {
|
||||
return boundUniforms;
|
||||
}
|
||||
|
||||
public Collection<ShaderSource> getSources(){
|
||||
return shaderSourceList;
|
||||
}
|
||||
|
@ -57,9 +57,9 @@ public abstract class ShaderGenerator {
|
||||
*/
|
||||
protected int indent;
|
||||
/**
|
||||
* the technique to use for the shader generation
|
||||
* the technique def to use for the shader generation
|
||||
*/
|
||||
protected Technique technique = null;
|
||||
protected TechniqueDef techniqueDef = null;
|
||||
|
||||
/**
|
||||
* Build a shaderGenerator
|
||||
@ -70,8 +70,8 @@ public abstract class ShaderGenerator {
|
||||
this.assetManager = assetManager;
|
||||
}
|
||||
|
||||
public void initialize(Technique technique){
|
||||
this.technique = technique;
|
||||
public void initialize(TechniqueDef techniqueDef){
|
||||
this.techniqueDef = techniqueDef;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -79,24 +79,29 @@ public abstract class ShaderGenerator {
|
||||
*
|
||||
* @return a Shader program
|
||||
*/
|
||||
public Shader generateShader() {
|
||||
if(technique == null){
|
||||
throw new UnsupportedOperationException("The shaderGenerator was not properly initialized, call initialize(Technique) before any generation");
|
||||
public Shader generateShader(String definesSourceCode) {
|
||||
if (techniqueDef == null) {
|
||||
throw new UnsupportedOperationException("The shaderGenerator was not "
|
||||
+ "properly initialized, call "
|
||||
+ "initialize(TechniqueDef) before any generation");
|
||||
}
|
||||
|
||||
DefineList defines = technique.getAllDefines();
|
||||
TechniqueDef def = technique.getDef();
|
||||
ShaderGenerationInfo info = def.getShaderGenerationInfo();
|
||||
|
||||
String vertexSource = buildShader(def.getShaderNodes(), info, ShaderType.Vertex);
|
||||
String fragmentSource = buildShader(def.getShaderNodes(), info, ShaderType.Fragment);
|
||||
String techniqueName = techniqueDef.getName();
|
||||
ShaderGenerationInfo info = techniqueDef.getShaderGenerationInfo();
|
||||
|
||||
Shader shader = new Shader();
|
||||
shader.initialize();
|
||||
shader.addSource(Shader.ShaderType.Vertex, technique.getDef().getName() + ".vert", vertexSource, defines.getCompiled(), getLanguageAndVersion(ShaderType.Vertex));
|
||||
shader.addSource(Shader.ShaderType.Fragment, technique.getDef().getName() + ".frag", fragmentSource, defines.getCompiled(), getLanguageAndVersion(ShaderType.Fragment));
|
||||
for (ShaderType type : ShaderType.values()) {
|
||||
String extension = type.getExtension();
|
||||
String language = getLanguageAndVersion(type);
|
||||
String shaderSourceCode = buildShader(techniqueDef.getShaderNodes(), info, type);
|
||||
|
||||
technique = null;
|
||||
if (shaderSourceCode != null) {
|
||||
String shaderSourceAssetName = techniqueName + "." + extension;
|
||||
shader.addSource(type, shaderSourceAssetName, shaderSourceCode, definesSourceCode, language);
|
||||
}
|
||||
}
|
||||
|
||||
techniqueDef = null;
|
||||
return shader;
|
||||
}
|
||||
|
||||
@ -109,6 +114,14 @@ public abstract class ShaderGenerator {
|
||||
* @return the code of the generated vertex shader
|
||||
*/
|
||||
protected String buildShader(List<ShaderNode> shaderNodes, ShaderGenerationInfo info, ShaderType type) {
|
||||
if (type == ShaderType.TessellationControl ||
|
||||
type == ShaderType.TessellationEvaluation ||
|
||||
type == ShaderType.Geometry) {
|
||||
// TODO: Those are not supported.
|
||||
// Too much code assumes that type is either Vertex or Fragment
|
||||
return null;
|
||||
}
|
||||
|
||||
indent = 0;
|
||||
|
||||
StringBuilder sourceDeclaration = new StringBuilder();
|
||||
|
@ -1,201 +0,0 @@
|
||||
/*
|
||||
* 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.shader;
|
||||
|
||||
import com.jme3.asset.AssetKey;
|
||||
import com.jme3.export.InputCapsule;
|
||||
import com.jme3.export.JmeExporter;
|
||||
import com.jme3.export.JmeImporter;
|
||||
import com.jme3.export.OutputCapsule;
|
||||
import java.io.IOException;
|
||||
import java.util.EnumMap;
|
||||
import java.util.Set;
|
||||
|
||||
public class ShaderKey extends AssetKey<Shader> {
|
||||
|
||||
protected EnumMap<Shader.ShaderType,String> shaderLanguage;
|
||||
protected EnumMap<Shader.ShaderType,String> shaderName;
|
||||
protected DefineList defines;
|
||||
protected int cachedHashedCode = 0;
|
||||
protected boolean usesShaderNodes = false;
|
||||
|
||||
public ShaderKey(){
|
||||
shaderLanguage=new EnumMap<Shader.ShaderType, String>(Shader.ShaderType.class);
|
||||
shaderName=new EnumMap<Shader.ShaderType, String>(Shader.ShaderType.class);
|
||||
}
|
||||
|
||||
public ShaderKey(DefineList defines, EnumMap<Shader.ShaderType,String> shaderLanguage,EnumMap<Shader.ShaderType,String> shaderName){
|
||||
super("");
|
||||
this.name = reducePath(getShaderName(Shader.ShaderType.Vertex));
|
||||
this.shaderLanguage=new EnumMap<Shader.ShaderType, String>(Shader.ShaderType.class);
|
||||
this.shaderName=new EnumMap<Shader.ShaderType, String>(Shader.ShaderType.class);
|
||||
this.defines = defines;
|
||||
for (Shader.ShaderType shaderType : shaderName.keySet()) {
|
||||
this.shaderName.put(shaderType,shaderName.get(shaderType));
|
||||
this.shaderLanguage.put(shaderType,shaderLanguage.get(shaderType));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ShaderKey clone() {
|
||||
ShaderKey clone = (ShaderKey) super.clone();
|
||||
clone.cachedHashedCode = 0;
|
||||
clone.defines = defines.clone();
|
||||
return clone;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString(){
|
||||
//todo:
|
||||
return "V="+name+";";
|
||||
}
|
||||
|
||||
private final String getShaderName(Shader.ShaderType type) {
|
||||
if (shaderName == null) {
|
||||
return "";
|
||||
}
|
||||
String shName = shaderName.get(type);
|
||||
return shName != null ? shName : "";
|
||||
}
|
||||
|
||||
//todo: make equals and hashCode work
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
final ShaderKey other = (ShaderKey) obj;
|
||||
if (name.equals(other.name) && getShaderName(Shader.ShaderType.Fragment).equals(other.getShaderName(Shader.ShaderType.Fragment))){
|
||||
if (defines != null && other.defines != null) {
|
||||
return defines.equals(other.defines);
|
||||
} else if (defines != null || other.defines != null) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
if (cachedHashedCode == 0) {
|
||||
int hash = 7;
|
||||
hash = 41 * hash + name.hashCode();
|
||||
hash = 41 * hash + getShaderName(Shader.ShaderType.Fragment).hashCode();
|
||||
hash = getShaderName(Shader.ShaderType.Geometry) == null ? hash : 41 * hash + getShaderName(Shader.ShaderType.Geometry).hashCode();
|
||||
hash = getShaderName(Shader.ShaderType.TessellationControl) == null ? hash : 41 * hash + getShaderName(Shader.ShaderType.TessellationControl).hashCode();
|
||||
hash = getShaderName(Shader.ShaderType.TessellationEvaluation) == null ? hash : 41 * hash + getShaderName(Shader.ShaderType.TessellationEvaluation).hashCode();
|
||||
hash = 41 * hash + (defines != null ? defines.hashCode() : 0);
|
||||
cachedHashedCode = hash;
|
||||
}
|
||||
return cachedHashedCode;
|
||||
}
|
||||
|
||||
public DefineList getDefines() {
|
||||
return defines;
|
||||
}
|
||||
|
||||
public String getVertName(){
|
||||
return getShaderName(Shader.ShaderType.Vertex);
|
||||
}
|
||||
|
||||
public String getFragName() {
|
||||
return getShaderName(Shader.ShaderType.Fragment);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #getVertexShaderLanguage() } instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public String getLanguage() {
|
||||
return shaderLanguage.get(Shader.ShaderType.Vertex);
|
||||
}
|
||||
|
||||
public String getVertexShaderLanguage() {
|
||||
return shaderLanguage.get(Shader.ShaderType.Vertex);
|
||||
}
|
||||
|
||||
public String getFragmentShaderLanguage() {
|
||||
return shaderLanguage.get(Shader.ShaderType.Vertex);
|
||||
}
|
||||
|
||||
public boolean isUsesShaderNodes() {
|
||||
return usesShaderNodes;
|
||||
}
|
||||
|
||||
public void setUsesShaderNodes(boolean usesShaderNodes) {
|
||||
this.usesShaderNodes = usesShaderNodes;
|
||||
}
|
||||
|
||||
public Set<Shader.ShaderType> getUsedShaderPrograms(){
|
||||
return shaderName.keySet();
|
||||
}
|
||||
|
||||
public String getShaderProgramLanguage(Shader.ShaderType shaderType){
|
||||
return shaderLanguage.get(shaderType);
|
||||
}
|
||||
|
||||
public String getShaderProgramName(Shader.ShaderType shaderType){
|
||||
return getShaderName(shaderType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(JmeExporter ex) throws IOException{
|
||||
super.write(ex);
|
||||
OutputCapsule oc = ex.getCapsule(this);
|
||||
oc.write(shaderName.get(Shader.ShaderType.Fragment), "fragment_name", null);
|
||||
oc.write(shaderName.get(Shader.ShaderType.Geometry), "geometry_name", null);
|
||||
oc.write(shaderName.get(Shader.ShaderType.TessellationControl), "tessControl_name", null);
|
||||
oc.write(shaderName.get(Shader.ShaderType.TessellationEvaluation), "tessEval_name", null);
|
||||
oc.write(shaderLanguage.get(Shader.ShaderType.Vertex), "language", null);
|
||||
oc.write(shaderLanguage.get(Shader.ShaderType.Fragment), "frag_language", null);
|
||||
oc.write(shaderLanguage.get(Shader.ShaderType.Geometry), "geom_language", null);
|
||||
oc.write(shaderLanguage.get(Shader.ShaderType.TessellationControl), "tsctrl_language", null);
|
||||
oc.write(shaderLanguage.get(Shader.ShaderType.TessellationEvaluation), "tseval_language", null);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(JmeImporter im) throws IOException{
|
||||
super.read(im);
|
||||
InputCapsule ic = im.getCapsule(this);
|
||||
shaderName.put(Shader.ShaderType.Vertex,name);
|
||||
shaderName.put(Shader.ShaderType.Fragment,ic.readString("fragment_name", null));
|
||||
shaderName.put(Shader.ShaderType.Geometry,ic.readString("geometry_name", null));
|
||||
shaderName.put(Shader.ShaderType.TessellationControl,ic.readString("tessControl_name", null));
|
||||
shaderName.put(Shader.ShaderType.TessellationEvaluation,ic.readString("tessEval_name", null));
|
||||
shaderLanguage.put(Shader.ShaderType.Vertex,ic.readString("language", null));
|
||||
shaderLanguage.put(Shader.ShaderType.Fragment,ic.readString("frag_language", null));
|
||||
shaderLanguage.put(Shader.ShaderType.Geometry,ic.readString("geom_language", null));
|
||||
shaderLanguage.put(Shader.ShaderType.TessellationControl,ic.readString("tsctrl_language", null));
|
||||
shaderLanguage.put(Shader.ShaderType.TessellationEvaluation,ic.readString("tseval_language", null));
|
||||
}
|
||||
|
||||
}
|
@ -70,6 +70,30 @@ public class Uniform extends ShaderVariable {
|
||||
*/
|
||||
protected boolean setByCurrentMaterial = false;
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hash = 5;
|
||||
hash = 31 * hash + (this.value != null ? this.value.hashCode() : 0);
|
||||
hash = 31 * hash + (this.varType != null ? this.varType.hashCode() : 0);
|
||||
hash = 31 * hash + (this.binding != null ? this.binding.hashCode() : 0);
|
||||
return hash;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
final Uniform other = (Uniform) obj;
|
||||
if (this.value != other.value && (this.value == null || !this.value.equals(other.value))) {
|
||||
return false;
|
||||
}
|
||||
return this.binding == other.binding && this.varType == other.varType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString(){
|
||||
StringBuilder sb = new StringBuilder();
|
||||
@ -103,6 +127,10 @@ public class Uniform extends ShaderVariable {
|
||||
return value;
|
||||
}
|
||||
|
||||
public FloatBuffer getMultiData() {
|
||||
return multiData;
|
||||
}
|
||||
|
||||
public boolean isSetByCurrentMaterial() {
|
||||
return setByCurrentMaterial;
|
||||
}
|
||||
@ -111,21 +139,6 @@ public class Uniform extends ShaderVariable {
|
||||
setByCurrentMaterial = false;
|
||||
}
|
||||
|
||||
private static void setVector4(Vector4f vec, Object value) {
|
||||
if (value instanceof ColorRGBA) {
|
||||
ColorRGBA color = (ColorRGBA) value;
|
||||
vec.set(color.r, color.g, color.b, color.a);
|
||||
} else if (value instanceof Quaternion) {
|
||||
Quaternion quat = (Quaternion) value;
|
||||
vec.set(quat.getX(), quat.getY(), quat.getZ(), quat.getW());
|
||||
} else if (value instanceof Vector4f) {
|
||||
Vector4f vec4 = (Vector4f) value;
|
||||
vec.set(vec4);
|
||||
} else{
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
}
|
||||
|
||||
public void clearValue(){
|
||||
updateNeeded = true;
|
||||
|
||||
@ -182,27 +195,43 @@ public class Uniform extends ShaderVariable {
|
||||
}
|
||||
|
||||
if (value == null) {
|
||||
throw new NullPointerException();
|
||||
throw new IllegalArgumentException("for uniform " + name + ": value cannot be null");
|
||||
}
|
||||
|
||||
setByCurrentMaterial = true;
|
||||
|
||||
switch (type){
|
||||
case Matrix3:
|
||||
if (value.equals(this.value)) {
|
||||
return;
|
||||
}
|
||||
Matrix3f m3 = (Matrix3f) value;
|
||||
if (multiData == null) {
|
||||
multiData = BufferUtils.createFloatBuffer(9);
|
||||
}
|
||||
m3.fillFloatBuffer(multiData, true);
|
||||
multiData.clear();
|
||||
if (this.value == null) {
|
||||
this.value = new Matrix3f(m3);
|
||||
} else {
|
||||
((Matrix3f)this.value).set(m3);
|
||||
}
|
||||
break;
|
||||
case Matrix4:
|
||||
if (value.equals(this.value)) {
|
||||
return;
|
||||
}
|
||||
Matrix4f m4 = (Matrix4f) value;
|
||||
if (multiData == null) {
|
||||
multiData = BufferUtils.createFloatBuffer(16);
|
||||
}
|
||||
m4.fillFloatBuffer(multiData, true);
|
||||
multiData.clear();
|
||||
if (this.value == null) {
|
||||
this.value = new Matrix4f(m4);
|
||||
} else {
|
||||
((Matrix4f)this.value).copy(m4);
|
||||
}
|
||||
break;
|
||||
case IntArray:
|
||||
int[] ia = (int[]) value;
|
||||
@ -283,11 +312,32 @@ public class Uniform extends ShaderVariable {
|
||||
}
|
||||
multiData.clear();
|
||||
break;
|
||||
case Vector4:
|
||||
if (value.equals(this.value)) {
|
||||
return;
|
||||
}
|
||||
if (value instanceof ColorRGBA) {
|
||||
if (this.value == null) {
|
||||
this.value = new ColorRGBA();
|
||||
}
|
||||
((ColorRGBA) this.value).set((ColorRGBA) value);
|
||||
} else if (value instanceof Vector4f) {
|
||||
if (this.value == null) {
|
||||
this.value = new Vector4f();
|
||||
}
|
||||
((Vector4f) this.value).set((Vector4f) value);
|
||||
} else {
|
||||
if (this.value == null) {
|
||||
this.value = new Quaternion();
|
||||
}
|
||||
((Quaternion) this.value).set((Quaternion) value);
|
||||
}
|
||||
break;
|
||||
// Only use check if equals optimization for primitive values
|
||||
case Int:
|
||||
case Float:
|
||||
case Boolean:
|
||||
if (this.value != null && this.value.equals(value)) {
|
||||
if (value.equals(this.value)) {
|
||||
return;
|
||||
}
|
||||
this.value = value;
|
||||
@ -297,39 +347,38 @@ public class Uniform extends ShaderVariable {
|
||||
break;
|
||||
}
|
||||
|
||||
if (multiData != null) {
|
||||
this.value = multiData;
|
||||
}
|
||||
// if (multiData != null) {
|
||||
// this.value = multiData;
|
||||
// }
|
||||
|
||||
varType = type;
|
||||
updateNeeded = true;
|
||||
}
|
||||
|
||||
public void setVector4Length(int length){
|
||||
if (location == -1)
|
||||
if (location == -1) {
|
||||
return;
|
||||
|
||||
FloatBuffer fb = (FloatBuffer) value;
|
||||
if (fb == null || fb.capacity() < length * 4) {
|
||||
value = BufferUtils.createFloatBuffer(length * 4);
|
||||
}
|
||||
|
||||
multiData = BufferUtils.ensureLargeEnough(multiData, length * 4);
|
||||
value = multiData;
|
||||
varType = VarType.Vector4Array;
|
||||
updateNeeded = true;
|
||||
setByCurrentMaterial = true;
|
||||
}
|
||||
|
||||
public void setVector4InArray(float x, float y, float z, float w, int index){
|
||||
if (location == -1)
|
||||
if (location == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (varType != null && varType != VarType.Vector4Array)
|
||||
if (varType != null && varType != VarType.Vector4Array) {
|
||||
throw new IllegalArgumentException("Expected a " + varType.name() + " value!");
|
||||
}
|
||||
|
||||
FloatBuffer fb = (FloatBuffer) value;
|
||||
fb.position(index * 4);
|
||||
fb.put(x).put(y).put(z).put(w);
|
||||
fb.rewind();
|
||||
multiData.position(index * 4);
|
||||
multiData.put(x).put(y).put(z).put(w);
|
||||
multiData.rewind();
|
||||
updateNeeded = true;
|
||||
setByCurrentMaterial = true;
|
||||
}
|
||||
|
@ -36,7 +36,7 @@ import com.jme3.math.*;
|
||||
import com.jme3.renderer.Camera;
|
||||
import com.jme3.renderer.RenderManager;
|
||||
import com.jme3.system.Timer;
|
||||
import java.util.List;
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* <code>UniformBindingManager</code> helps {@link RenderManager} to manage
|
||||
@ -84,7 +84,8 @@ public class UniformBindingManager {
|
||||
* Updates the given list of uniforms with {@link UniformBinding uniform bindings}
|
||||
* based on the current world state.
|
||||
*/
|
||||
public void updateUniformBindings(List<Uniform> params) {
|
||||
public void updateUniformBindings(Shader shader) {
|
||||
ArrayList<Uniform> params = shader.getBoundUniforms();
|
||||
for (int i = 0; i < params.size(); i++) {
|
||||
Uniform u = params.get(i);
|
||||
switch (u.getBinding()) {
|
||||
|
@ -55,7 +55,7 @@ public enum VarType {
|
||||
TextureBuffer(false,true,"sampler1D|sampler1DShadow"),
|
||||
Texture2D(false,true,"sampler2D|sampler2DShadow"),
|
||||
Texture3D(false,true,"sampler3D"),
|
||||
TextureArray(false,true,"sampler2DArray"),
|
||||
TextureArray(false,true,"sampler2DArray|sampler2DArrayShadow"),
|
||||
TextureCubeMap(false,true,"samplerCube"),
|
||||
Int("int");
|
||||
|
||||
|
@ -128,12 +128,13 @@ public class NullContext implements JmeContext, Runnable {
|
||||
public void run(){
|
||||
initInThread();
|
||||
|
||||
while (!needClose.get()){
|
||||
do {
|
||||
listener.update();
|
||||
|
||||
if (frameRate > 0)
|
||||
if (frameRate > 0) {
|
||||
sync(frameRate);
|
||||
}
|
||||
} while (!needClose.get());
|
||||
|
||||
deinitInThread();
|
||||
|
||||
|
@ -51,7 +51,7 @@ import com.jme3.texture.Texture;
|
||||
|
||||
public class NullRenderer implements Renderer {
|
||||
|
||||
private static final EnumSet<Caps> caps = EnumSet.noneOf(Caps.class);
|
||||
private static final EnumSet<Caps> caps = EnumSet.allOf(Caps.class);
|
||||
private static final Statistics stats = new Statistics();
|
||||
|
||||
public void initialize() {
|
||||
|
@ -97,6 +97,7 @@ public class FrameBuffer extends NativeObject {
|
||||
int id = -1;
|
||||
int slot = SLOT_UNDEF;
|
||||
int face = -1;
|
||||
int layer = -1;
|
||||
|
||||
/**
|
||||
* @return The image format of the render buffer.
|
||||
@ -160,6 +161,10 @@ public class FrameBuffer extends NativeObject {
|
||||
return "BufferTarget[format=" + format + "]";
|
||||
}
|
||||
}
|
||||
|
||||
public int getLayer() {
|
||||
return this.layer;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -324,6 +329,19 @@ public class FrameBuffer extends NativeObject {
|
||||
addColorTexture(tex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the color texture array to use for this framebuffer.
|
||||
* This automatically clears all existing textures added previously
|
||||
* with {@link FrameBuffer#addColorTexture } and adds this texture as the
|
||||
* only target.
|
||||
*
|
||||
* @param tex The color texture array to set.
|
||||
*/
|
||||
public void setColorTexture(TextureArray tex, int layer){
|
||||
clearColorTargets();
|
||||
addColorTexture(tex, layer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the color texture to use for this framebuffer.
|
||||
* This automatically clears all existing textures added previously
|
||||
@ -369,6 +387,31 @@ public class FrameBuffer extends NativeObject {
|
||||
colorBufs.add(colorBuf);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a color texture array to use for this framebuffer.
|
||||
* If MRT is enabled, then each subsequently added texture can be
|
||||
* rendered to through a shader that writes to the array <code>gl_FragData</code>.
|
||||
* If MRT is not enabled, then the index set with {@link FrameBuffer#setTargetIndex(int) }
|
||||
* is rendered to by the shader.
|
||||
*
|
||||
* @param tex The texture array to add.
|
||||
*/
|
||||
public void addColorTexture(TextureArray tex, int layer) {
|
||||
if (id != -1)
|
||||
throw new UnsupportedOperationException("FrameBuffer already initialized.");
|
||||
|
||||
Image img = tex.getImage();
|
||||
checkSetTexture(tex, false);
|
||||
|
||||
RenderBuffer colorBuf = new RenderBuffer();
|
||||
colorBuf.slot = colorBufs.size();
|
||||
colorBuf.tex = tex;
|
||||
colorBuf.format = img.getFormat();
|
||||
colorBuf.layer = layer;
|
||||
|
||||
colorBufs.add(colorBuf);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a color texture to use for this framebuffer.
|
||||
* If MRT is enabled, then each subsequently added texture can be
|
||||
@ -412,6 +455,19 @@ public class FrameBuffer extends NativeObject {
|
||||
depthBuf.tex = tex;
|
||||
depthBuf.format = img.getFormat();
|
||||
}
|
||||
public void setDepthTexture(TextureArray tex, int layer){
|
||||
if (id != -1)
|
||||
throw new UnsupportedOperationException("FrameBuffer already initialized.");
|
||||
|
||||
Image img = tex.getImage();
|
||||
checkSetTexture(tex, true);
|
||||
|
||||
depthBuf = new RenderBuffer();
|
||||
depthBuf.slot = img.getFormat().isDepthStencilFormat() ? SLOT_DEPTH_STENCIL : SLOT_DEPTH;
|
||||
depthBuf.tex = tex;
|
||||
depthBuf.format = img.getFormat();
|
||||
depthBuf.layer = layer;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The number of color buffers attached to this texture.
|
||||
|
@ -32,6 +32,8 @@
|
||||
package com.jme3.util;
|
||||
|
||||
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.Map;
|
||||
import java.util.NoSuchElementException;
|
||||
@ -43,7 +45,7 @@ import java.util.NoSuchElementException;
|
||||
*
|
||||
* @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 final float loadFactor;
|
||||
@ -93,6 +95,26 @@ public final class IntMap<T> implements Iterable<Entry<T>>, Cloneable {
|
||||
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) {
|
||||
Entry[] table = this.table;
|
||||
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;
|
||||
T value;
|
||||
@ -303,5 +325,20 @@ public final class IntMap<T> implements Iterable<Entry<T>>, Cloneable {
|
||||
}
|
||||
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$
|
||||
* @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
|
||||
// incorrect or non-optimal behavior from AbstractList. For
|
||||
@ -97,6 +97,24 @@ public class SafeArrayList<E> implements List<E> {
|
||||
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) {
|
||||
return (T[])java.lang.reflect.Array.newInstance(type, size);
|
||||
}
|
||||
|
@ -37,6 +37,8 @@ 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;
|
||||
|
||||
@ -97,6 +99,8 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||
*/
|
||||
public class Cloner {
|
||||
|
||||
static Logger log = Logger.getLogger(Cloner.class.getName());
|
||||
|
||||
/**
|
||||
* Keeps track of the objects that have been cloned so far.
|
||||
*/
|
||||
@ -190,14 +194,24 @@ public class Cloner {
|
||||
* 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);
|
||||
}
|
||||
|
||||
@ -213,6 +227,15 @@ public class Cloner {
|
||||
// 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;
|
||||
}
|
||||
|
||||
@ -246,14 +269,20 @@ public class Cloner {
|
||||
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 the exact Java type. Note: no inheritence
|
||||
* checks are performed so a function must be registered for each specific type
|
||||
* that it handles. By default ListCloneFunction is registered for
|
||||
* ArrayList, LinkedList, CopyOnWriteArrayList, Vector, Stack, and JME's SafeArrayList.
|
||||
* 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 ) {
|
||||
@ -269,7 +298,42 @@ public class Cloner {
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> CloneFunction<T> getCloneFunction( Class<T> type ) {
|
||||
return (CloneFunction<T>)functions.get(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);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -290,6 +354,9 @@ public class Cloner {
|
||||
* clone() and objects without necessarily knowing their real type.</p>
|
||||
*/
|
||||
public <T> T javaClone( T object ) throws CloneNotSupportedException {
|
||||
if( object == null ) {
|
||||
return null;
|
||||
}
|
||||
Method m = methodCache.get(object.getClass());
|
||||
if( m == null ) {
|
||||
try {
|
||||
|
@ -149,7 +149,7 @@ MaterialDef Phong Lighting {
|
||||
SEPARATE_TEXCOORD : SeparateTexCoord
|
||||
DISCARD_ALPHA : AlphaDiscardThreshold
|
||||
USE_REFLECTION : EnvMap
|
||||
SPHERE_MAP : SphereMap
|
||||
SPHERE_MAP : EnvMapAsSphereMap
|
||||
NUM_BONES : NumberOfBones
|
||||
INSTANCING : UseInstancing
|
||||
}
|
||||
@ -188,7 +188,7 @@ MaterialDef Phong Lighting {
|
||||
SEPARATE_TEXCOORD : SeparateTexCoord
|
||||
DISCARD_ALPHA : AlphaDiscardThreshold
|
||||
USE_REFLECTION : EnvMap
|
||||
SPHERE_MAP : SphereMap
|
||||
SPHERE_MAP : EnvMapAsSphereMap
|
||||
NUM_BONES : NumberOfBones
|
||||
INSTANCING : UseInstancing
|
||||
}
|
||||
@ -209,7 +209,6 @@ MaterialDef Phong Lighting {
|
||||
}
|
||||
|
||||
Defines {
|
||||
COLOR_MAP : ColorMap
|
||||
DISCARD_ALPHA : AlphaDiscardThreshold
|
||||
NUM_BONES : NumberOfBones
|
||||
INSTANCING : UseInstancing
|
||||
@ -235,7 +234,6 @@ MaterialDef Phong Lighting {
|
||||
FILTER_MODE : FilterMode
|
||||
PCFEDGE : PCFEdge
|
||||
DISCARD_ALPHA : AlphaDiscardThreshold
|
||||
COLOR_MAP : ColorMap
|
||||
SHADOWMAP_SIZE : ShadowMapSize
|
||||
FADE : FadeInfo
|
||||
PSSM : Splits
|
||||
@ -269,7 +267,6 @@ MaterialDef Phong Lighting {
|
||||
FILTER_MODE : FilterMode
|
||||
PCFEDGE : PCFEdge
|
||||
DISCARD_ALPHA : AlphaDiscardThreshold
|
||||
COLOR_MAP : ColorMap
|
||||
SHADOWMAP_SIZE : ShadowMapSize
|
||||
FADE : FadeInfo
|
||||
PSSM : Splits
|
||||
@ -343,10 +340,6 @@ MaterialDef Phong Lighting {
|
||||
Defines {
|
||||
VERTEX_COLOR : UseVertexColor
|
||||
MATERIAL_COLORS : UseMaterialColors
|
||||
V_TANGENT : VTangent
|
||||
MINNAERT : Minnaert
|
||||
WARDISO : WardIso
|
||||
|
||||
DIFFUSEMAP : DiffuseMap
|
||||
NORMALMAP : NormalMap
|
||||
SPECULARMAP : SpecularMap
|
||||
|
@ -41,8 +41,7 @@ varying vec3 SpecularSum;
|
||||
|
||||
#ifdef NORMALMAP
|
||||
uniform sampler2D m_NormalMap;
|
||||
varying vec3 vTangent;
|
||||
varying vec3 vBinormal;
|
||||
varying vec4 vTangent;
|
||||
#endif
|
||||
varying vec3 vNormal;
|
||||
|
||||
@ -71,7 +70,7 @@ uniform float m_Shininess;
|
||||
void main(){
|
||||
#if !defined(VERTEX_LIGHTING)
|
||||
#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)
|
||||
{
|
||||
|
@ -39,8 +39,7 @@ attribute vec3 inNormal;
|
||||
varying vec3 vPos;
|
||||
#ifdef NORMALMAP
|
||||
attribute vec4 inTangent;
|
||||
varying vec3 vTangent;
|
||||
varying vec3 vBinormal;
|
||||
varying vec4 vTangent;
|
||||
#endif
|
||||
#else
|
||||
#ifdef COLORRAMP
|
||||
@ -104,8 +103,7 @@ void main(){
|
||||
|
||||
|
||||
#if defined(NORMALMAP) && !defined(VERTEX_LIGHTING)
|
||||
vTangent = TransformNormal(modelSpaceTan);
|
||||
vBinormal = cross(wvNormal, vTangent)* inTangent.w;
|
||||
vTangent = vec4(TransformNormal(modelSpaceTan).xyz,inTangent.w);
|
||||
vNormal = wvNormal;
|
||||
vPos = wvPosition;
|
||||
#elif !defined(VERTEX_LIGHTING)
|
||||
|
@ -25,9 +25,9 @@ uniform vec2 g_ResolutionInverse;
|
||||
uniform mat4 m_LightViewProjectionMatrix4;
|
||||
uniform mat4 m_LightViewProjectionMatrix5;
|
||||
#else
|
||||
uniform vec3 m_LightDir;
|
||||
#ifndef PSSM
|
||||
uniform vec3 m_LightPos;
|
||||
uniform vec3 m_LightDir;
|
||||
#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
|
@ -31,6 +31,9 @@
|
||||
*/
|
||||
package com.jme3.material.plugins;
|
||||
|
||||
import com.jme3.material.logic.MultiPassLightingLogic;
|
||||
import com.jme3.material.logic.SinglePassLightingLogic;
|
||||
import com.jme3.material.logic.DefaultTechniqueDefLogic;
|
||||
import com.jme3.asset.*;
|
||||
import com.jme3.material.*;
|
||||
import com.jme3.material.RenderState.BlendMode;
|
||||
@ -40,6 +43,7 @@ import com.jme3.material.TechniqueDef.ShadowMode;
|
||||
import com.jme3.math.ColorRGBA;
|
||||
import com.jme3.math.Vector2f;
|
||||
import com.jme3.math.Vector3f;
|
||||
import com.jme3.shader.DefineList;
|
||||
import com.jme3.shader.Shader;
|
||||
import com.jme3.shader.VarType;
|
||||
import com.jme3.texture.Texture;
|
||||
@ -73,6 +77,7 @@ public class J3MLoader implements AssetLoader {
|
||||
private Material material;
|
||||
private TechniqueDef technique;
|
||||
private RenderState renderState;
|
||||
private ArrayList<String> presetDefines = new ArrayList<String>();
|
||||
|
||||
private EnumMap<Shader.ShaderType, String> shaderLanguage;
|
||||
private EnumMap<Shader.ShaderType, String> shaderName;
|
||||
@ -115,6 +120,10 @@ public class J3MLoader implements AssetLoader {
|
||||
throw new IOException("LightMode statement syntax incorrect");
|
||||
}
|
||||
LightMode lm = LightMode.valueOf(split[1]);
|
||||
if (lm == LightMode.FixedPipeline) {
|
||||
throw new UnsupportedOperationException("OpenGL1 is not supported");
|
||||
}
|
||||
|
||||
technique.setLightMode(lm);
|
||||
}
|
||||
|
||||
@ -458,6 +467,8 @@ public class J3MLoader implements AssetLoader {
|
||||
renderState.setDepthFunc(RenderState.TestFunction.valueOf(split[1]));
|
||||
}else if (split[0].equals("AlphaFunc")){
|
||||
renderState.setAlphaFunc(RenderState.TestFunction.valueOf(split[1]));
|
||||
}else if (split[0].equals("LineWidth")){
|
||||
renderState.setLineWidth(Float.parseFloat(split[1]));
|
||||
} else {
|
||||
throw new MatParseException(null, split[0], statement);
|
||||
}
|
||||
@ -493,10 +504,22 @@ public class J3MLoader implements AssetLoader {
|
||||
private void readDefine(String statement) throws IOException{
|
||||
String[] split = statement.split(":");
|
||||
if (split.length == 1){
|
||||
// add preset define
|
||||
technique.addShaderPresetDefine(split[0].trim(), VarType.Boolean, true);
|
||||
String defineName = split[0].trim();
|
||||
presetDefines.add(defineName);
|
||||
}else if (split.length == 2){
|
||||
technique.addShaderParamDefine(split[1].trim(), split[0].trim());
|
||||
String defineName = split[0].trim();
|
||||
String paramName = split[1].trim();
|
||||
MatParam param = materialDef.getMaterialParam(paramName);
|
||||
if (param == null) {
|
||||
logger.log(Level.WARNING, "In technique ''{0}'':\n"
|
||||
+ "Define ''{1}'' mapped to non-existent"
|
||||
+ " material parameter ''{2}'', ignoring.",
|
||||
new Object[]{technique.getName(), defineName, paramName});
|
||||
return;
|
||||
}
|
||||
|
||||
VarType paramType = param.getVarType();
|
||||
technique.addShaderParamDefine(paramName, paramType, defineName);
|
||||
}else{
|
||||
throw new IOException("Define syntax incorrect");
|
||||
}
|
||||
@ -559,14 +582,27 @@ public class J3MLoader implements AssetLoader {
|
||||
material.setTransparent(parseBoolean(split[1]));
|
||||
}
|
||||
|
||||
private static String createShaderPrologue(List<String> presetDefines) {
|
||||
DefineList dl = new DefineList(presetDefines.size());
|
||||
for (int i = 0; i < presetDefines.size(); i++) {
|
||||
dl.set(i, 1);
|
||||
}
|
||||
StringBuilder sb = new StringBuilder();
|
||||
dl.generateSource(sb, presetDefines, null);
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private void readTechnique(Statement techStat) throws IOException{
|
||||
isUseNodes = false;
|
||||
String[] split = techStat.getLine().split(whitespacePattern);
|
||||
|
||||
if (split.length == 1) {
|
||||
technique = new TechniqueDef(null);
|
||||
String techniqueUniqueName = materialDef.getAssetName() + "@Default";
|
||||
technique = new TechniqueDef(null, techniqueUniqueName.hashCode());
|
||||
} else if (split.length == 2) {
|
||||
String techName = split[1];
|
||||
technique = new TechniqueDef(techName);
|
||||
String techniqueUniqueName = materialDef.getAssetName() + "@" + techName;
|
||||
technique = new TechniqueDef(techName, techniqueUniqueName.hashCode());
|
||||
} else {
|
||||
throw new IOException("Technique statement syntax incorrect");
|
||||
}
|
||||
@ -577,7 +613,12 @@ public class J3MLoader implements AssetLoader {
|
||||
|
||||
if(isUseNodes){
|
||||
nodesLoaderDelegate.computeConditions();
|
||||
|
||||
//used for caching later, the shader here is not a file.
|
||||
|
||||
// KIRILL 9/19/2015
|
||||
// Not sure if this is needed anymore, since shader caching
|
||||
// is now done by TechniqueDef.
|
||||
technique.setShaderFile(technique.hashCode() + "", technique.hashCode() + "", "GLSL100", "GLSL100");
|
||||
}
|
||||
|
||||
@ -585,10 +626,27 @@ public class J3MLoader implements AssetLoader {
|
||||
technique.setShaderFile(shaderName, shaderLanguage);
|
||||
}
|
||||
|
||||
technique.setShaderPrologue(createShaderPrologue(presetDefines));
|
||||
|
||||
switch (technique.getLightMode()) {
|
||||
case Disable:
|
||||
technique.setLogic(new DefaultTechniqueDefLogic(technique));
|
||||
break;
|
||||
case MultiPass:
|
||||
technique.setLogic(new MultiPassLightingLogic(technique));
|
||||
break;
|
||||
case SinglePass:
|
||||
technique.setLogic(new SinglePassLightingLogic(technique));
|
||||
break;
|
||||
default:
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
materialDef.addTechniqueDef(technique);
|
||||
technique = null;
|
||||
shaderLanguage.clear();
|
||||
shaderName.clear();
|
||||
presetDefines.clear();
|
||||
}
|
||||
|
||||
private void loadFromRoot(List<Statement> roots) throws IOException{
|
||||
|
@ -44,6 +44,7 @@ import com.jme3.shader.ShaderNodeDefinition;
|
||||
import com.jme3.shader.ShaderNodeVariable;
|
||||
import com.jme3.shader.ShaderUtils;
|
||||
import com.jme3.shader.UniformBinding;
|
||||
import com.jme3.shader.VarType;
|
||||
import com.jme3.shader.VariableMapping;
|
||||
import com.jme3.util.blockparser.Statement;
|
||||
import java.io.IOException;
|
||||
@ -583,7 +584,7 @@ public class ShaderNodeLoaderDelegate {
|
||||
//multiplicity is not an int attempting to find for a material parameter.
|
||||
MatParam mp = findMatParam(multiplicity);
|
||||
if (mp != null) {
|
||||
addDefine(multiplicity);
|
||||
addDefine(multiplicity, VarType.Int);
|
||||
multiplicity = multiplicity.toUpperCase();
|
||||
} else {
|
||||
throw new MatParseException("Wrong multiplicity for variable" + mapping.getLeftVariable().getName() + ". " + multiplicity + " should be an int or a declared material parameter.", statement);
|
||||
@ -625,9 +626,9 @@ public class ShaderNodeLoaderDelegate {
|
||||
*
|
||||
* @param paramName
|
||||
*/
|
||||
public void addDefine(String paramName) {
|
||||
public void addDefine(String paramName, VarType paramType) {
|
||||
if (techniqueDef.getShaderParamDefine(paramName) == null) {
|
||||
techniqueDef.addShaderParamDefine(paramName, paramName.toUpperCase());
|
||||
techniqueDef.addShaderParamDefine(paramName, paramType, paramName.toUpperCase());
|
||||
}
|
||||
}
|
||||
|
||||
@ -660,7 +661,7 @@ public class ShaderNodeLoaderDelegate {
|
||||
for (String string : defines) {
|
||||
MatParam param = findMatParam(string);
|
||||
if (param != null) {
|
||||
addDefine(param.getName());
|
||||
addDefine(param.getName(), param.getVarType());
|
||||
} else {
|
||||
throw new MatParseException("Invalid condition, condition must match a Material Parameter named " + cond, statement);
|
||||
}
|
||||
@ -752,6 +753,7 @@ public class ShaderNodeLoaderDelegate {
|
||||
}
|
||||
right.setNameSpace(node.getName());
|
||||
right.setType(var.getType());
|
||||
right.setMultiplicity(var.getMultiplicity());
|
||||
mapping.setRightVariable(right);
|
||||
storeVaryings(node, mapping.getRightVariable());
|
||||
|
||||
|
@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2015 jMonkeyEngine
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package com.jme3.asset;
|
||||
|
||||
import com.jme3.asset.plugins.ClasspathLocator;
|
||||
import com.jme3.shader.plugins.GLSLLoader;
|
||||
import com.jme3.system.JmeSystem;
|
||||
import com.jme3.system.MockJmeSystemDelegate;
|
||||
import org.junit.Test;
|
||||
|
||||
public class LoadShaderSourceTest {
|
||||
|
||||
@Test
|
||||
public void testLoadShaderSource() {
|
||||
JmeSystem.setSystemDelegate(new MockJmeSystemDelegate());
|
||||
AssetManager assetManager = new DesktopAssetManager();
|
||||
assetManager.registerLocator(null, ClasspathLocator.class);
|
||||
assetManager.registerLoader(GLSLLoader.class, "frag");
|
||||
String showNormals = (String) assetManager.loadAsset("Common/MatDefs/Misc/ShowNormals.frag");
|
||||
System.out.println(showNormals);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,538 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2016 jMonkeyEngine
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package com.jme3.material;
|
||||
|
||||
import com.jme3.asset.AssetManager;
|
||||
import com.jme3.light.LightList;
|
||||
import com.jme3.math.Matrix4f;
|
||||
import com.jme3.renderer.RenderManager;
|
||||
import com.jme3.scene.Geometry;
|
||||
import com.jme3.scene.shape.Box;
|
||||
import com.jme3.shader.Shader;
|
||||
import com.jme3.shader.Uniform;
|
||||
import com.jme3.shader.VarType;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import static com.jme3.scene.MPOTestUtils.*;
|
||||
import com.jme3.shader.DefineList;
|
||||
import com.jme3.system.NullRenderer;
|
||||
import com.jme3.system.TestUtil;
|
||||
import com.jme3.texture.Image.Format;
|
||||
import com.jme3.texture.Texture;
|
||||
import com.jme3.texture.Texture2D;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Validates how {@link MatParamOverride MPOs} work on the material level.
|
||||
*
|
||||
* @author Kirill Vainer
|
||||
*/
|
||||
public class MaterialMatParamOverrideTest {
|
||||
|
||||
private static final HashSet<String> IGNORED_UNIFORMS = new HashSet<String>(
|
||||
Arrays.asList(new String[]{"m_ParallaxHeight", "m_Shininess", "m_BackfaceShadows"}));
|
||||
|
||||
@Test
|
||||
public void testBoolMpoOnly() {
|
||||
material("Common/MatDefs/Light/Lighting.j3md");
|
||||
inputMpo(mpoBool("UseMaterialColors", true));
|
||||
outDefines(def("MATERIAL_COLORS", VarType.Boolean, true));
|
||||
outUniforms(uniform("UseMaterialColors", VarType.Boolean, true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBoolMpOnly() {
|
||||
material("Common/MatDefs/Light/Lighting.j3md");
|
||||
inputMp(mpoBool("UseMaterialColors", true));
|
||||
outDefines(def("MATERIAL_COLORS", VarType.Boolean, true));
|
||||
outUniforms(uniform("UseMaterialColors", VarType.Boolean, true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBoolOverrideFalse() {
|
||||
material("Common/MatDefs/Light/Lighting.j3md");
|
||||
inputMp(mpoBool("UseMaterialColors", true));
|
||||
inputMpo(mpoBool("UseMaterialColors", false));
|
||||
outDefines();
|
||||
outUniforms(uniform("UseMaterialColors", VarType.Boolean, false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBoolOverrideTrue() {
|
||||
material("Common/MatDefs/Light/Lighting.j3md");
|
||||
inputMp(mpoBool("UseMaterialColors", false));
|
||||
inputMpo(mpoBool("UseMaterialColors", true));
|
||||
outDefines(def("MATERIAL_COLORS", VarType.Boolean, true));
|
||||
outUniforms(uniform("UseMaterialColors", VarType.Boolean, true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFloatMpoOnly() {
|
||||
material("Common/MatDefs/Light/Lighting.j3md");
|
||||
inputMpo(mpoFloat("AlphaDiscardThreshold", 3.12f));
|
||||
outDefines(def("DISCARD_ALPHA", VarType.Float, 3.12f));
|
||||
outUniforms(uniform("AlphaDiscardThreshold", VarType.Float, 3.12f));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFloatMpOnly() {
|
||||
material("Common/MatDefs/Light/Lighting.j3md");
|
||||
inputMp(mpoFloat("AlphaDiscardThreshold", 3.12f));
|
||||
outDefines(def("DISCARD_ALPHA", VarType.Float, 3.12f));
|
||||
outUniforms(uniform("AlphaDiscardThreshold", VarType.Float, 3.12f));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFloatOverride() {
|
||||
material("Common/MatDefs/Light/Lighting.j3md");
|
||||
inputMp(mpoFloat("AlphaDiscardThreshold", 3.12f));
|
||||
inputMpo(mpoFloat("AlphaDiscardThreshold", 2.79f));
|
||||
outDefines(def("DISCARD_ALPHA", VarType.Float, 2.79f));
|
||||
outUniforms(uniform("AlphaDiscardThreshold", VarType.Float, 2.79f));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMpoDisable() {
|
||||
material("Common/MatDefs/Light/Lighting.j3md");
|
||||
inputMp(mpoFloat("AlphaDiscardThreshold", 3.12f));
|
||||
|
||||
MatParamOverride override = mpoFloat("AlphaDiscardThreshold", 2.79f);
|
||||
inputMpo(override);
|
||||
outDefines(def("DISCARD_ALPHA", VarType.Float, 2.79f));
|
||||
outUniforms(uniform("AlphaDiscardThreshold", VarType.Float, 2.79f));
|
||||
|
||||
reset();
|
||||
override.setEnabled(false);
|
||||
outDefines(def("DISCARD_ALPHA", VarType.Float, 3.12f));
|
||||
outUniforms(uniform("AlphaDiscardThreshold", VarType.Float, 3.12f));
|
||||
|
||||
reset();
|
||||
override.setEnabled(true);
|
||||
outDefines(def("DISCARD_ALPHA", VarType.Float, 2.79f));
|
||||
outUniforms(uniform("AlphaDiscardThreshold", VarType.Float, 2.79f));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIntMpoOnly() {
|
||||
material("Common/MatDefs/Light/Lighting.j3md");
|
||||
inputMpo(mpoInt("NumberOfBones", 1234));
|
||||
outDefines(def("NUM_BONES", VarType.Int, 1234));
|
||||
outUniforms(uniform("NumberOfBones", VarType.Int, 1234));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIntMpOnly() {
|
||||
material("Common/MatDefs/Light/Lighting.j3md");
|
||||
inputMp(mpoInt("NumberOfBones", 1234));
|
||||
outDefines(def("NUM_BONES", VarType.Int, 1234));
|
||||
outUniforms(uniform("NumberOfBones", VarType.Int, 1234));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIntOverride() {
|
||||
material("Common/MatDefs/Light/Lighting.j3md");
|
||||
inputMp(mpoInt("NumberOfBones", 1234));
|
||||
inputMpo(mpoInt("NumberOfBones", 4321));
|
||||
outDefines(def("NUM_BONES", VarType.Int, 4321));
|
||||
outUniforms(uniform("NumberOfBones", VarType.Int, 4321));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatrixArray() {
|
||||
Matrix4f[] matrices = new Matrix4f[]{
|
||||
new Matrix4f()
|
||||
};
|
||||
|
||||
material("Common/MatDefs/Light/Lighting.j3md");
|
||||
inputMpo(mpoMatrix4Array("BoneMatrices", matrices));
|
||||
outDefines();
|
||||
outUniforms(uniform("BoneMatrices", VarType.Matrix4Array, matrices));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNonExistentParameter() {
|
||||
material("Common/MatDefs/Light/Lighting.j3md");
|
||||
inputMpo(mpoInt("NonExistent", 4321));
|
||||
outDefines();
|
||||
outUniforms();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWrongType() {
|
||||
material("Common/MatDefs/Light/Lighting.j3md");
|
||||
inputMpo(mpoInt("UseMaterialColors", 4321));
|
||||
outDefines();
|
||||
outUniforms();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParamOnly() {
|
||||
material("Common/MatDefs/Light/Lighting.j3md");
|
||||
inputMpo(mpoFloat("ShadowMapSize", 3.12f));
|
||||
outDefines();
|
||||
outUniforms(uniform("ShadowMapSize", VarType.Float, 3.12f));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemove() {
|
||||
material("Common/MatDefs/Light/Lighting.j3md");
|
||||
|
||||
reset();
|
||||
inputMp(mpoInt("NumberOfBones", 1234));
|
||||
outDefines(def("NUM_BONES", VarType.Int, 1234));
|
||||
outUniforms(uniform("NumberOfBones", VarType.Int, 1234));
|
||||
|
||||
reset();
|
||||
inputMpo(mpoInt("NumberOfBones", 4321));
|
||||
outDefines(def("NUM_BONES", VarType.Int, 4321));
|
||||
outUniforms(uniform("NumberOfBones", VarType.Int, 4321));
|
||||
|
||||
reset();
|
||||
geometry.clearMatParamOverrides();
|
||||
geometry.updateGeometricState();
|
||||
outDefines(def("NUM_BONES", VarType.Int, 1234));
|
||||
outUniforms(uniform("NumberOfBones", VarType.Int, 1234));
|
||||
|
||||
reset();
|
||||
geometry.getMaterial().clearParam("NumberOfBones");
|
||||
outDefines();
|
||||
outUniforms();
|
||||
|
||||
reset();
|
||||
inputMpo(mpoInt("NumberOfBones", 4321));
|
||||
outDefines(def("NUM_BONES", VarType.Int, 4321));
|
||||
outUniforms(uniform("NumberOfBones", VarType.Int, 4321));
|
||||
|
||||
reset();
|
||||
inputMp(mpoInt("NumberOfBones", 1234));
|
||||
outDefines(def("NUM_BONES", VarType.Int, 4321));
|
||||
outUniforms(uniform("NumberOfBones", VarType.Int, 4321));
|
||||
}
|
||||
|
||||
public void testRemoveOverride() {
|
||||
material("Common/MatDefs/Light/Lighting.j3md");
|
||||
|
||||
reset();
|
||||
inputMp(mpoInt("NumberOfBones", 1234));
|
||||
outDefines(def("NUM_BONES", VarType.Int, 1234));
|
||||
outUniforms(uniform("NumberOfBones", VarType.Int, 1234));
|
||||
|
||||
reset();
|
||||
inputMpo(mpoInt("NumberOfBones", 4321));
|
||||
outDefines(def("NUM_BONES", VarType.Int, 4321));
|
||||
outUniforms(uniform("NumberOfBones", VarType.Int, 4321));
|
||||
|
||||
reset();
|
||||
geometry.clearMatParamOverrides();
|
||||
outDefines(def("NUM_BONES", VarType.Int, 1234));
|
||||
outUniforms(uniform("NumberOfBones", VarType.Int, 1234));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemoveMpoOnly() {
|
||||
material("Common/MatDefs/Light/Lighting.j3md");
|
||||
|
||||
reset();
|
||||
inputMpo(mpoInt("NumberOfBones", 4321));
|
||||
outDefines(def("NUM_BONES", VarType.Int, 4321));
|
||||
outUniforms(uniform("NumberOfBones", VarType.Int, 4321));
|
||||
|
||||
reset();
|
||||
geometry.clearMatParamOverrides();
|
||||
geometry.updateGeometricState();
|
||||
outDefines();
|
||||
outUniforms();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTextureMpoOnly() {
|
||||
material("Common/MatDefs/Light/Lighting.j3md");
|
||||
Texture2D tex = new Texture2D(128, 128, Format.RGBA8);
|
||||
|
||||
inputMpo(mpoTexture2D("DiffuseMap", tex));
|
||||
outDefines(def("DIFFUSEMAP", VarType.Texture2D, tex));
|
||||
outUniforms(uniform("DiffuseMap", VarType.Int, 0));
|
||||
outTextures(tex);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTextureOverride() {
|
||||
material("Common/MatDefs/Light/Lighting.j3md");
|
||||
Texture2D tex1 = new Texture2D(128, 128, Format.RGBA8);
|
||||
Texture2D tex2 = new Texture2D(128, 128, Format.RGBA8);
|
||||
|
||||
inputMp(mpoTexture2D("DiffuseMap", tex1));
|
||||
inputMpo(mpoTexture2D("DiffuseMap", tex2));
|
||||
|
||||
outDefines(def("DIFFUSEMAP", VarType.Texture2D, tex2));
|
||||
outUniforms(uniform("DiffuseMap", VarType.Int, 0));
|
||||
outTextures(tex2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemoveTexture() {
|
||||
material("Common/MatDefs/Light/Lighting.j3md");
|
||||
Texture2D tex = new Texture2D(128, 128, Format.RGBA8);
|
||||
|
||||
reset();
|
||||
inputMpo(mpoTexture2D("DiffuseMap", tex));
|
||||
outDefines(def("DIFFUSEMAP", VarType.Texture2D, tex));
|
||||
outUniforms(uniform("DiffuseMap", VarType.Int, 0));
|
||||
outTextures(tex);
|
||||
|
||||
reset();
|
||||
geometry.clearMatParamOverrides();
|
||||
geometry.updateGeometricState();
|
||||
outDefines();
|
||||
outUniforms();
|
||||
outTextures();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemoveTextureOverride() {
|
||||
material("Common/MatDefs/Light/Lighting.j3md");
|
||||
Texture2D tex1 = new Texture2D(128, 128, Format.RGBA8);
|
||||
Texture2D tex2 = new Texture2D(128, 128, Format.RGBA8);
|
||||
|
||||
reset();
|
||||
inputMp(mpoTexture2D("DiffuseMap", tex1));
|
||||
outDefines(def("DIFFUSEMAP", VarType.Texture2D, tex1));
|
||||
outUniforms(uniform("DiffuseMap", VarType.Int, 0));
|
||||
outTextures(tex1);
|
||||
|
||||
reset();
|
||||
inputMpo(mpoTexture2D("DiffuseMap", tex2));
|
||||
outDefines(def("DIFFUSEMAP", VarType.Texture2D, tex2));
|
||||
outUniforms(uniform("DiffuseMap", VarType.Int, 0));
|
||||
outTextures(tex2);
|
||||
|
||||
reset();
|
||||
geometry.clearMatParamOverrides();
|
||||
geometry.updateGeometricState();
|
||||
outDefines(def("DIFFUSEMAP", VarType.Texture2D, tex1));
|
||||
outUniforms(uniform("DiffuseMap", VarType.Int, 0));
|
||||
outTextures(tex1);
|
||||
}
|
||||
|
||||
private static final class Define {
|
||||
|
||||
public String name;
|
||||
public VarType type;
|
||||
public Object value;
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hash = 3;
|
||||
hash = 89 * hash + this.name.hashCode();
|
||||
hash = 89 * hash + this.type.hashCode();
|
||||
hash = 89 * hash + this.value.hashCode();
|
||||
return hash;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
final Define other = (Define) obj;
|
||||
return this.name.equals(other.name) && this.type.equals(other.type) && this.value.equals(other.value);
|
||||
}
|
||||
}
|
||||
|
||||
private final Geometry geometry = new Geometry("geometry", new Box(1, 1, 1));
|
||||
private final LightList lightList = new LightList(geometry);
|
||||
|
||||
private final NullRenderer renderer = new NullRenderer() {
|
||||
@Override
|
||||
public void setShader(Shader shader) {
|
||||
MaterialMatParamOverrideTest.this.usedShader = shader;
|
||||
evaluated = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTexture(int unit, Texture texture) {
|
||||
MaterialMatParamOverrideTest.this.usedTextures[unit] = texture;
|
||||
}
|
||||
};
|
||||
private final RenderManager renderManager = new RenderManager(renderer);
|
||||
|
||||
private boolean evaluated = false;
|
||||
private Shader usedShader = null;
|
||||
private final Texture[] usedTextures = new Texture[32];
|
||||
|
||||
private void inputMp(MatParam... params) {
|
||||
if (evaluated) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
Material mat = geometry.getMaterial();
|
||||
for (MatParam param : params) {
|
||||
mat.setParam(param.getName(), param.getVarType(), param.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
private void inputMpo(MatParamOverride... overrides) {
|
||||
if (evaluated) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
for (MatParamOverride override : overrides) {
|
||||
geometry.addMatParamOverride(override);
|
||||
}
|
||||
geometry.updateGeometricState();
|
||||
}
|
||||
|
||||
private void reset() {
|
||||
evaluated = false;
|
||||
usedShader = null;
|
||||
Arrays.fill(usedTextures, null);
|
||||
}
|
||||
|
||||
private Define def(String name, VarType type, Object value) {
|
||||
Define d = new Define();
|
||||
d.name = name;
|
||||
d.type = type;
|
||||
d.value = value;
|
||||
return d;
|
||||
}
|
||||
|
||||
private Uniform uniform(String name, VarType type, Object value) {
|
||||
Uniform u = new Uniform();
|
||||
u.setName("m_" + name);
|
||||
u.setValue(type, value);
|
||||
return u;
|
||||
}
|
||||
|
||||
private void material(String path) {
|
||||
AssetManager assetManager = TestUtil.createAssetManager();
|
||||
geometry.setMaterial(new Material(assetManager, path));
|
||||
}
|
||||
|
||||
private void evaluateTechniqueDef() {
|
||||
Assert.assertFalse(evaluated);
|
||||
Material mat = geometry.getMaterial();
|
||||
mat.render(geometry, lightList, renderManager);
|
||||
Assert.assertTrue(evaluated);
|
||||
}
|
||||
|
||||
private void outTextures(Texture... textures) {
|
||||
for (int i = 0; i < usedTextures.length; i++) {
|
||||
if (i < textures.length) {
|
||||
Assert.assertSame(textures[i], usedTextures[i]);
|
||||
} else {
|
||||
Assert.assertNull(usedTextures[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void outDefines(Define... expectedDefinesArray) {
|
||||
Map<String, Define> nameToDefineMap = new HashMap<String, Define>();
|
||||
for (Define define : expectedDefinesArray) {
|
||||
nameToDefineMap.put(define.name, define);
|
||||
}
|
||||
|
||||
if (!evaluated) {
|
||||
evaluateTechniqueDef();
|
||||
}
|
||||
|
||||
Material mat = geometry.getMaterial();
|
||||
Technique tech = mat.getActiveTechnique();
|
||||
TechniqueDef def = tech.getDef();
|
||||
DefineList actualDefines = tech.getDynamicDefines();
|
||||
|
||||
String[] defineNames = def.getDefineNames();
|
||||
VarType[] defineTypes = def.getDefineTypes();
|
||||
|
||||
Assert.assertEquals(defineNames.length, defineTypes.length);
|
||||
|
||||
for (int index = 0; index < defineNames.length; index++) {
|
||||
String name = defineNames[index];
|
||||
VarType type = defineTypes[index];
|
||||
Define expectedDefine = nameToDefineMap.remove(name);
|
||||
Object expectedValue = null;
|
||||
|
||||
if (expectedDefine != null) {
|
||||
Assert.assertEquals(expectedDefine.type, type);
|
||||
expectedValue = expectedDefine.value;
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case Boolean:
|
||||
if (expectedValue != null) {
|
||||
Assert.assertEquals((boolean) (Boolean) expectedValue, actualDefines.getBoolean(index));
|
||||
} else {
|
||||
Assert.assertEquals(false, actualDefines.getBoolean(index));
|
||||
}
|
||||
break;
|
||||
case Int:
|
||||
if (expectedValue != null) {
|
||||
Assert.assertEquals((int) (Integer) expectedValue, actualDefines.getInt(index));
|
||||
} else {
|
||||
Assert.assertEquals(0, actualDefines.getInt(index));
|
||||
}
|
||||
break;
|
||||
case Float:
|
||||
if (expectedValue != null) {
|
||||
Assert.assertEquals((float) (Float) expectedValue, actualDefines.getFloat(index), 0f);
|
||||
} else {
|
||||
Assert.assertEquals(0f, actualDefines.getFloat(index), 0f);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (expectedValue != null) {
|
||||
Assert.assertEquals(1, actualDefines.getInt(index));
|
||||
} else {
|
||||
Assert.assertEquals(0, actualDefines.getInt(index));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Assert.assertTrue(nameToDefineMap.isEmpty());
|
||||
}
|
||||
|
||||
private void outUniforms(Uniform... uniforms) {
|
||||
HashSet<Uniform> actualUniforms = new HashSet<>();
|
||||
|
||||
for (Uniform uniform : usedShader.getUniformMap().values()) {
|
||||
if (uniform.getName().startsWith("m_")
|
||||
&& !IGNORED_UNIFORMS.contains(uniform.getName())) {
|
||||
actualUniforms.add(uniform);
|
||||
}
|
||||
}
|
||||
|
||||
HashSet<Uniform> expectedUniforms = new HashSet<>(Arrays.asList(uniforms));
|
||||
|
||||
if (!expectedUniforms.equals(actualUniforms)) {
|
||||
Assert.fail("Uniform lists must match: " + expectedUniforms + " != " + actualUniforms);
|
||||
}
|
||||
}
|
||||
}
|
@ -33,6 +33,9 @@ package com.jme3.math;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import org.junit.Ignore;
|
||||
|
||||
/**
|
||||
* Verifies that algorithms in {@link FastMath} are working correctly.
|
||||
*
|
||||
@ -56,4 +59,39 @@ public class FastMathTest {
|
||||
assert nextPowerOf2 == nearestPowerOfTwoSlow(i);
|
||||
}
|
||||
}
|
||||
|
||||
private static int fastCounterClockwise(Vector2f p0, Vector2f p1, Vector2f p2) {
|
||||
float result = (p1.x - p0.x) * (p2.y - p1.y) - (p1.y - p0.y) * (p2.x - p1.x);
|
||||
return (int) Math.signum(result);
|
||||
}
|
||||
|
||||
private static Vector2f randomVector() {
|
||||
return new Vector2f(FastMath.nextRandomFloat(),
|
||||
FastMath.nextRandomFloat());
|
||||
}
|
||||
|
||||
@Ignore
|
||||
@Test
|
||||
public void testCounterClockwise() {
|
||||
for (int i = 0; i < 100; i++) {
|
||||
Vector2f p0 = randomVector();
|
||||
Vector2f p1 = randomVector();
|
||||
Vector2f p2 = randomVector();
|
||||
|
||||
int fastResult = fastCounterClockwise(p0, p1, p2);
|
||||
int slowResult = FastMath.counterClockwise(p0, p1, p2);
|
||||
|
||||
assert fastResult == slowResult;
|
||||
}
|
||||
|
||||
// duplicate test
|
||||
Vector2f p0 = new Vector2f(0,0);
|
||||
Vector2f p1 = new Vector2f(0,0);
|
||||
Vector2f p2 = new Vector2f(0,1);
|
||||
|
||||
int fastResult = fastCounterClockwise(p0, p1, p2);
|
||||
int slowResult = FastMath.counterClockwise(p0, p1, p2);
|
||||
|
||||
assertEquals(slowResult, fastResult);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,342 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2015 jMonkeyEngine
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package com.jme3.renderer;
|
||||
|
||||
import com.jme3.asset.AssetManager;
|
||||
import com.jme3.material.Material;
|
||||
import com.jme3.math.ColorRGBA;
|
||||
import com.jme3.renderer.queue.GeometryList;
|
||||
import com.jme3.renderer.queue.OpaqueComparator;
|
||||
import com.jme3.scene.Geometry;
|
||||
import com.jme3.scene.Mesh;
|
||||
import com.jme3.scene.shape.Box;
|
||||
import com.jme3.system.TestUtil;
|
||||
import com.jme3.texture.Image;
|
||||
import com.jme3.texture.Image.Format;
|
||||
import com.jme3.texture.Texture;
|
||||
import com.jme3.texture.Texture2D;
|
||||
import com.jme3.texture.image.ColorSpace;
|
||||
import com.jme3.util.BufferUtils;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import org.junit.Before;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
public class OpaqueComparatorTest {
|
||||
|
||||
private final Mesh mesh = new Box(1,1,1);
|
||||
private Camera cam = new Camera(1, 1);
|
||||
private RenderManager renderManager;
|
||||
private AssetManager assetManager;
|
||||
private OpaqueComparator comparator = new OpaqueComparator();
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
assetManager = TestUtil.createAssetManager();
|
||||
renderManager = TestUtil.createRenderManager();
|
||||
comparator.setCamera(cam);
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a correctly sorted list of materials, check if the
|
||||
* opaque comparator can sort a reversed list of them.
|
||||
*
|
||||
* Each material will be cloned so that none of them will be equal to each other
|
||||
* in reference, forcing the comparator to compare the material sort ID.
|
||||
*
|
||||
* E.g. for a list of materials A, B, C, the following list will be generated:
|
||||
* <pre>C, B, A, C, B, A, C, B, A</pre>, it should result in
|
||||
* <pre>A, A, A, B, B, B, C, C, C</pre>.
|
||||
*
|
||||
* @param materials The pre-sorted list of materials to check sorting for.
|
||||
*/
|
||||
private void testSort(Material ... materials) {
|
||||
GeometryList gl = new GeometryList(comparator);
|
||||
for (int g = 0; g < 5; g++) {
|
||||
for (int i = materials.length - 1; i >= 0; i--) {
|
||||
Geometry geom = new Geometry("geom", mesh);
|
||||
Material clonedMaterial = materials[i].clone();
|
||||
|
||||
if (materials[i].getActiveTechnique() != null) {
|
||||
String techniqueName = materials[i].getActiveTechnique().getDef().getName();
|
||||
clonedMaterial.selectTechnique(techniqueName, renderManager);
|
||||
} else {
|
||||
clonedMaterial.selectTechnique("Default", renderManager);
|
||||
}
|
||||
|
||||
geom.setMaterial(clonedMaterial);
|
||||
gl.add(geom);
|
||||
}
|
||||
}
|
||||
gl.sort();
|
||||
|
||||
for (int i = 0; i < gl.size(); i++) {
|
||||
Material mat = gl.get(i).getMaterial();
|
||||
String sortId = Integer.toHexString(mat.getSortId()).toUpperCase();
|
||||
System.out.print(sortId + "\t");
|
||||
System.out.println(mat);
|
||||
}
|
||||
|
||||
Set<String> alreadySeen = new HashSet<String>();
|
||||
Material current = null;
|
||||
for (int i = 0; i < gl.size(); i++) {
|
||||
Material mat = gl.get(i).getMaterial();
|
||||
if (current == null) {
|
||||
current = mat;
|
||||
} else if (!current.getName().equals(mat.getName())) {
|
||||
assert !alreadySeen.contains(mat.getName());
|
||||
alreadySeen.add(current.getName());
|
||||
current = mat;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < materials.length; i++) {
|
||||
for (int g = 0; g < 5; g++) {
|
||||
int index = i * 5 + g;
|
||||
Material mat1 = gl.get(index).getMaterial();
|
||||
Material mat2 = materials[i];
|
||||
assert mat1.getName().equals(mat2.getName()) :
|
||||
mat1.getName() + " != " + mat2.getName() + " for index " + index;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSortByMaterialDef() {
|
||||
Material lightingMat = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md");
|
||||
Material particleMat = new Material(assetManager, "Common/MatDefs/Misc/Particle.j3md");
|
||||
Material unshadedMat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
|
||||
Material skyMat = new Material(assetManager, "Common/MatDefs/Misc/Sky.j3md");
|
||||
|
||||
lightingMat.setName("MatLight");
|
||||
particleMat.setName("MatParticle");
|
||||
unshadedMat.setName("MatUnshaded");
|
||||
skyMat.setName("MatSky");
|
||||
testSort(skyMat, lightingMat, particleMat, unshadedMat);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSortByTechnique() {
|
||||
Material lightingMatDefault = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md");
|
||||
Material lightingPreShadow = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md");
|
||||
Material lightingPostShadow = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md");
|
||||
Material lightingMatPreNormalPass = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md");
|
||||
Material lightingMatGBuf = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md");
|
||||
Material lightingMatGlow = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md");
|
||||
|
||||
lightingMatDefault.setName("TechDefault");
|
||||
lightingMatDefault.selectTechnique("Default", renderManager);
|
||||
|
||||
lightingPostShadow.setName("TechPostShad");
|
||||
lightingPostShadow.selectTechnique("PostShadow", renderManager);
|
||||
|
||||
lightingPreShadow.setName("TechPreShad");
|
||||
lightingPreShadow.selectTechnique("PreShadow", renderManager);
|
||||
|
||||
lightingMatPreNormalPass.setName("TechNorm");
|
||||
lightingMatPreNormalPass.selectTechnique("PreNormalPass", renderManager);
|
||||
|
||||
lightingMatGBuf.setName("TechGBuf");
|
||||
lightingMatGBuf.selectTechnique("GBuf", renderManager);
|
||||
|
||||
lightingMatGlow.setName("TechGlow");
|
||||
lightingMatGlow.selectTechnique("Glow", renderManager);
|
||||
|
||||
testSort(lightingMatGlow, lightingPreShadow, lightingMatPreNormalPass,
|
||||
lightingMatDefault, lightingPostShadow, lightingMatGBuf);
|
||||
}
|
||||
|
||||
@Test(expected = AssertionError.class)
|
||||
public void testNoSortByParam() {
|
||||
Material sameMat1 = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
|
||||
Material sameMat2 = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
|
||||
|
||||
sameMat1.setName("MatRed");
|
||||
sameMat1.setColor("Color", ColorRGBA.Red);
|
||||
|
||||
sameMat2.setName("MatBlue");
|
||||
sameMat2.setColor("Color", ColorRGBA.Blue);
|
||||
|
||||
testSort(sameMat1, sameMat2);
|
||||
}
|
||||
|
||||
private Texture createTexture(String name) {
|
||||
ByteBuffer bb = BufferUtils.createByteBuffer(3);
|
||||
Image image = new Image(Format.RGB8, 1, 1, bb, ColorSpace.sRGB);
|
||||
Texture2D texture = new Texture2D(image);
|
||||
texture.setName(name);
|
||||
return texture;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSortByTexture() {
|
||||
Material texture1Mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
|
||||
Material texture2Mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
|
||||
Material texture3Mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
|
||||
|
||||
Texture tex1 = createTexture("A");
|
||||
tex1.getImage().setId(1);
|
||||
|
||||
Texture tex2 = createTexture("B");
|
||||
tex2.getImage().setId(2);
|
||||
|
||||
Texture tex3 = createTexture("C");
|
||||
tex3.getImage().setId(3);
|
||||
|
||||
texture1Mat.setName("TexA");
|
||||
texture1Mat.setTexture("ColorMap", tex1);
|
||||
|
||||
texture2Mat.setName("TexB");
|
||||
texture2Mat.setTexture("ColorMap", tex2);
|
||||
|
||||
texture3Mat.setName("TexC");
|
||||
texture3Mat.setTexture("ColorMap", tex3);
|
||||
|
||||
testSort(texture1Mat, texture2Mat, texture3Mat);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSortByShaderDefines() {
|
||||
Material lightingMat = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md");
|
||||
Material lightingMatVColor = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md");
|
||||
Material lightingMatVLight = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md");
|
||||
Material lightingMatTC = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md");
|
||||
Material lightingMatVColorLight = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md");
|
||||
Material lightingMatTCVColorLight = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md");
|
||||
|
||||
lightingMat.setName("DefNone");
|
||||
|
||||
lightingMatVColor.setName("DefVC");
|
||||
lightingMatVColor.setBoolean("UseVertexColor", true);
|
||||
|
||||
lightingMatVLight.setName("DefVL");
|
||||
lightingMatVLight.setBoolean("VertexLighting", true);
|
||||
|
||||
lightingMatTC.setName("DefTC");
|
||||
lightingMatTC.setBoolean("SeparateTexCoord", true);
|
||||
|
||||
lightingMatVColorLight.setName("DefVCVL");
|
||||
lightingMatVColorLight.setBoolean("UseVertexColor", true);
|
||||
lightingMatVColorLight.setBoolean("VertexLighting", true);
|
||||
|
||||
lightingMatTCVColorLight.setName("DefVCVLTC");
|
||||
lightingMatTCVColorLight.setBoolean("UseVertexColor", true);
|
||||
lightingMatTCVColorLight.setBoolean("VertexLighting", true);
|
||||
lightingMatTCVColorLight.setBoolean("SeparateTexCoord", true);
|
||||
|
||||
testSort(lightingMat, lightingMatVColor, lightingMatVLight,
|
||||
lightingMatVColorLight, lightingMatTC, lightingMatTCVColorLight);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSortByAll() {
|
||||
Material matBase1 = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md");
|
||||
Material matBase2 = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
|
||||
|
||||
Texture texBase = createTexture("BASE");
|
||||
texBase.getImage().setId(1);
|
||||
Texture tex1 = createTexture("1");
|
||||
tex1.getImage().setId(2);
|
||||
Texture tex2 = createTexture("2");
|
||||
tex2.getImage().setId(3);
|
||||
|
||||
matBase1.setName("BASE");
|
||||
matBase1.selectTechnique("Default", renderManager);
|
||||
matBase1.setBoolean("UseVertexColor", true);
|
||||
matBase1.setTexture("DiffuseMap", texBase);
|
||||
|
||||
Material mat1100 = matBase1.clone();
|
||||
mat1100.setName("1100");
|
||||
mat1100.selectTechnique("PreShadow", renderManager);
|
||||
|
||||
Material mat1101 = matBase1.clone();
|
||||
mat1101.setName("1101");
|
||||
mat1101.selectTechnique("PreShadow", renderManager);
|
||||
mat1101.setTexture("DiffuseMap", tex1);
|
||||
|
||||
Material mat1102 = matBase1.clone();
|
||||
mat1102.setName("1102");
|
||||
mat1102.selectTechnique("PreShadow", renderManager);
|
||||
mat1102.setTexture("DiffuseMap", tex2);
|
||||
|
||||
Material mat1110 = matBase1.clone();
|
||||
mat1110.setName("1110");
|
||||
mat1110.selectTechnique("PreShadow", renderManager);
|
||||
mat1110.setFloat("AlphaDiscardThreshold", 2f);
|
||||
|
||||
Material mat1120 = matBase1.clone();
|
||||
mat1120.setName("1120");
|
||||
mat1120.selectTechnique("PreShadow", renderManager);
|
||||
mat1120.setBoolean("UseInstancing", true);
|
||||
|
||||
Material mat1121 = matBase1.clone();
|
||||
mat1121.setName("1121");
|
||||
mat1121.selectTechnique("PreShadow", renderManager);
|
||||
mat1121.setBoolean("UseInstancing", true);
|
||||
mat1121.setTexture("DiffuseMap", tex1);
|
||||
|
||||
Material mat1122 = matBase1.clone();
|
||||
mat1122.setName("1122");
|
||||
mat1122.selectTechnique("PreShadow", renderManager);
|
||||
mat1122.setBoolean("UseInstancing", true);
|
||||
mat1122.setTexture("DiffuseMap", tex2);
|
||||
|
||||
Material mat1140 = matBase1.clone();
|
||||
mat1140.setName("1140");
|
||||
mat1140.selectTechnique("PreShadow", renderManager);
|
||||
mat1140.setFloat("AlphaDiscardThreshold", 2f);
|
||||
mat1140.setBoolean("UseInstancing", true);
|
||||
|
||||
Material mat1200 = matBase1.clone();
|
||||
mat1200.setName("1200");
|
||||
mat1200.selectTechnique("PostShadow", renderManager);
|
||||
|
||||
Material mat1210 = matBase1.clone();
|
||||
mat1210.setName("1210");
|
||||
mat1210.selectTechnique("PostShadow", renderManager);
|
||||
mat1210.setFloat("AlphaDiscardThreshold", 2f);
|
||||
|
||||
Material mat1220 = matBase1.clone();
|
||||
mat1220.setName("1220");
|
||||
mat1220.selectTechnique("PostShadow", renderManager);
|
||||
mat1220.setBoolean("UseInstancing", true);
|
||||
|
||||
Material mat2000 = matBase2.clone();
|
||||
mat2000.setName("2000");
|
||||
|
||||
testSort(mat1100, mat1101, mat1102, mat1110,
|
||||
mat1120, mat1121, mat1122, mat1140,
|
||||
mat1200, mat1210, mat1220, mat2000);
|
||||
}
|
||||
}
|
173
jme3-core/src/test/java/com/jme3/scene/MPOTestUtils.java
Normal file
173
jme3-core/src/test/java/com/jme3/scene/MPOTestUtils.java
Normal file
@ -0,0 +1,173 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2016 jMonkeyEngine
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package com.jme3.scene;
|
||||
|
||||
import com.jme3.material.MatParamOverride;
|
||||
import com.jme3.math.Matrix4f;
|
||||
import com.jme3.renderer.Camera;
|
||||
import com.jme3.shader.VarType;
|
||||
import com.jme3.texture.Texture2D;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
public class MPOTestUtils {
|
||||
|
||||
private static final Camera DUMMY_CAM = new Camera(640, 480);
|
||||
|
||||
private static final SceneGraphVisitor VISITOR = new SceneGraphVisitor() {
|
||||
@Override
|
||||
public void visit(Spatial spatial) {
|
||||
validateSubScene(spatial);
|
||||
}
|
||||
};
|
||||
|
||||
private static void validateSubScene(Spatial scene) {
|
||||
scene.checkCulling(DUMMY_CAM);
|
||||
|
||||
Set<MatParamOverride> actualOverrides = new HashSet<MatParamOverride>();
|
||||
for (MatParamOverride override : scene.getWorldMatParamOverrides()) {
|
||||
actualOverrides.add(override);
|
||||
}
|
||||
|
||||
Set<MatParamOverride> expectedOverrides = new HashSet<MatParamOverride>();
|
||||
Spatial current = scene;
|
||||
while (current != null) {
|
||||
for (MatParamOverride override : current.getLocalMatParamOverrides()) {
|
||||
expectedOverrides.add(override);
|
||||
}
|
||||
current = current.getParent();
|
||||
}
|
||||
|
||||
assertEquals("For " + scene, expectedOverrides, actualOverrides);
|
||||
}
|
||||
|
||||
public static void validateScene(Spatial scene) {
|
||||
scene.updateGeometricState();
|
||||
scene.depthFirstTraversal(VISITOR);
|
||||
}
|
||||
|
||||
public static MatParamOverride mpoInt(String name, int value) {
|
||||
return new MatParamOverride(VarType.Int, name, value);
|
||||
}
|
||||
|
||||
public static MatParamOverride mpoBool(String name, boolean value) {
|
||||
return new MatParamOverride(VarType.Boolean, name, value);
|
||||
}
|
||||
|
||||
public static MatParamOverride mpoFloat(String name, float value) {
|
||||
return new MatParamOverride(VarType.Float, name, value);
|
||||
}
|
||||
|
||||
public static MatParamOverride mpoMatrix4Array(String name, Matrix4f[] value) {
|
||||
return new MatParamOverride(VarType.Matrix4Array, name, value);
|
||||
}
|
||||
|
||||
public static MatParamOverride mpoTexture2D(String name, Texture2D texture) {
|
||||
return new MatParamOverride(VarType.Texture2D, name, texture);
|
||||
}
|
||||
|
||||
private static int getRefreshFlags(Spatial scene) {
|
||||
try {
|
||||
Field refreshFlagsField = Spatial.class.getDeclaredField("refreshFlags");
|
||||
refreshFlagsField.setAccessible(true);
|
||||
return (Integer) refreshFlagsField.get(scene);
|
||||
} catch (NoSuchFieldException ex) {
|
||||
throw new AssertionError(ex);
|
||||
} catch (SecurityException ex) {
|
||||
throw new AssertionError(ex);
|
||||
} catch (IllegalArgumentException ex) {
|
||||
throw new AssertionError(ex);
|
||||
} catch (IllegalAccessException ex) {
|
||||
throw new AssertionError(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private static void dumpSceneRF(Spatial scene, String indent, boolean last, int refreshFlagsMask) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
sb.append(indent);
|
||||
if (last) {
|
||||
if (!indent.isEmpty()) {
|
||||
sb.append("└─");
|
||||
} else {
|
||||
sb.append(" ");
|
||||
}
|
||||
indent += " ";
|
||||
} else {
|
||||
sb.append("├─");
|
||||
indent += "│ ";
|
||||
}
|
||||
sb.append(scene.getName());
|
||||
int rf = getRefreshFlags(scene) & refreshFlagsMask;
|
||||
if (rf != 0) {
|
||||
sb.append("(");
|
||||
if ((rf & 0x1) != 0) {
|
||||
sb.append("T");
|
||||
}
|
||||
if ((rf & 0x2) != 0) {
|
||||
sb.append("B");
|
||||
}
|
||||
if ((rf & 0x4) != 0) {
|
||||
sb.append("L");
|
||||
}
|
||||
if ((rf & 0x8) != 0) {
|
||||
sb.append("l");
|
||||
}
|
||||
if ((rf & 0x10) != 0) {
|
||||
sb.append("O");
|
||||
}
|
||||
sb.append(")");
|
||||
}
|
||||
|
||||
if (!scene.getLocalMatParamOverrides().isEmpty()) {
|
||||
sb.append(" [MPO]");
|
||||
}
|
||||
|
||||
System.out.println(sb);
|
||||
|
||||
if (scene instanceof Node) {
|
||||
Node node = (Node) scene;
|
||||
int childIndex = 0;
|
||||
for (Spatial child : node.getChildren()) {
|
||||
boolean childLast = childIndex == node.getQuantity() - 1;
|
||||
dumpSceneRF(child, indent, childLast, refreshFlagsMask);
|
||||
childIndex++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void dumpSceneRF(Spatial scene, int refreshFlagsMask) {
|
||||
dumpSceneRF(scene, "", true, refreshFlagsMask);
|
||||
}
|
||||
}
|
@ -0,0 +1,278 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2016 jMonkeyEngine
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package com.jme3.scene;
|
||||
|
||||
import com.jme3.asset.AssetManager;
|
||||
import com.jme3.export.binary.BinaryExporter;
|
||||
import com.jme3.material.MatParamOverride;
|
||||
import org.junit.Test;
|
||||
|
||||
import static com.jme3.scene.MPOTestUtils.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import com.jme3.system.TestUtil;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Validates how {@link MatParamOverride MPOs} work on the scene level.
|
||||
*
|
||||
* @author Kirill Vainer
|
||||
*/
|
||||
public class SceneMatParamOverrideTest {
|
||||
|
||||
private static Node createDummyScene() {
|
||||
Node scene = new Node("Scene Node");
|
||||
|
||||
Node a = new Node("A");
|
||||
Node b = new Node("B");
|
||||
|
||||
Node c = new Node("C");
|
||||
Node d = new Node("D");
|
||||
|
||||
Node e = new Node("E");
|
||||
Node f = new Node("F");
|
||||
|
||||
Node g = new Node("G");
|
||||
Node h = new Node("H");
|
||||
Node j = new Node("J");
|
||||
|
||||
scene.attachChild(a);
|
||||
scene.attachChild(b);
|
||||
|
||||
a.attachChild(c);
|
||||
a.attachChild(d);
|
||||
|
||||
b.attachChild(e);
|
||||
b.attachChild(f);
|
||||
|
||||
c.attachChild(g);
|
||||
c.attachChild(h);
|
||||
c.attachChild(j);
|
||||
|
||||
return scene;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOverrides_Empty() {
|
||||
Node n = new Node("Node");
|
||||
assertTrue(n.getLocalMatParamOverrides().isEmpty());
|
||||
assertTrue(n.getWorldMatParamOverrides().isEmpty());
|
||||
|
||||
n.updateGeometricState();
|
||||
assertTrue(n.getLocalMatParamOverrides().isEmpty());
|
||||
assertTrue(n.getWorldMatParamOverrides().isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOverrides_AddRemove() {
|
||||
MatParamOverride override = mpoBool("Test", true);
|
||||
Node n = new Node("Node");
|
||||
|
||||
n.removeMatParamOverride(override);
|
||||
assertTrue(n.getLocalMatParamOverrides().isEmpty());
|
||||
assertTrue(n.getWorldMatParamOverrides().isEmpty());
|
||||
|
||||
n.addMatParamOverride(override);
|
||||
|
||||
assertSame(n.getLocalMatParamOverrides().get(0), override);
|
||||
assertTrue(n.getWorldMatParamOverrides().isEmpty());
|
||||
n.updateGeometricState();
|
||||
|
||||
assertSame(n.getLocalMatParamOverrides().get(0), override);
|
||||
assertSame(n.getWorldMatParamOverrides().get(0), override);
|
||||
|
||||
n.removeMatParamOverride(override);
|
||||
assertTrue(n.getLocalMatParamOverrides().isEmpty());
|
||||
assertSame(n.getWorldMatParamOverrides().get(0), override);
|
||||
|
||||
n.updateGeometricState();
|
||||
assertTrue(n.getLocalMatParamOverrides().isEmpty());
|
||||
assertTrue(n.getWorldMatParamOverrides().isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOverrides_Clear() {
|
||||
MatParamOverride override = mpoBool("Test", true);
|
||||
Node n = new Node("Node");
|
||||
|
||||
n.clearMatParamOverrides();
|
||||
assertTrue(n.getLocalMatParamOverrides().isEmpty());
|
||||
assertTrue(n.getWorldMatParamOverrides().isEmpty());
|
||||
|
||||
n.addMatParamOverride(override);
|
||||
n.clearMatParamOverrides();
|
||||
assertTrue(n.getLocalMatParamOverrides().isEmpty());
|
||||
assertTrue(n.getWorldMatParamOverrides().isEmpty());
|
||||
|
||||
n.addMatParamOverride(override);
|
||||
n.updateGeometricState();
|
||||
n.clearMatParamOverrides();
|
||||
assertTrue(n.getLocalMatParamOverrides().isEmpty());
|
||||
assertSame(n.getWorldMatParamOverrides().get(0), override);
|
||||
|
||||
n.updateGeometricState();
|
||||
assertTrue(n.getLocalMatParamOverrides().isEmpty());
|
||||
assertTrue(n.getWorldMatParamOverrides().isEmpty());
|
||||
|
||||
n.addMatParamOverride(override);
|
||||
n.clearMatParamOverrides();
|
||||
n.updateGeometricState();
|
||||
assertTrue(n.getLocalMatParamOverrides().isEmpty());
|
||||
assertTrue(n.getWorldMatParamOverrides().isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOverrides_AddAfterAttach() {
|
||||
Node scene = createDummyScene();
|
||||
scene.updateGeometricState();
|
||||
|
||||
Node root = new Node("Root Node");
|
||||
root.updateGeometricState();
|
||||
|
||||
root.attachChild(scene);
|
||||
scene.getChild("A").addMatParamOverride(mpoInt("val", 5));
|
||||
|
||||
validateScene(root);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOverrides_AddBeforeAttach() {
|
||||
Node scene = createDummyScene();
|
||||
scene.getChild("A").addMatParamOverride(mpoInt("val", 5));
|
||||
scene.updateGeometricState();
|
||||
|
||||
Node root = new Node("Root Node");
|
||||
root.updateGeometricState();
|
||||
|
||||
root.attachChild(scene);
|
||||
|
||||
validateScene(root);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOverrides_RemoveBeforeAttach() {
|
||||
Node scene = createDummyScene();
|
||||
scene.updateGeometricState();
|
||||
|
||||
Node root = new Node("Root Node");
|
||||
root.updateGeometricState();
|
||||
|
||||
scene.getChild("A").addMatParamOverride(mpoInt("val", 5));
|
||||
validateScene(scene);
|
||||
|
||||
scene.getChild("A").clearMatParamOverrides();
|
||||
validateScene(scene);
|
||||
|
||||
root.attachChild(scene);
|
||||
validateScene(root);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOverrides_RemoveAfterAttach() {
|
||||
Node scene = createDummyScene();
|
||||
scene.updateGeometricState();
|
||||
|
||||
Node root = new Node("Root Node");
|
||||
root.updateGeometricState();
|
||||
|
||||
scene.getChild("A").addMatParamOverride(mpoInt("val", 5));
|
||||
|
||||
root.attachChild(scene);
|
||||
validateScene(root);
|
||||
|
||||
scene.getChild("A").clearMatParamOverrides();
|
||||
validateScene(root);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOverrides_IdenticalNames() {
|
||||
Node scene = createDummyScene();
|
||||
|
||||
scene.getChild("A").addMatParamOverride(mpoInt("val", 5));
|
||||
scene.getChild("C").addMatParamOverride(mpoInt("val", 7));
|
||||
|
||||
validateScene(scene);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOverrides_CloningScene_DoesntCloneMPO() {
|
||||
Node originalScene = createDummyScene();
|
||||
|
||||
originalScene.getChild("A").addMatParamOverride(mpoInt("int", 5));
|
||||
originalScene.getChild("A").addMatParamOverride(mpoBool("bool", true));
|
||||
originalScene.getChild("A").addMatParamOverride(mpoFloat("float", 3.12f));
|
||||
|
||||
Node clonedScene = originalScene.clone(false);
|
||||
|
||||
validateScene(clonedScene);
|
||||
validateScene(originalScene);
|
||||
|
||||
List<MatParamOverride> clonedOverrides = clonedScene.getChild("A").getLocalMatParamOverrides();
|
||||
List<MatParamOverride> originalOverrides = originalScene.getChild("A").getLocalMatParamOverrides();
|
||||
|
||||
assertNotSame(clonedOverrides, originalOverrides);
|
||||
assertEquals(clonedOverrides, originalOverrides);
|
||||
|
||||
for (int i = 0; i < clonedOverrides.size(); i++) {
|
||||
assertNotSame(clonedOverrides.get(i), originalOverrides.get(i));
|
||||
assertEquals(clonedOverrides.get(i), originalOverrides.get(i));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOverrides_SaveAndLoad_KeepsMPOs() {
|
||||
MatParamOverride override = mpoInt("val", 5);
|
||||
Node scene = createDummyScene();
|
||||
scene.getChild("A").addMatParamOverride(override);
|
||||
|
||||
AssetManager assetManager = TestUtil.createAssetManager();
|
||||
Node loadedScene = BinaryExporter.saveAndLoad(assetManager, scene);
|
||||
|
||||
Node root = new Node("Root Node");
|
||||
root.attachChild(loadedScene);
|
||||
validateScene(root);
|
||||
validateScene(scene);
|
||||
|
||||
assertNotSame(override, loadedScene.getChild("A").getLocalMatParamOverrides().get(0));
|
||||
assertEquals(override, loadedScene.getChild("A").getLocalMatParamOverrides().get(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEquals() {
|
||||
assertEquals(mpoInt("val", 5), mpoInt("val", 5));
|
||||
assertEquals(mpoBool("val", true), mpoBool("val", true));
|
||||
assertNotEquals(mpoInt("val", 5), mpoInt("val", 6));
|
||||
assertNotEquals(mpoInt("val1", 5), mpoInt("val2", 5));
|
||||
assertNotEquals(mpoBool("val", true), mpoInt("val", 1));
|
||||
}
|
||||
}
|
300
jme3-core/src/test/java/com/jme3/shader/DefineListTest.java
Normal file
300
jme3-core/src/test/java/com/jme3/shader/DefineListTest.java
Normal file
@ -0,0 +1,300 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2015 jMonkeyEngine
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package com.jme3.shader;
|
||||
|
||||
import com.jme3.math.FastMath;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
public class DefineListTest {
|
||||
|
||||
private static final List<String> DEFINE_NAMES = Arrays.asList("BOOL_VAR", "INT_VAR", "FLOAT_VAR");
|
||||
private static final List<VarType> DEFINE_TYPES = Arrays.asList(VarType.Boolean, VarType.Int, VarType.Float);
|
||||
private static final int NUM_DEFINES = DEFINE_NAMES.size();
|
||||
private static final int BOOL_VAR = 0;
|
||||
private static final int INT_VAR = 1;
|
||||
private static final int FLOAT_VAR = 2;
|
||||
private static final DefineList EMPTY = new DefineList(NUM_DEFINES);
|
||||
|
||||
@Test
|
||||
public void testHashCollision() {
|
||||
DefineList dl1 = new DefineList(64);
|
||||
DefineList dl2 = new DefineList(64);
|
||||
|
||||
// Try to cause a hash collision
|
||||
// (since bit #32 is aliased to bit #1 in 32-bit ints)
|
||||
dl1.set(0, 123);
|
||||
dl1.set(32, 0);
|
||||
|
||||
dl2.set(32, 0);
|
||||
dl2.set(0, 123);
|
||||
|
||||
assert dl1.hashCode() == dl2.hashCode();
|
||||
assert dl1.equals(dl2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetSet() {
|
||||
DefineList dl = new DefineList(NUM_DEFINES);
|
||||
|
||||
assertFalse(dl.getBoolean(BOOL_VAR));
|
||||
assertEquals(dl.getInt(INT_VAR), 0);
|
||||
assertEquals(dl.getFloat(FLOAT_VAR), 0f, 0f);
|
||||
|
||||
dl.set(BOOL_VAR, true);
|
||||
dl.set(INT_VAR, -1);
|
||||
dl.set(FLOAT_VAR, Float.NaN);
|
||||
|
||||
assertTrue(dl.getBoolean(BOOL_VAR));
|
||||
assertEquals(dl.getInt(INT_VAR), -1);
|
||||
assertTrue(Float.isNaN(dl.getFloat(FLOAT_VAR)));
|
||||
}
|
||||
|
||||
private String generateSource(DefineList dl) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
dl.generateSource(sb, DEFINE_NAMES, DEFINE_TYPES);
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSourceInitial() {
|
||||
DefineList dl = new DefineList(NUM_DEFINES);
|
||||
assert dl.hashCode() == 0;
|
||||
assert generateSource(dl).equals("");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSourceBooleanDefine() {
|
||||
DefineList dl = new DefineList(NUM_DEFINES);
|
||||
|
||||
dl.set(BOOL_VAR, true);
|
||||
assert dl.hashCode() == 1;
|
||||
assert generateSource(dl).equals("#define BOOL_VAR 1\n");
|
||||
|
||||
dl.set(BOOL_VAR, false);
|
||||
assert dl.hashCode() == 0;
|
||||
assert generateSource(dl).equals("");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSourceIntDefine() {
|
||||
DefineList dl = new DefineList(NUM_DEFINES);
|
||||
|
||||
int hashCodeWithInt = 1 << INT_VAR;
|
||||
|
||||
dl.set(INT_VAR, 123);
|
||||
assert dl.hashCode() == hashCodeWithInt;
|
||||
assert generateSource(dl).equals("#define INT_VAR 123\n");
|
||||
|
||||
dl.set(INT_VAR, 0);
|
||||
assert dl.hashCode() == 0;
|
||||
assert generateSource(dl).equals("");
|
||||
|
||||
dl.set(INT_VAR, -99);
|
||||
assert dl.hashCode() == hashCodeWithInt;
|
||||
assert generateSource(dl).equals("#define INT_VAR -99\n");
|
||||
|
||||
dl.set(INT_VAR, Integer.MAX_VALUE);
|
||||
assert dl.hashCode() == hashCodeWithInt;
|
||||
assert generateSource(dl).equals("#define INT_VAR 2147483647\n");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSourceFloatDefine() {
|
||||
DefineList dl = new DefineList(NUM_DEFINES);
|
||||
|
||||
dl.set(FLOAT_VAR, 1f);
|
||||
assert dl.hashCode() == (1 << FLOAT_VAR);
|
||||
assert generateSource(dl).equals("#define FLOAT_VAR 1.0\n");
|
||||
|
||||
dl.set(FLOAT_VAR, 0f);
|
||||
assert dl.hashCode() == 0;
|
||||
assert generateSource(dl).equals("");
|
||||
|
||||
dl.set(FLOAT_VAR, -1f);
|
||||
assert generateSource(dl).equals("#define FLOAT_VAR -1.0\n");
|
||||
|
||||
dl.set(FLOAT_VAR, FastMath.FLT_EPSILON);
|
||||
assert generateSource(dl).equals("#define FLOAT_VAR 1.1920929E-7\n");
|
||||
|
||||
dl.set(FLOAT_VAR, FastMath.PI);
|
||||
assert generateSource(dl).equals("#define FLOAT_VAR 3.1415927\n");
|
||||
|
||||
try {
|
||||
dl.set(FLOAT_VAR, Float.NaN);
|
||||
generateSource(dl);
|
||||
assert false;
|
||||
} catch (IllegalArgumentException ex) { }
|
||||
|
||||
try {
|
||||
dl.set(FLOAT_VAR, Float.POSITIVE_INFINITY);
|
||||
generateSource(dl);
|
||||
assert false;
|
||||
} catch (IllegalArgumentException ex) { }
|
||||
|
||||
try {
|
||||
dl.set(FLOAT_VAR, Float.NEGATIVE_INFINITY);
|
||||
generateSource(dl);
|
||||
assert false;
|
||||
} catch (IllegalArgumentException ex) { }
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEqualsAndHashCode() {
|
||||
DefineList dl1 = new DefineList(NUM_DEFINES);
|
||||
DefineList dl2 = new DefineList(NUM_DEFINES);
|
||||
|
||||
assertTrue(dl1.hashCode() == 0);
|
||||
assertEquals(dl1, dl2);
|
||||
|
||||
dl1.set(BOOL_VAR, true);
|
||||
|
||||
assertTrue(dl1.hashCode() == 1);
|
||||
assertNotSame(dl1, dl2);
|
||||
|
||||
dl2.set(BOOL_VAR, true);
|
||||
|
||||
assertEquals(dl1, dl2);
|
||||
|
||||
dl1.set(INT_VAR, 2);
|
||||
|
||||
assertTrue(dl1.hashCode() == (1|2));
|
||||
assertNotSame(dl1, dl2);
|
||||
|
||||
dl2.set(INT_VAR, 2);
|
||||
|
||||
assertEquals(dl1, dl2);
|
||||
|
||||
dl1.set(BOOL_VAR, false);
|
||||
|
||||
assertTrue(dl1.hashCode() == 2);
|
||||
assertNotSame(dl1, dl2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeepClone() {
|
||||
DefineList dl1 = new DefineList(NUM_DEFINES);
|
||||
DefineList dl2 = dl1.deepClone();
|
||||
|
||||
assertFalse(dl1 == dl2);
|
||||
assertTrue(dl1.equals(dl2));
|
||||
assertTrue(dl1.hashCode() == dl2.hashCode());
|
||||
|
||||
dl1.set(BOOL_VAR, true);
|
||||
dl2 = dl1.deepClone();
|
||||
|
||||
assertTrue(dl1.equals(dl2));
|
||||
assertTrue(dl1.hashCode() == dl2.hashCode());
|
||||
|
||||
dl1.set(INT_VAR, 123);
|
||||
|
||||
assertFalse(dl1.equals(dl2));
|
||||
assertFalse(dl1.hashCode() == dl2.hashCode());
|
||||
|
||||
dl2 = dl1.deepClone();
|
||||
|
||||
assertTrue(dl1.equals(dl2));
|
||||
assertTrue(dl1.hashCode() == dl2.hashCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGenerateSource() {
|
||||
DefineList dl = new DefineList(NUM_DEFINES);
|
||||
|
||||
assertEquals("", generateSource(dl));
|
||||
|
||||
dl.set(BOOL_VAR, true);
|
||||
|
||||
assertEquals("#define BOOL_VAR 1\n", generateSource(dl));
|
||||
|
||||
dl.set(INT_VAR, 123);
|
||||
|
||||
assertEquals("#define BOOL_VAR 1\n" +
|
||||
"#define INT_VAR 123\n", generateSource(dl));
|
||||
|
||||
dl.set(BOOL_VAR, false);
|
||||
|
||||
assertEquals("#define INT_VAR 123\n", generateSource(dl));
|
||||
|
||||
dl.set(BOOL_VAR, true);
|
||||
|
||||
// should have predictable ordering based on defineId
|
||||
assertEquals("#define BOOL_VAR 1\n" +
|
||||
"#define INT_VAR 123\n", generateSource(dl));
|
||||
}
|
||||
|
||||
private static String doLookup(HashMap<DefineList, String> map, boolean boolVal, int intVal, float floatVal) {
|
||||
DefineList dl = new DefineList(NUM_DEFINES);
|
||||
dl.set(BOOL_VAR, boolVal);
|
||||
dl.set(INT_VAR, intVal);
|
||||
dl.set(FLOAT_VAR, floatVal);
|
||||
return map.get(dl);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHashLookup() {
|
||||
String STR_EMPTY = "This is an empty define list";
|
||||
String STR_INT = "This define list has an int value";
|
||||
String STR_BOOL = "This define list just has boolean value set";
|
||||
String STR_BOOL_INT = "This define list has both a boolean and int value";
|
||||
String STR_BOOL_INT_FLOAT = "This define list has a boolean, int, and float value";
|
||||
|
||||
HashMap<DefineList, String> map = new HashMap<DefineList, String>();
|
||||
|
||||
DefineList lookup = new DefineList(NUM_DEFINES);
|
||||
|
||||
map.put(lookup.deepClone(), STR_EMPTY);
|
||||
|
||||
lookup.set(BOOL_VAR, true);
|
||||
map.put(lookup.deepClone(), STR_BOOL);
|
||||
|
||||
lookup.set(BOOL_VAR, false);
|
||||
lookup.set(INT_VAR, 123);
|
||||
map.put(lookup.deepClone(), STR_INT);
|
||||
|
||||
lookup.set(BOOL_VAR, true);
|
||||
map.put(lookup.deepClone(), STR_BOOL_INT);
|
||||
|
||||
lookup.set(FLOAT_VAR, FastMath.PI);
|
||||
map.put(lookup.deepClone(), STR_BOOL_INT_FLOAT);
|
||||
|
||||
assertEquals(doLookup(map, false, 0, 0f), STR_EMPTY);
|
||||
assertEquals(doLookup(map, false, 123, 0f), STR_INT);
|
||||
assertEquals(doLookup(map, true, 0, 0f), STR_BOOL);
|
||||
assertEquals(doLookup(map, true, 123, 0f), STR_BOOL_INT);
|
||||
assertEquals(doLookup(map, true, 123, FastMath.PI), STR_BOOL_INT_FLOAT);
|
||||
}
|
||||
}
|
@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2015 jMonkeyEngine
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package com.jme3.system;
|
||||
|
||||
import com.jme3.audio.AudioRenderer;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.net.URL;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
public class MockJmeSystemDelegate extends JmeSystemDelegate {
|
||||
|
||||
@Override
|
||||
public void writeImageFile(OutputStream outStream, String format, ByteBuffer imageData, int width, int height) throws IOException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showErrorDialog(String message) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean showSettingsDialog(AppSettings sourceSettings, boolean loadFromRegistry) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public URL getPlatformAssetConfigURL() {
|
||||
return Thread.currentThread().getContextClassLoader().getResource("com/jme3/asset/General.cfg");
|
||||
}
|
||||
|
||||
@Override
|
||||
public JmeContext newContext(AppSettings settings, JmeContext.Type contextType) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AudioRenderer newAudioRenderer(AppSettings settings) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize(AppSettings settings) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showSoftKeyboard(boolean show) {
|
||||
}
|
||||
|
||||
}
|
55
jme3-core/src/test/java/com/jme3/system/TestUtil.java
Normal file
55
jme3-core/src/test/java/com/jme3/system/TestUtil.java
Normal file
@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2015 jMonkeyEngine
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package com.jme3.system;
|
||||
|
||||
import com.jme3.asset.AssetConfig;
|
||||
import com.jme3.asset.AssetManager;
|
||||
import com.jme3.asset.DesktopAssetManager;
|
||||
import com.jme3.renderer.RenderManager;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
public class TestUtil {
|
||||
|
||||
static {
|
||||
JmeSystem.setSystemDelegate(new MockJmeSystemDelegate());
|
||||
}
|
||||
|
||||
public static AssetManager createAssetManager() {
|
||||
Logger.getLogger(AssetConfig.class.getName()).setLevel(Level.OFF);
|
||||
return new DesktopAssetManager(true);
|
||||
}
|
||||
|
||||
public static RenderManager createRenderManager() {
|
||||
return new RenderManager(new NullRenderer());
|
||||
}
|
||||
}
|
@ -6,11 +6,12 @@ import com.jme3.asset.plugins.FileLocator;
|
||||
import com.jme3.material.MaterialDef;
|
||||
import com.jme3.material.TechniqueDef;
|
||||
import com.jme3.material.plugins.J3MLoader;
|
||||
import com.jme3.renderer.Caps;
|
||||
import com.jme3.shader.DefineList;
|
||||
import com.jme3.shader.Shader;
|
||||
import com.jme3.shader.ShaderKey;
|
||||
import com.jme3.shader.plugins.GLSLLoader;
|
||||
import com.jme3.system.JmeSystem;
|
||||
import java.util.EnumSet;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
@ -35,21 +36,20 @@ public class ShaderCheck {
|
||||
|
||||
private static void checkMatDef(String matdefName) {
|
||||
MaterialDef def = (MaterialDef) assetManager.loadAsset(matdefName);
|
||||
EnumSet<Caps> rendererCaps = EnumSet.noneOf(Caps.class);
|
||||
rendererCaps.add(Caps.GLSL100);
|
||||
for (TechniqueDef techDef : def.getDefaultTechniques()) {
|
||||
DefineList dl = new DefineList();
|
||||
dl.addFrom(techDef.getShaderPresetDefines());
|
||||
ShaderKey shaderKey = new ShaderKey(dl,techDef.getShaderProgramLanguages(),techDef.getShaderProgramNames());
|
||||
|
||||
Shader shader = assetManager.loadShader(shaderKey);
|
||||
|
||||
DefineList defines = techDef.createDefineList();
|
||||
Shader shader = techDef.getShader(assetManager, rendererCaps, defines);
|
||||
for (Validator validator : validators) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
validator.validate(shader, sb);
|
||||
System.out.println("==== Validator: " + validator.getName() + " " +
|
||||
validator.getInstalledVersion() + " ====");
|
||||
System.out.println("==== Validator: " + validator.getName() + " "
|
||||
+ validator.getInstalledVersion() + " ====");
|
||||
System.out.println(sb.toString());
|
||||
}
|
||||
}
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public static void main(String[] args){
|
||||
|
@ -50,12 +50,12 @@ import javax.swing.SwingUtilities;
|
||||
*/
|
||||
public class AppletHarness extends Applet {
|
||||
|
||||
public static final HashMap<Application, Applet> appToApplet
|
||||
= new HashMap<Application, Applet>();
|
||||
public static final HashMap<LegacyApplication, Applet> appToApplet
|
||||
= new HashMap<LegacyApplication, Applet>();
|
||||
|
||||
protected JmeCanvasContext context;
|
||||
protected Canvas canvas;
|
||||
protected Application app;
|
||||
protected LegacyApplication app;
|
||||
|
||||
protected String appClass;
|
||||
protected URL appCfg = null;
|
||||
@ -103,7 +103,7 @@ public class AppletHarness extends Applet {
|
||||
JmeSystem.setLowPermissions(true);
|
||||
|
||||
try{
|
||||
Class<? extends Application> clazz = (Class<? extends Application>) Class.forName(appClass);
|
||||
Class<? extends LegacyApplication> clazz = (Class<? extends LegacyApplication>) Class.forName(appClass);
|
||||
app = clazz.newInstance();
|
||||
}catch (ClassNotFoundException ex){
|
||||
ex.printStackTrace();
|
||||
|
@ -43,6 +43,7 @@ import com.jme3.system.JmeContext.Type;
|
||||
import com.jme3.util.Screenshots;
|
||||
import java.awt.EventQueue;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.GraphicsEnvironment;
|
||||
import java.awt.RenderingHints;
|
||||
import java.awt.geom.AffineTransform;
|
||||
import java.awt.image.AffineTransformOp;
|
||||
@ -116,12 +117,16 @@ public class JmeDesktopSystem extends JmeSystemDelegate {
|
||||
|
||||
@Override
|
||||
public void showErrorDialog(String message) {
|
||||
if (!GraphicsEnvironment.isHeadless()) {
|
||||
final String msg = message;
|
||||
EventQueue.invokeLater(new Runnable() {
|
||||
public void run() {
|
||||
ErrorDialog.showDialog(msg);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
System.err.println("[JME ERROR] " + message);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -129,6 +134,9 @@ public class JmeDesktopSystem extends JmeSystemDelegate {
|
||||
if (SwingUtilities.isEventDispatchThread()) {
|
||||
throw new IllegalStateException("Cannot run from EDT");
|
||||
}
|
||||
if (GraphicsEnvironment.isHeadless()) {
|
||||
throw new IllegalStateException("Cannot show dialog in headless environment");
|
||||
}
|
||||
|
||||
final AppSettings settings = new AppSettings(false);
|
||||
settings.copyFrom(sourceSettings);
|
||||
@ -333,27 +341,13 @@ public class JmeDesktopSystem extends JmeSystemDelegate {
|
||||
if (initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
initialized = true;
|
||||
try {
|
||||
if (!lowPermissions) {
|
||||
// can only modify logging settings
|
||||
// if permissions are available
|
||||
// JmeFormatter formatter = new JmeFormatter();
|
||||
// Handler fileHandler = new FileHandler("jme.log");
|
||||
// fileHandler.setFormatter(formatter);
|
||||
// Logger.getLogger("").addHandler(fileHandler);
|
||||
// Handler consoleHandler = new ConsoleHandler();
|
||||
// consoleHandler.setFormatter(formatter);
|
||||
// Logger.getLogger("").removeHandler(Logger.getLogger("").getHandlers()[0]);
|
||||
// Logger.getLogger("").addHandler(consoleHandler);
|
||||
}
|
||||
// } catch (IOException ex){
|
||||
// logger.log(Level.SEVERE, "I/O Error while creating log file", ex);
|
||||
} catch (SecurityException ex) {
|
||||
logger.log(Level.SEVERE, "Security error in creating log file", ex);
|
||||
}
|
||||
logger.log(Level.INFO, getBuildInfo());
|
||||
if (!lowPermissions) {
|
||||
if (NativeLibraryLoader.isUsingNativeBullet()) {
|
||||
NativeLibraryLoader.loadNativeLibrary("bulletjme", true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -11,6 +11,11 @@ task run(dependsOn: 'build', type:JavaExec) {
|
||||
jvmArgs "-XstartOnFirstThread"
|
||||
jvmArgs "-Djava.awt.headless=true"
|
||||
}
|
||||
|
||||
if (System.properties['java.util.logging.config.file'] != null) {
|
||||
systemProperty "java.util.logging.config.file", System.properties['java.util.logging.config.file']
|
||||
}
|
||||
|
||||
if( assertions == "true" ){
|
||||
enableAssertions = true;
|
||||
}
|
||||
|
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