diff --git a/jme3-core/src/main/java/com/jme3/asset/AssetManager.java b/jme3-core/src/main/java/com/jme3/asset/AssetManager.java index a077512ef..4cd284340 100644 --- a/jme3-core/src/main/java/com/jme3/asset/AssetManager.java +++ b/jme3-core/src/main/java/com/jme3/asset/AssetManager.java @@ -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". diff --git a/jme3-core/src/main/java/com/jme3/asset/DesktopAssetManager.java b/jme3-core/src/main/java/com/jme3/asset/DesktopAssetManager.java index 66a6fbbdf..94ee68ceb 100644 --- a/jme3-core/src/main/java/com/jme3/asset/DesktopAssetManager.java +++ b/jme3-core/src/main/java/com/jme3/asset/DesktopAssetManager.java @@ -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} */ diff --git a/jme3-core/src/main/java/com/jme3/material/DefaultTechniqueDefLogic.java b/jme3-core/src/main/java/com/jme3/material/DefaultTechniqueDefLogic.java new file mode 100644 index 000000000..601ea7878 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/material/DefaultTechniqueDefLogic.java @@ -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 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); + } +} diff --git a/jme3-core/src/main/java/com/jme3/material/MatParam.java b/jme3-core/src/main/java/com/jme3/material/MatParam.java index 677c17d55..f9e6156f3 100644 --- a/jme3-core/src/main/java/com/jme3/material/MatParam.java +++ b/jme3-core/src/main/java/com/jme3/material/MatParam.java @@ -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 diff --git a/jme3-core/src/main/java/com/jme3/material/MatParamTexture.java b/jme3-core/src/main/java/com/jme3/material/MatParamTexture.java index a3db63a1c..78c8ef33c 100644 --- a/jme3-core/src/main/java/com/jme3/material/MatParamTexture.java +++ b/jme3-core/src/main/java/com/jme3/material/MatParamTexture.java @@ -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 { diff --git a/jme3-core/src/main/java/com/jme3/material/Material.java b/jme3-core/src/main/java/com/jme3/material/Material.java index 2aabbd667..6f04ccef6 100644 --- a/jme3-core/src/main/java/com/jme3/material/Material.java +++ b/jme3-core/src/main/java/com/jme3/material/Material.java @@ -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 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.

* - *

- * uniform vec4 g_LightColor[numLights];
// - * g_LightColor.rgb is the diffuse/specular color of the light.
// - * g_Lightcolor.a is the type of light, 0 = Directional, 1 = Point,
// - * 2 = Spot.

- * uniform vec4 g_LightPosition[numLights];
// - * g_LightPosition.xyz is the position of the light (for point lights)
- * // or the direction of the light (for directional lights).
// - * g_LightPosition.w is the inverse radius (1/r) of the light (for - * attenuation)

- */ - 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. *

@@ -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 rendererCaps = renderManager.getRenderer().getCaps(); if (tech == null) { - + EnumSet rendererCaps = renderManager.getRenderer().getCaps(); if (name.equals("Default")) { List 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); + 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 { - technique.makeCurrent(def.getAssetManager(), false, rm.getRenderer().getCaps(), rm); + 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. *

@@ -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); - - Renderer r = rm.getRenderer(); - TechniqueDef techDef = technique.getDef(); + public void preload(RenderManager renderManager) { + if (technique == null) { + selectTechnique("Default", renderManager); + } + TechniqueDef techniqueDef = technique.getDef(); + Renderer renderer = renderManager.getRenderer(); + EnumSet rendererCaps = renderer.getCaps(); - Collection 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 { * * * - * @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)); - } - } - - - // 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); + public void render(Geometry geometry, LightList lights, RenderManager renderManager) { + if (technique == null) { + selectTechnique("Default", renderManager); } - - 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; + + TechniqueDef techniqueDef = technique.getDef(); + Renderer renderer = renderManager.getRenderer(); + EnumSet rendererCaps = renderer.getCaps(); + + if (techniqueDef.isNoRender()) { + return; } - // upload and bind shader - // any unset uniforms will be set to 0 + // Apply render state + updateRenderState(renderManager, renderer, techniqueDef); + + // 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); diff --git a/jme3-core/src/main/java/com/jme3/material/MultiPassLightingLogic.java b/jme3-core/src/main/java/com/jme3/material/MultiPassLightingLogic.java new file mode 100644 index 000000000..293859b66 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/material/MultiPassLightingLogic.java @@ -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); + } + } +} diff --git a/jme3-core/src/main/java/com/jme3/material/SinglePassLightingLogic.java b/jme3-core/src/main/java/com/jme3/material/SinglePassLightingLogic.java new file mode 100644 index 000000000..8c6e8cd43 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/material/SinglePassLightingLogic.java @@ -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 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.

