Add the new material system
Also includes some unrelated tests Conflicts: jme3-core/src/main/java/com/jme3/renderer/RenderManager.java jme3-core/src/main/java/com/jme3/renderer/opengl/GLRenderer.java
This commit is contained in:
parent
49339497fa
commit
9d035f747a
@ -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}
|
||||
*/
|
||||
|
@ -0,0 +1,96 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import com.jme3.asset.AssetManager;
|
||||
import com.jme3.light.AmbientLight;
|
||||
import com.jme3.light.Light;
|
||||
import com.jme3.light.LightList;
|
||||
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, 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);
|
||||
}
|
||||
}
|
||||
|
||||
public 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);
|
||||
}
|
||||
}
|
@ -129,9 +129,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
|
||||
|
@ -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;
|
||||
}
|
||||
texId += tex.getTextureValue().getImage().getId() % 0xff;
|
||||
}
|
||||
if (!param.getVarType().isTextureType()) {
|
||||
continue;
|
||||
}
|
||||
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;
|
||||
}
|
||||
texturesSortId = texturesSortId * 23 + textureId;
|
||||
}
|
||||
sortingId = texId + t.getShader().getId() * 1000;
|
||||
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,20 +779,40 @@ 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);
|
||||
} else {
|
||||
technique.makeCurrent(def.getAssetManager(), false, rm.getRenderer().getCaps(), rm);
|
||||
private void updateShaderMaterialParameters(Renderer renderer, Shader shader) {
|
||||
int unit = 0;
|
||||
for (int i = 0; i < paramValues.size(); i++) {
|
||||
MatParam param = paramValues.getValue(i);
|
||||
VarType type = param.getVarType();
|
||||
Uniform uniform = shader.getUniform(param.getPrefixedName());
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Preloads this material for the given render manager.
|
||||
* <p>
|
||||
@ -1048,18 +822,21 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
|
||||
*
|
||||
* @param rm 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, rendererCaps);
|
||||
updateShaderMaterialParameters(renderer, shader);
|
||||
renderManager.getRenderer().setShader(shader);
|
||||
}
|
||||
|
||||
private void clearUniformsSetByCurrent(Shader shader) {
|
||||
@ -1141,80 +918,43 @@ 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();
|
||||
|
||||
if (techniqueDef.isNoRender()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Apply render state
|
||||
updateRenderState(renderManager, renderer, techniqueDef);
|
||||
|
||||
// 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!
|
||||
return;
|
||||
}
|
||||
|
||||
// upload and bind shader
|
||||
// any unset uniforms will be set to 0
|
||||
// Select shader to use
|
||||
Shader shader = technique.makeCurrent(renderManager, rendererCaps);
|
||||
|
||||
// Begin tracking which uniforms were changed by material.
|
||||
clearUniformsSetByCurrent(shader);
|
||||
|
||||
// Set uniform bindings
|
||||
renderManager.updateUniformBindings(shader);
|
||||
|
||||
// Set material parameters
|
||||
updateShaderMaterialParameters(renderer, shader);
|
||||
|
||||
// Clear any uniforms not changed by material.
|
||||
resetUniformsNotSetByCurrent(shader);
|
||||
r.setShader(shader);
|
||||
|
||||
renderMeshFromGeometry(r, geom);
|
||||
|
||||
// Delegate rendering to the technique
|
||||
technique.render(renderManager, shader, geometry, lights);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1239,6 +979,14 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
|
||||
oc.write(name, "name", null);
|
||||
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);
|
||||
|
@ -0,0 +1,176 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
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.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;
|
||||
|
||||
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.BlendMode;
|
||||
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, DefineList defines) {
|
||||
defines.set(nbLightsDefineId, renderManager.getSinglePassLightBatchSize() * 3);
|
||||
defines.set(singlePassLightingDefineId, true);
|
||||
return super.makeCurrent(assetManager, renderManager, rendererCaps, 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -32,26 +32,25 @@
|
||||
package com.jme3.material;
|
||||
|
||||
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 java.util.ArrayList;
|
||||
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.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 dynamicDefines;
|
||||
|
||||
/**
|
||||
* Creates a new technique instance that implements the given
|
||||
@ -63,14 +62,7 @@ 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.dynamicDefines = def.createDefineList();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -84,158 +76,113 @@ 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.
|
||||
|
||||
if (value == null) {
|
||||
// Clear the define.
|
||||
needReload = defines.remove(defineName) || needReload;
|
||||
} else {
|
||||
// Set the define.
|
||||
needReload = defines.set(defineName, type, value) || needReload;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void updateUniformParam(String paramName, VarType type, Object value) {
|
||||
if (paramName == null) {
|
||||
throw new IllegalArgumentException();
|
||||
Integer defineId = def.getShaderParamDefineId(paramName);
|
||||
if (defineId == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (value == null) {
|
||||
dynamicDefines.set(defineId, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
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);
|
||||
dynamicDefines.set(defineId, (Integer) value);
|
||||
break;
|
||||
case Float:
|
||||
dynamicDefines.set(defineId, (Float) value);
|
||||
break;
|
||||
case Boolean:
|
||||
dynamicDefines.set(defineId, ((Boolean)value));
|
||||
break;
|
||||
default:
|
||||
u.setValue(type, value);
|
||||
dynamicDefines.set(defineId, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns true if the technique must be reloaded.
|
||||
* <p>
|
||||
* If a technique needs to reload, then the {@link Material} should
|
||||
* call {@link #makeCurrent(com.jme3.asset.AssetManager) } on this
|
||||
* technique.
|
||||
*
|
||||
* @return true if the technique must be reloaded.
|
||||
* 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.
|
||||
*/
|
||||
public boolean isNeedReload() {
|
||||
return needReload;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares the technique for use by loading the shader and setting
|
||||
* the proper defines based on material parameters.
|
||||
*
|
||||
* @param assetManager The asset manager to use for loading shaders.
|
||||
*/
|
||||
public void makeCurrent(AssetManager assetManager, boolean techniqueSwitched, EnumSet<Caps> rendererCaps, RenderManager rm) {
|
||||
if (techniqueSwitched) {
|
||||
if (defines.update(owner.getParamsMap(), def)) {
|
||||
needReload = true;
|
||||
}
|
||||
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);
|
||||
void notifyTechniqueSwitched() {
|
||||
ListMap<String, MatParam> paramMap = owner.getParamsMap();
|
||||
for (int i = 0; i < paramMap.size(); i++) {
|
||||
MatParam param = paramMap.getValue(i);
|
||||
notifyParamChanged(param.getName(), param.getVarType(), param.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
/**
|
||||
* Called by the material to determine which shader to use for rendering.
|
||||
*
|
||||
* The {@link TechniqueDefLogic} is used to determine the shader to use
|
||||
* based on the {@link LightMode}.
|
||||
*
|
||||
* @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.
|
||||
*/
|
||||
Shader makeCurrent(RenderManager renderManager, EnumSet<Caps> rendererCaps) {
|
||||
TechniqueDefLogic logic = def.getLogic();
|
||||
AssetManager assetManager = owner.getMaterialDef().getAssetManager();
|
||||
return logic.makeCurrent(assetManager, renderManager, rendererCaps, 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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);
|
||||
/**
|
||||
* 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 + dynamicDefines.hashCode();
|
||||
return hash;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
@ -31,9 +31,11 @@
|
||||
*/
|
||||
package com.jme3.material;
|
||||
|
||||
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 +95,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;
|
||||
@ -106,10 +114,10 @@ public class TechniqueDef implements Savable {
|
||||
private RenderState renderState;
|
||||
private RenderState forcedRenderState;
|
||||
|
||||
private LightMode lightMode = LightMode.Disable;
|
||||
private LightMode lightMode = LightMode.Disable;
|
||||
private ShadowMode shadowMode = ShadowMode.Disable;
|
||||
private TechniqueDefLogic logic;
|
||||
|
||||
private HashMap<String, String> defineParams;
|
||||
private ArrayList<UniformBinding> worldBinds;
|
||||
|
||||
/**
|
||||
@ -120,17 +128,30 @@ 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialization only. Do not use.
|
||||
*/
|
||||
public TechniqueDef(){
|
||||
shaderLanguages=new EnumMap<Shader.ShaderType, String>(Shader.ShaderType.class);
|
||||
shaderNames=new EnumMap<Shader.ShaderType, String>(Shader.ShaderType.class);
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -162,7 +183,15 @@ public class TechniqueDef implements Savable {
|
||||
public void setLightMode(LightMode lightMode) {
|
||||
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 +253,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,7 +294,157 @@ 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 define name.
|
||||
*
|
||||
* @param defineName The define name to lookup
|
||||
* @return The define ID, or null if not found.
|
||||
*/
|
||||
public Integer getShaderParamDefineId(String defineName) {
|
||||
return paramToDefineId.get(defineName);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 +472,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 +573,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 +596,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 +653,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,95 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
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 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, 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);
|
||||
}
|
@ -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;
|
||||
@ -51,7 +55,6 @@ 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;
|
||||
@ -530,6 +533,7 @@ public class RenderManager {
|
||||
lightFilter.filterLights(g, filteredLightList);
|
||||
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
|
||||
@ -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>
|
||||
*
|
||||
|
@ -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());
|
||||
}
|
||||
|
||||
@ -964,12 +975,12 @@ public final class GLRenderer implements Renderer {
|
||||
gl.glUniform1i(loc, b.booleanValue() ? GL.GL_TRUE : GL.GL_FALSE);
|
||||
break;
|
||||
case Matrix3:
|
||||
fb = uniform.getMultiData();
|
||||
fb = (FloatBuffer) uniform.getValue();
|
||||
assert fb.remaining() == 9;
|
||||
gl.glUniformMatrix3(loc, false, fb);
|
||||
break;
|
||||
case Matrix4:
|
||||
fb = uniform.getMultiData();
|
||||
fb = (FloatBuffer) uniform.getValue();
|
||||
assert fb.remaining() == 16;
|
||||
gl.glUniformMatrix4(loc, false, fb);
|
||||
break;
|
||||
@ -978,23 +989,23 @@ public final class GLRenderer implements Renderer {
|
||||
gl.glUniform1(loc, ib);
|
||||
break;
|
||||
case FloatArray:
|
||||
fb = uniform.getMultiData();
|
||||
fb = (FloatBuffer) uniform.getValue();
|
||||
gl.glUniform1(loc, fb);
|
||||
break;
|
||||
case Vector2Array:
|
||||
fb = uniform.getMultiData();
|
||||
fb = (FloatBuffer) uniform.getValue();
|
||||
gl.glUniform2(loc, fb);
|
||||
break;
|
||||
case Vector3Array:
|
||||
fb = uniform.getMultiData();
|
||||
fb = (FloatBuffer) uniform.getValue();
|
||||
gl.glUniform3(loc, fb);
|
||||
break;
|
||||
case Vector4Array:
|
||||
fb = uniform.getMultiData();
|
||||
fb = (FloatBuffer) uniform.getValue();
|
||||
gl.glUniform4(loc, fb);
|
||||
break;
|
||||
case Matrix4Array:
|
||||
fb = uniform.getMultiData();
|
||||
fb = (FloatBuffer) uniform.getValue();
|
||||
gl.glUniformMatrix4(loc, false, fb);
|
||||
break;
|
||||
case Int:
|
||||
@ -2689,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;
|
||||
}
|
||||
|
||||
//this is kept for backward compatibility.
|
||||
if (mesh.getLineWidth() != -1 && context.lineWidth != mesh.getLineWidth()) {
|
||||
if (count > 1 && !caps.contains(Caps.MeshInstancing)) {
|
||||
throw new RendererException("Mesh instancing is not supported by the video hardware");
|
||||
}
|
||||
|
||||
if (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.
|
||||
|
@ -1,286 +1,128 @@
|
||||
/*
|
||||
* 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.export.*;
|
||||
import com.jme3.material.MatParam;
|
||||
import com.jme3.material.TechniqueDef;
|
||||
import com.jme3.util.ListMap;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
|
||||
public final class DefineList implements Savable, Cloneable {
|
||||
|
||||
private static final String ONE = "1";
|
||||
|
||||
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++;
|
||||
}
|
||||
|
||||
oc.write(keys, "keys", null);
|
||||
oc.write(vals, "vals", null);
|
||||
}
|
||||
|
||||
public void read(JmeImporter im) throws IOException{
|
||||
InputCapsule ic = im.getCapsule(this);
|
||||
|
||||
String[] keys = ic.readStringArray("keys", null);
|
||||
String[] vals = ic.readStringArray("vals", null);
|
||||
for (int i = 0; i < keys.length; i++){
|
||||
defines.put(keys[i], vals[i]);
|
||||
}
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
defines.clear();
|
||||
compiled = "";
|
||||
cachedHashCode = 0;
|
||||
}
|
||||
|
||||
public String get(String key){
|
||||
return defines.get(key);
|
||||
}
|
||||
|
||||
@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 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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
if (cachedHashCode == 0) {
|
||||
cachedHashCode = defines.hashCode();
|
||||
}
|
||||
return cachedHashCode;
|
||||
}
|
||||
|
||||
@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(", ");
|
||||
}
|
||||
i++;
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* 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 java.util.List;
|
||||
|
||||
/**
|
||||
* The new define list.
|
||||
*
|
||||
* @author Kirill Vainer
|
||||
*/
|
||||
public final class DefineList implements Cloneable {
|
||||
|
||||
public static final int MAX_DEFINES = 64;
|
||||
|
||||
public static final int SAVABLE_VERSION = 1;
|
||||
|
||||
private long hash;
|
||||
private final int[] vals;
|
||||
|
||||
public DefineList(int numValues) {
|
||||
if (numValues < 0 || numValues > MAX_DEFINES) {
|
||||
throw new IllegalArgumentException("numValues must be between 0 and 64");
|
||||
}
|
||||
vals = new int[numValues];
|
||||
}
|
||||
|
||||
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 set(int id, int val) {
|
||||
assert 0 <= id && id < 64;
|
||||
if (val != 0) {
|
||||
hash |= (1L << id);
|
||||
} else {
|
||||
hash &= ~(1L << id);
|
||||
}
|
||||
vals[id] = val;
|
||||
}
|
||||
|
||||
public void set(int id, float val) {
|
||||
set(id, Float.floatToIntBits(val));
|
||||
}
|
||||
|
||||
public void set(int id, boolean val) {
|
||||
set(id, val ? 1 : 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return (int)((hash >> 32) ^ hash);
|
||||
}
|
||||
|
||||
@Override
|
||||
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;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
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.isFinite(val)) {
|
||||
throw new IllegalArgumentException(
|
||||
"GLSL does not support NaN "
|
||||
+ "or Infinite float literals");
|
||||
}
|
||||
sb.append(val);
|
||||
} else {
|
||||
sb.append(vals[i]);
|
||||
}
|
||||
|
||||
sb.append("\n");
|
||||
}
|
||||
}
|
||||
System.out.println(sb.toString());
|
||||
}
|
||||
}
|
@ -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);
|
||||
@ -277,6 +306,10 @@ public final class Shader extends NativeObject {
|
||||
public ListMap<String, Uniform> getUniformMap(){
|
||||
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);
|
||||
|
||||
if (shaderSourceCode != null) {
|
||||
String shaderSourceAssetName = techniqueName + "." + extension;
|
||||
shader.addSource(type, shaderSourceAssetName, shaderSourceCode, definesSourceCode, language);
|
||||
}
|
||||
}
|
||||
|
||||
technique = null;
|
||||
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));
|
||||
}
|
||||
|
||||
}
|
@ -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()) {
|
||||
|
@ -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() {
|
||||
|
@ -117,7 +117,7 @@ MaterialDef Phong Lighting {
|
||||
Boolean BackfaceShadows: false
|
||||
}
|
||||
|
||||
Technique {
|
||||
Technique {
|
||||
LightMode SinglePass
|
||||
|
||||
VertexShader GLSL100: Common/MatDefs/Light/SPLighting.vert
|
||||
@ -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
|
||||
@ -234,8 +233,7 @@ MaterialDef Phong Lighting {
|
||||
HARDWARE_SHADOWS : HardwareShadows
|
||||
FILTER_MODE : FilterMode
|
||||
PCFEDGE : PCFEdge
|
||||
DISCARD_ALPHA : AlphaDiscardThreshold
|
||||
COLOR_MAP : ColorMap
|
||||
DISCARD_ALPHA : AlphaDiscardThreshold
|
||||
SHADOWMAP_SIZE : ShadowMapSize
|
||||
FADE : FadeInfo
|
||||
PSSM : Splits
|
||||
@ -268,8 +266,7 @@ MaterialDef Phong Lighting {
|
||||
HARDWARE_SHADOWS : HardwareShadows
|
||||
FILTER_MODE : FilterMode
|
||||
PCFEDGE : PCFEdge
|
||||
DISCARD_ALPHA : AlphaDiscardThreshold
|
||||
COLOR_MAP : ColorMap
|
||||
DISCARD_ALPHA : AlphaDiscardThreshold
|
||||
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
|
||||
|
@ -40,6 +40,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 +74,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 +117,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);
|
||||
}
|
||||
|
||||
@ -495,10 +501,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");
|
||||
}
|
||||
@ -560,15 +578,28 @@ 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");
|
||||
}
|
||||
@ -579,18 +610,40 @@ 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");
|
||||
}
|
||||
|
||||
if (shaderName.containsKey(Shader.ShaderType.Vertex) && shaderName.containsKey(Shader.ShaderType.Fragment)) {
|
||||
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);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -56,4 +56,38 @@ 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());
|
||||
}
|
||||
|
||||
@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);
|
||||
|
||||
assert fastResult == slowResult;
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
143
jme3-core/src/test/java/com/jme3/shader/DefineListTest.java
Normal file
143
jme3-core/src/test/java/com/jme3/shader/DefineListTest.java
Normal file
@ -0,0 +1,143 @@
|
||||
/*
|
||||
* 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.List;
|
||||
import org.junit.Test;
|
||||
|
||||
public class DefineListTest {
|
||||
|
||||
private List<String> defineNames;
|
||||
private List<VarType> defineTypes;
|
||||
|
||||
@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);
|
||||
}
|
||||
|
||||
private String generateSource(DefineList dl) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
dl.generateSource(sb, defineNames, defineTypes);
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInitial() {
|
||||
DefineList dl = new DefineList(3);
|
||||
defineNames = Arrays.asList("A", "B", "C");
|
||||
defineTypes = Arrays.asList(VarType.Boolean, VarType.Int, VarType.Float);
|
||||
|
||||
assert dl.hashCode() == 0;
|
||||
assert generateSource(dl).equals("");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBooleanDefine() {
|
||||
DefineList dl = new DefineList(1);
|
||||
defineNames = Arrays.asList("BOOL_VAR");
|
||||
defineTypes = Arrays.asList(VarType.Boolean);
|
||||
|
||||
dl.set(0, true);
|
||||
assert dl.hashCode() == 1;
|
||||
assert generateSource(dl).equals("#define BOOL_VAR 1\n");
|
||||
|
||||
dl.set(0, false);
|
||||
assert dl.hashCode() == 0;
|
||||
assert generateSource(dl).equals("");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFloatDefine() {
|
||||
DefineList dl = new DefineList(1);
|
||||
defineNames = Arrays.asList("FLOAT_VAR");
|
||||
defineTypes = Arrays.asList(VarType.Float);
|
||||
|
||||
dl.set(0, 1f);
|
||||
assert dl.hashCode() == 1;
|
||||
assert generateSource(dl).equals("#define FLOAT_VAR 1.0\n");
|
||||
|
||||
dl.set(0, 0f);
|
||||
assert dl.hashCode() == 0;
|
||||
assert generateSource(dl).equals("");
|
||||
|
||||
dl.set(0, -1f);
|
||||
assert generateSource(dl).equals("#define FLOAT_VAR -1.0\n");
|
||||
|
||||
dl.set(0, FastMath.FLT_EPSILON);
|
||||
assert generateSource(dl).equals("#define FLOAT_VAR 1.1920929E-7\n");
|
||||
|
||||
dl.set(0, FastMath.PI);
|
||||
assert generateSource(dl).equals("#define FLOAT_VAR 3.1415927\n");
|
||||
|
||||
try {
|
||||
dl.set(0, Float.NaN);
|
||||
generateSource(dl);
|
||||
assert false;
|
||||
} catch (IllegalArgumentException ex) { }
|
||||
|
||||
try {
|
||||
dl.set(0, Float.POSITIVE_INFINITY);
|
||||
generateSource(dl);
|
||||
assert false;
|
||||
} catch (IllegalArgumentException ex) { }
|
||||
|
||||
try {
|
||||
dl.set(0, Float.NEGATIVE_INFINITY);
|
||||
generateSource(dl);
|
||||
assert false;
|
||||
} catch (IllegalArgumentException ex) { }
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSourceGeneration() {
|
||||
DefineList dl = new DefineList(64);
|
||||
defineNames = Arrays.asList("BOOL_VAR", "INT_VAR", "FLOAT_VAR");
|
||||
defineTypes = Arrays.asList(VarType.Boolean, VarType.Int, VarType.Float);
|
||||
dl.set(0, true);
|
||||
dl.set(1, -1);
|
||||
dl.set(2, Float.NaN);
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
||||
@ -33,23 +34,22 @@ public class ShaderCheck {
|
||||
assetManager.registerLoader(GLSLLoader.class, "vert", "frag","geom","tsctrl","tseval","glsllib");
|
||||
}
|
||||
|
||||
private static void checkMatDef(String matdefName){
|
||||
private static void checkMatDef(String matdefName) {
|
||||
MaterialDef def = (MaterialDef) assetManager.loadAsset(matdefName);
|
||||
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);
|
||||
|
||||
for (Validator validator : validators){
|
||||
EnumSet<Caps> rendererCaps = EnumSet.noneOf(Caps.class);
|
||||
rendererCaps.add(Caps.GLSL100);
|
||||
for (TechniqueDef techDef : def.getDefaultTechniques()) {
|
||||
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
jme3-desktop/src/test/java/LibraryLoaderTest.java
Normal file
50
jme3-desktop/src/test/java/LibraryLoaderTest.java
Normal file
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import com.jme3.system.NativeLibraryLoader;
|
||||
import java.io.File;
|
||||
import java.util.Arrays;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Kirill Vainer
|
||||
*/
|
||||
public class LibraryLoaderTest {
|
||||
|
||||
@Test
|
||||
public void whatever() {
|
||||
File[] nativeJars = NativeLibraryLoader.getJarsWithNatives();
|
||||
System.out.println(Arrays.toString(nativeJars));
|
||||
}
|
||||
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user