* + *

+ * uniform vec4 g_LightColor[numLights];
// + * g_LightColor.rgb is the diffuse/specular color of the light.
// + * g_Lightcolor.a is the type of light, 0 = Directional, 1 = Point,
// + * 2 = Spot.

+ * uniform vec4 g_LightPosition[numLights];
// + * g_LightPosition.xyz is the position of the light (for point lights)
+ * // or the direction of the light (for directional lights).
// + * g_LightPosition.w is the inverse radius (1/r) of the light (for + * attenuation)

+ */ + 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); + } + } + } +} diff --git a/jme3-core/src/main/java/com/jme3/material/Technique.java b/jme3-core/src/main/java/com/jme3/material/Technique.java index 8321991bf..b6ab27e2c 100644 --- a/jme3-core/src/main/java/com/jme3/material/Technique.java +++ b/jme3-core/src/main/java/com/jme3/material/Technique.java @@ -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 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(); - 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. - *

- * 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 getWorldBindUniforms() { - return worldBindUniforms; - } - /** * Called by the material to tell the technique a parameter was modified. * Specify null 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; - } + Integer defineId = def.getShaderParamDefineId(paramName); + if (defineId == null) { + return; } - } - - void updateUniformParam(String paramName, VarType type, Object value) { - if (paramName == null) { - throw new IllegalArgumentException(); + + 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; } } + + /** + * 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. + */ + void notifyTechniqueSwitched() { + ListMap paramMap = owner.getParamsMap(); + for (int i = 0; i < paramMap.size(); i++) { + MatParam param = paramMap.getValue(i); + notifyParamChanged(param.getName(), param.getVarType(), param.getValue()); + } + } /** - * Returns true if the technique must be reloaded. - *

- * If a technique needs to reload, then the {@link Material} should - * call {@link #makeCurrent(com.jme3.asset.AssetManager) } on this - * technique. + * Called by the material to determine which shader to use for rendering. + * + * The {@link TechniqueDefLogic} is used to determine the shader to use + * based on the {@link LightMode}. * - * @return true if the technique must be reloaded. + * @param renderManager The render manager for which the shader is to be selected. + * @param rendererCaps The renderer capabilities which the shader should support. + * @return A compatible shader. */ - public boolean isNeedReload() { - return needReload; + Shader makeCurrent(RenderManager renderManager, EnumSet rendererCaps) { + TechniqueDefLogic logic = def.getLogic(); + AssetManager assetManager = owner.getMaterialDef().getAssetManager(); + return logic.makeCurrent(assetManager, renderManager, rendererCaps, dynamicDefines); } - + /** - * Prepares the technique for use by loading the shader and setting - * the proper defines based on material parameters. + * Render the technique according to its {@link TechniqueDefLogic}. * - * @param assetManager The asset manager to use for loading shaders. + * @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. */ - public void makeCurrent(AssetManager assetManager, boolean techniqueSwitched, EnumSet 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 render(RenderManager renderManager, Shader shader, Geometry geometry, LightList lights) { + TechniqueDefLogic logic = def.getLogic(); + logic.render(renderManager, shader, geometry, lights); } - - private void loadShader(AssetManager manager,EnumSet 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; + + /** + * 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; } /** - * Computes the define list - * @return the complete define list + * @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; - } - - /* - 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); + throw new UnsupportedOperationException(); } - - public void read(JmeImporter im) throws IOException { - InputCapsule ic = im.getCapsule(this); - def = (TechniqueDef) ic.readSavable("def", null); - worldBindUniforms = ic.readSavableArrayList("worldBindUniforms", null); - defines = (DefineList) ic.readSavable("defines", null); - shader = (Shader) ic.readSavable("shader", null); + + /** + * Compute the sort ID. Similar to {@link Object#hashCode()} but used + * for sorting geometries for rendering. + * + * @return the sort ID for this technique instance. + */ + public int getSortId() { + int hash = 17; + hash = hash * 23 + def.getSortId(); + hash = hash * 23 + dynamicDefines.hashCode(); + return hash; } - */ } diff --git a/jme3-core/src/main/java/com/jme3/material/TechniqueDef.java b/jme3-core/src/main/java/com/jme3/material/TechniqueDef.java index d7523956c..43419e543 100644 --- a/jme3-core/src/main/java/com/jme3/material/TechniqueDef.java +++ b/jme3-core/src/main/java/com/jme3/material/TechniqueDef.java @@ -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 requiredCaps = EnumSet.noneOf(Caps.class); private String name; - + private int sortId; + private EnumMap shaderLanguages; private EnumMap shaderNames; - private DefineList presetDefines; + private String shaderPrologue; + private ArrayList defineNames; + private ArrayList defineTypes; + private HashMap paramToDefineId; + private final HashMap definesToShaderMap; + private boolean usesNodes = false; private List 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 defineParams; private ArrayList worldBinds; /** @@ -120,17 +128,30 @@ public class TechniqueDef implements Savable { * @param name The name of the technique, should be set to null * 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.class); - shaderNames=new EnumMap(Shader.ShaderType.class); + public TechniqueDef() { + shaderLanguages = new EnumMap(Shader.ShaderType.class); + shaderNames = new EnumMap(Shader.ShaderType.class); + defineNames = new ArrayList(); + defineTypes = new ArrayList(); + paramToDefineId = new HashMap(); + definesToShaderMap = new HashMap(); + } + + /** + * @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,34 +294,24 @@ public class TechniqueDef implements Savable { requiredCaps.add(fragCap); } - /** - * Sets the shaders that this technique definition will use. - * - * @param shaderNames EnumMap containing all shader names for this stage - * @param shaderLanguages EnumMap containing all shader languages for this stage + * 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 setShaderFile(EnumMap shaderNames, EnumMap shaderLanguages) { - requiredCaps.clear(); - - for (Shader.ShaderType shaderType : shaderNames.keySet()) { - String language = shaderLanguages.get(shaderType); - String shaderFile = shaderNames.get(shaderType); - - this.shaderLanguages.put(shaderType, language); - this.shaderNames.put(shaderType, shaderFile); - - Caps vertCap = Caps.valueOf(language); - requiredCaps.add(vertCap); - - if (shaderType.equals(Shader.ShaderType.Geometry)) { - requiredCaps.add(Caps.GeometryShader); - } else if (shaderType.equals(Shader.ShaderType.TessellationControl)) { - requiredCaps.add(Caps.TesselationShader); - } - } + 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. * @@ -310,60 +321,155 @@ public class TechniqueDef implements Savable { * @see #addShaderParamDefine(java.lang.String, java.lang.String) */ public String getShaderParamDefine(String paramName){ - if (defineParams == null) { + Integer defineId = paramToDefineId.get(paramName); + if (defineId != null) { + return defineNames.get(defineId); + } else { return null; } - return defineParams.get(paramName); + } + + /** + * 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. *

* 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. + * When set, the material parameter will be mapped to an integer define, + * typically 1 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, String defineName){ - if (defineParams == null) { - defineParams = new HashMap(); + 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."); } - defineParams.put(paramName, defineName); + + paramToDefineId.put(paramName, defineId); + defineNames.add(defineName); + defineTypes.add(paramType); } /** - * 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; + * 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 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 rendererCaps, DefineList defines) { + Shader shader = definesToShaderMap.get(defines); + if (shader == null) { + shader = loadShader(assetManager, rendererCaps, defines); + definesToShaderMap.put(defines.deepClone(), shader); + } + return shader; + } + /** - * Adds a preset define. - *

- * 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. + * Sets the shaders that this technique definition will use. * - * @param value The value of the define + * @param shaderNames EnumMap containing all shader names for this stage + * @param shaderLanguages EnumMap containing all shader languages for this stage */ - public void addShaderPresetDefine(String defineName, VarType type, Object value){ - if (presetDefines == null) { - presetDefines = new DefineList(); + public void setShaderFile(EnumMap shaderNames, EnumMap shaderLanguages) { + requiredCaps.clear(); + + for (Shader.ShaderType shaderType : shaderNames.keySet()) { + String language = shaderLanguages.get(shaderType); + String shaderFile = shaderNames.get(shaderType); + + this.shaderLanguages.put(shaderType, language); + this.shaderNames.put(shaderType, shaderFile); + + Caps vertCap = Caps.valueOf(language); + requiredCaps.add(vertCap); + + if (shaderType.equals(Shader.ShaderType.Geometry)) { + requiredCaps.add(Caps.GeometryShader); + } else if (shaderType.equals(Shader.ShaderType.TessellationControl)) { + requiredCaps.add(Caps.TesselationShader); + } } - presetDefines.set(defineName, type, value); } /** @@ -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 + "]"; } } diff --git a/jme3-core/src/main/java/com/jme3/material/TechniqueDefLogic.java b/jme3-core/src/main/java/com/jme3/material/TechniqueDefLogic.java new file mode 100644 index 000000000..f7bab2f88 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/material/TechniqueDefLogic.java @@ -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; + +/** + * TechniqueDefLogic 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 rendererCaps, DefineList defines); + + /** + * Requests that the TechniqueDefLogic 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); +} diff --git a/jme3-core/src/main/java/com/jme3/renderer/RenderManager.java b/jme3-core/src/main/java/com/jme3/renderer/RenderManager.java index 7aac5a689..d4e6ff17f 100644 --- a/jme3-core/src/main/java/com/jme3/renderer/RenderManager.java +++ b/jme3-core/src/main/java/com/jme3/renderer/RenderManager.java @@ -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 { *

* 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) }) *

  • 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.
  • * * diff --git a/jme3-core/src/main/java/com/jme3/renderer/opengl/GLRenderer.java b/jme3-core/src/main/java/com/jme3/renderer/opengl/GLRenderer.java index 20c75d47f..6e44a4c42 100644 --- a/jme3-core/src/main/java/com/jme3/renderer/opengl/GLRenderer.java +++ b/jme3-core/src/main/java/com/jme3/renderer/opengl/GLRenderer.java @@ -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(); } diff --git a/jme3-core/src/main/java/com/jme3/renderer/queue/GeometryList.java b/jme3-core/src/main/java/com/jme3/renderer/queue/GeometryList.java index 5529ac84e..09b43b9e8 100644 --- a/jme3-core/src/main/java/com/jme3/renderer/queue/GeometryList.java +++ b/jme3-core/src/main/java/com/jme3/renderer/queue/GeometryList.java @@ -99,6 +99,16 @@ public class GeometryList implements Iterable{ 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. * diff --git a/jme3-core/src/main/java/com/jme3/renderer/queue/OpaqueComparator.java b/jme3-core/src/main/java/com/jme3/renderer/queue/OpaqueComparator.java index 6ffff5702..4b1305c60 100644 --- a/jme3-core/src/main/java/com/jme3/renderer/queue/OpaqueComparator.java +++ b/jme3-core/src/main/java/com/jme3/renderer/queue/OpaqueComparator.java @@ -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. diff --git a/jme3-core/src/main/java/com/jme3/shader/DefineList.java b/jme3-core/src/main/java/com/jme3/shader/DefineList.java index dd605fc7e..089798ffd 100644 --- a/jme3-core/src/main/java/com/jme3/shader/DefineList.java +++ b/jme3-core/src/main/java/com/jme3/shader/DefineList.java @@ -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 defines = new TreeMap(); - 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 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) 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 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 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 defineNames, List 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()); + } +} \ No newline at end of file diff --git a/jme3-core/src/main/java/com/jme3/shader/Shader.java b/jme3-core/src/main/java/com/jme3/shader/Shader.java index eb084f178..d6fc495f2 100644 --- a/jme3-core/src/main/java/com/jme3/shader/Shader.java +++ b/jme3-core/src/main/java/com/jme3/shader/Shader.java @@ -45,44 +45,63 @@ public final class Shader extends NativeObject { /** * A list of all shader sources currently attached. */ - private ArrayList shaderSourceList; + private final ArrayList shaderSourceList; /** * Maps uniform name to the uniform variable. */ - private ListMap uniforms; + private final ListMap uniforms; + + /** + * Uniforms bound to {@link UniformBinding}s. + * + * Managed by the {@link UniformBindingManager}. + */ + private final ArrayList boundUniforms; /** * Maps attribute name to the location of the attribute in the shader. */ - private IntMap attribs; + private final IntMap 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) + * Control geometry assembly. (e.g compile a triangle list from input + * data) */ - Geometry, + Geometry("geom"), /** - * Controls tesselation factor (e.g how often a input patch should be subdivided) + * Controls tesselation factor (e.g how often a input patch should be + * subdivided) */ - TessellationControl, + TessellationControl("tsctrl"), /** - * Controls tesselation transform (e.g similar to the vertex shader, but required to mix inputs manual) + * Controls tesselation transform (e.g similar to the vertex shader, but + * required to mix inputs manual) */ - TessellationEvaluation; + TessellationEvaluation("tseval"); + + 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(); - uniforms = new ListMap(); - attribs = new IntMap(); - } - /** * Creates a new shader, {@link #initialize() } must be called * after this constructor for the shader to be usable. */ public Shader(){ super(); + shaderSourceList = new ArrayList(); + uniforms = new ListMap(); + attribs = new IntMap(); + boundUniforms = new ArrayList(); } /** @@ -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 getUniformMap(){ return uniforms; } + + public ArrayList getBoundUniforms() { + return boundUniforms; + } public Collection getSources(){ return shaderSourceList; diff --git a/jme3-core/src/main/java/com/jme3/shader/ShaderGenerator.java b/jme3-core/src/main/java/com/jme3/shader/ShaderGenerator.java index f5aeca2ed..d42dccf17 100644 --- a/jme3-core/src/main/java/com/jme3/shader/ShaderGenerator.java +++ b/jme3-core/src/main/java/com/jme3/shader/ShaderGenerator.java @@ -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 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(); diff --git a/jme3-core/src/main/java/com/jme3/shader/ShaderKey.java b/jme3-core/src/main/java/com/jme3/shader/ShaderKey.java deleted file mode 100644 index 23a187250..000000000 --- a/jme3-core/src/main/java/com/jme3/shader/ShaderKey.java +++ /dev/null @@ -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 { - - protected EnumMap shaderLanguage; - protected EnumMap shaderName; - protected DefineList defines; - protected int cachedHashedCode = 0; - protected boolean usesShaderNodes = false; - - public ShaderKey(){ - shaderLanguage=new EnumMap(Shader.ShaderType.class); - shaderName=new EnumMap(Shader.ShaderType.class); - } - - public ShaderKey(DefineList defines, EnumMap shaderLanguage,EnumMap shaderName){ - super(""); - this.name = reducePath(getShaderName(Shader.ShaderType.Vertex)); - this.shaderLanguage=new EnumMap(Shader.ShaderType.class); - this.shaderName=new EnumMap(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 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)); - } - -} diff --git a/jme3-core/src/main/java/com/jme3/shader/UniformBindingManager.java b/jme3-core/src/main/java/com/jme3/shader/UniformBindingManager.java index af2df5a94..f9ead8979 100644 --- a/jme3-core/src/main/java/com/jme3/shader/UniformBindingManager.java +++ b/jme3-core/src/main/java/com/jme3/shader/UniformBindingManager.java @@ -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; /** * UniformBindingManager 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 params) { + public void updateUniformBindings(Shader shader) { + ArrayList params = shader.getBoundUniforms(); for (int i = 0; i < params.size(); i++) { Uniform u = params.get(i); switch (u.getBinding()) { diff --git a/jme3-core/src/main/java/com/jme3/system/NullContext.java b/jme3-core/src/main/java/com/jme3/system/NullContext.java index 41204e6c9..54ee59c42 100644 --- a/jme3-core/src/main/java/com/jme3/system/NullContext.java +++ b/jme3-core/src/main/java/com/jme3/system/NullContext.java @@ -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(); diff --git a/jme3-core/src/main/java/com/jme3/system/NullRenderer.java b/jme3-core/src/main/java/com/jme3/system/NullRenderer.java index f2e029af4..ae00386ee 100644 --- a/jme3-core/src/main/java/com/jme3/system/NullRenderer.java +++ b/jme3-core/src/main/java/com/jme3/system/NullRenderer.java @@ -51,7 +51,7 @@ import com.jme3.texture.Texture; public class NullRenderer implements Renderer { - private static final EnumSet caps = EnumSet.noneOf(Caps.class); + private static final EnumSet caps = EnumSet.allOf(Caps.class); private static final Statistics stats = new Statistics(); public void initialize() { diff --git a/jme3-core/src/main/resources/Common/MatDefs/Light/Lighting.j3md b/jme3-core/src/main/resources/Common/MatDefs/Light/Lighting.j3md index a0f31aeb0..570cf6864 100644 --- a/jme3-core/src/main/resources/Common/MatDefs/Light/Lighting.j3md +++ b/jme3-core/src/main/resources/Common/MatDefs/Light/Lighting.j3md @@ -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 diff --git a/jme3-core/src/plugins/java/com/jme3/material/plugins/J3MLoader.java b/jme3-core/src/plugins/java/com/jme3/material/plugins/J3MLoader.java index 402ce6d47..e9286e82b 100644 --- a/jme3-core/src/plugins/java/com/jme3/material/plugins/J3MLoader.java +++ b/jme3-core/src/plugins/java/com/jme3/material/plugins/J3MLoader.java @@ -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 presetDefines = new ArrayList(); private EnumMap shaderLanguage; private EnumMap 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 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 roots) throws IOException{ diff --git a/jme3-core/src/plugins/java/com/jme3/material/plugins/ShaderNodeLoaderDelegate.java b/jme3-core/src/plugins/java/com/jme3/material/plugins/ShaderNodeLoaderDelegate.java index 7cc902f95..efdf28f4b 100644 --- a/jme3-core/src/plugins/java/com/jme3/material/plugins/ShaderNodeLoaderDelegate.java +++ b/jme3-core/src/plugins/java/com/jme3/material/plugins/ShaderNodeLoaderDelegate.java @@ -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); } diff --git a/jme3-core/src/test/java/com/jme3/asset/LoadShaderSourceTest.java b/jme3-core/src/test/java/com/jme3/asset/LoadShaderSourceTest.java new file mode 100644 index 000000000..4e9999b20 --- /dev/null +++ b/jme3-core/src/test/java/com/jme3/asset/LoadShaderSourceTest.java @@ -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); + } + +} diff --git a/jme3-core/src/test/java/com/jme3/math/FastMathTest.java b/jme3-core/src/test/java/com/jme3/math/FastMathTest.java index a74390d42..8156d9c3d 100644 --- a/jme3-core/src/test/java/com/jme3/math/FastMathTest.java +++ b/jme3-core/src/test/java/com/jme3/math/FastMathTest.java @@ -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; + } } diff --git a/jme3-core/src/test/java/com/jme3/renderer/OpaqueComparatorTest.java b/jme3-core/src/test/java/com/jme3/renderer/OpaqueComparatorTest.java new file mode 100644 index 000000000..84555c9f6 --- /dev/null +++ b/jme3-core/src/test/java/com/jme3/renderer/OpaqueComparatorTest.java @@ -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: + *
    C, B, A, C, B, A, C, B, A
    , it should result in + *
    A, A, A, B, B, B, C, C, C
    . + * + * @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 alreadySeen = new HashSet(); + 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); + } +} diff --git a/jme3-core/src/test/java/com/jme3/shader/DefineListTest.java b/jme3-core/src/test/java/com/jme3/shader/DefineListTest.java new file mode 100644 index 000000000..fc9148850 --- /dev/null +++ b/jme3-core/src/test/java/com/jme3/shader/DefineListTest.java @@ -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 defineNames; + private List 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); + } +} diff --git a/jme3-core/src/test/java/com/jme3/system/MockJmeSystemDelegate.java b/jme3-core/src/test/java/com/jme3/system/MockJmeSystemDelegate.java new file mode 100644 index 000000000..b9beac7f6 --- /dev/null +++ b/jme3-core/src/test/java/com/jme3/system/MockJmeSystemDelegate.java @@ -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) { + } + +} diff --git a/jme3-core/src/test/java/com/jme3/system/TestUtil.java b/jme3-core/src/test/java/com/jme3/system/TestUtil.java new file mode 100644 index 000000000..124b59ba7 --- /dev/null +++ b/jme3-core/src/test/java/com/jme3/system/TestUtil.java @@ -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()); + } +} diff --git a/jme3-core/src/tools/java/jme3tools/shadercheck/ShaderCheck.java b/jme3-core/src/tools/java/jme3tools/shadercheck/ShaderCheck.java index c77fa2b2e..d41dde064 100644 --- a/jme3-core/src/tools/java/jme3tools/shadercheck/ShaderCheck.java +++ b/jme3-core/src/tools/java/jme3tools/shadercheck/ShaderCheck.java @@ -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 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){ diff --git a/jme3-desktop/src/test/java/LibraryLoaderTest.java b/jme3-desktop/src/test/java/LibraryLoaderTest.java new file mode 100644 index 000000000..4b3833a8a --- /dev/null +++ b/jme3-desktop/src/test/java/LibraryLoaderTest.java @@ -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)); + } + +}