|
@@ -1,5 +1,5 @@
|
|
/*
|
|
/*
|
|
- * Copyright (c) 2009-2021 jMonkeyEngine
|
|
|
|
|
|
+ * Copyright (c) 2009-2024 jMonkeyEngine
|
|
* All rights reserved.
|
|
* All rights reserved.
|
|
*
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* Redistribution and use in source and binary forms, with or without
|
|
@@ -47,6 +47,7 @@ import com.jme3.renderer.TextureUnitException;
|
|
import com.jme3.renderer.queue.RenderQueue.Bucket;
|
|
import com.jme3.renderer.queue.RenderQueue.Bucket;
|
|
import com.jme3.scene.Geometry;
|
|
import com.jme3.scene.Geometry;
|
|
import com.jme3.shader.*;
|
|
import com.jme3.shader.*;
|
|
|
|
+import com.jme3.shader.bufferobject.BufferObject;
|
|
import com.jme3.texture.Image;
|
|
import com.jme3.texture.Image;
|
|
import com.jme3.texture.Texture;
|
|
import com.jme3.texture.Texture;
|
|
import com.jme3.texture.image.ColorSpace;
|
|
import com.jme3.texture.image.ColorSpace;
|
|
@@ -82,11 +83,21 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
|
|
private Technique technique;
|
|
private Technique technique;
|
|
private HashMap<String, Technique> techniques = new HashMap<>();
|
|
private HashMap<String, Technique> techniques = new HashMap<>();
|
|
private RenderState additionalState = null;
|
|
private RenderState additionalState = null;
|
|
- final private RenderState mergedRenderState = new RenderState();
|
|
|
|
|
|
+ private final RenderState mergedRenderState = new RenderState();
|
|
private boolean transparent = false;
|
|
private boolean transparent = false;
|
|
private boolean receivesShadows = false;
|
|
private boolean receivesShadows = false;
|
|
private int sortingId = -1;
|
|
private int sortingId = -1;
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
+ * Track bind ids for textures and buffers
|
|
|
|
+ * Used internally
|
|
|
|
+ */
|
|
|
|
+ public static class BindUnits {
|
|
|
|
+ public int textureUnit = 0;
|
|
|
|
+ public int bufferUnit = 0;
|
|
|
|
+ }
|
|
|
|
+ private BindUnits bindUnits = new BindUnits();
|
|
|
|
+
|
|
public Material(MaterialDef def) {
|
|
public Material(MaterialDef def) {
|
|
if (def == null) {
|
|
if (def == null) {
|
|
throw new IllegalArgumentException("Material definition cannot be null");
|
|
throw new IllegalArgumentException("Material definition cannot be null");
|
|
@@ -506,6 +517,18 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
+ * Pass a parameter to the material shader.
|
|
|
|
+ *
|
|
|
|
+ * @param name the name of the parameter defined in the material definition (j3md)
|
|
|
|
+ * @param value the value of the parameter
|
|
|
|
+ */
|
|
|
|
+ public void setParam(String name, Object value) {
|
|
|
|
+ MatParam p = getMaterialDef().getMaterialParam(name);
|
|
|
|
+ setParam(name, p.getVarType(), value);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+
|
|
/**
|
|
/**
|
|
* Clear a parameter from this material. The parameter must exist
|
|
* Clear a parameter from this material. The parameter must exist
|
|
* @param name the name of the parameter to clear
|
|
* @param name the name of the parameter to clear
|
|
@@ -685,8 +708,7 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
|
|
* @param value the buffer object.
|
|
* @param value the buffer object.
|
|
*/
|
|
*/
|
|
public void setUniformBufferObject(final String name, final BufferObject value) {
|
|
public void setUniformBufferObject(final String name, final BufferObject value) {
|
|
- value.setBufferType(BufferObject.BufferType.UniformBufferObject);
|
|
|
|
- setParam(name, VarType.BufferObject, value);
|
|
|
|
|
|
+ setParam(name, VarType.UniformBufferObject, value);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -696,8 +718,7 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
|
|
* @param value the buffer object.
|
|
* @param value the buffer object.
|
|
*/
|
|
*/
|
|
public void setShaderStorageBufferObject(final String name, final BufferObject value) {
|
|
public void setShaderStorageBufferObject(final String name, final BufferObject value) {
|
|
- value.setBufferType(BufferObject.BufferType.ShaderStorageBufferObject);
|
|
|
|
- setParam(name, VarType.BufferObject, value);
|
|
|
|
|
|
+ setParam(name, VarType.ShaderStorageBufferObject, value);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -797,7 +818,7 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
|
|
sortingId = -1;
|
|
sortingId = -1;
|
|
}
|
|
}
|
|
|
|
|
|
- private int applyOverrides(Renderer renderer, Shader shader, SafeArrayList<MatParamOverride> overrides, int unit) {
|
|
|
|
|
|
+ private void applyOverrides(Renderer renderer, Shader shader, SafeArrayList<MatParamOverride> overrides, BindUnits bindUnits) {
|
|
for (MatParamOverride override : overrides.getArray()) {
|
|
for (MatParamOverride override : overrides.getArray()) {
|
|
VarType type = override.getVarType();
|
|
VarType type = override.getVarType();
|
|
|
|
|
|
@@ -810,36 +831,64 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
|
|
Uniform uniform = shader.getUniform(override.getPrefixedName());
|
|
Uniform uniform = shader.getUniform(override.getPrefixedName());
|
|
|
|
|
|
if (override.getValue() != null) {
|
|
if (override.getValue() != null) {
|
|
- if (type.isTextureType()) {
|
|
|
|
- try {
|
|
|
|
- renderer.setTexture(unit, (Texture) override.getValue());
|
|
|
|
- } catch (TextureUnitException exception) {
|
|
|
|
- int numTexParams = unit + 1;
|
|
|
|
- String message = "Too many texture parameters ("
|
|
|
|
- + numTexParams + ") assigned\n to " + toString();
|
|
|
|
- throw new IllegalStateException(message);
|
|
|
|
- }
|
|
|
|
- uniform.setValue(VarType.Int, unit);
|
|
|
|
- unit++;
|
|
|
|
- } else {
|
|
|
|
- uniform.setValue(type, override.getValue());
|
|
|
|
- }
|
|
|
|
|
|
+ updateShaderMaterialParameter(renderer, type, shader, override, bindUnits, true);
|
|
} else {
|
|
} else {
|
|
uniform.clearValue();
|
|
uniform.clearValue();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
- return unit;
|
|
|
|
}
|
|
}
|
|
|
|
|
|
- private int updateShaderMaterialParameters(Renderer renderer, Shader shader,
|
|
|
|
- SafeArrayList<MatParamOverride> worldOverrides, SafeArrayList<MatParamOverride> forcedOverrides) {
|
|
|
|
|
|
|
|
- int unit = 0;
|
|
|
|
|
|
+ private void updateShaderMaterialParameter(Renderer renderer, VarType type, Shader shader, MatParam param, BindUnits unit, boolean override) {
|
|
|
|
+ if (type == VarType.UniformBufferObject || type == VarType.ShaderStorageBufferObject) {
|
|
|
|
+ ShaderBufferBlock bufferBlock = shader.getBufferBlock(param.getPrefixedName());
|
|
|
|
+ BufferObject bufferObject = (BufferObject) param.getValue();
|
|
|
|
+
|
|
|
|
+ ShaderBufferBlock.BufferType btype;
|
|
|
|
+ if (type == VarType.ShaderStorageBufferObject) {
|
|
|
|
+ btype = ShaderBufferBlock.BufferType.ShaderStorageBufferObject;
|
|
|
|
+ bufferBlock.setBufferObject(btype, bufferObject);
|
|
|
|
+ renderer.setShaderStorageBufferObject(unit.bufferUnit, bufferObject); // TODO: probably not needed
|
|
|
|
+ } else {
|
|
|
|
+ btype = ShaderBufferBlock.BufferType.UniformBufferObject;
|
|
|
|
+ bufferBlock.setBufferObject(btype, bufferObject);
|
|
|
|
+ renderer.setUniformBufferObject(unit.bufferUnit, bufferObject); // TODO: probably not needed
|
|
|
|
+ }
|
|
|
|
+ unit.bufferUnit++;
|
|
|
|
+ } else {
|
|
|
|
+ Uniform uniform = shader.getUniform(param.getPrefixedName());
|
|
|
|
+ if (!override && uniform.isSetByCurrentMaterial()) return;
|
|
|
|
+
|
|
|
|
+ if (type.isTextureType()) {
|
|
|
|
+ try {
|
|
|
|
+ renderer.setTexture(unit.textureUnit, (Texture) param.getValue());
|
|
|
|
+ } catch (TextureUnitException exception) {
|
|
|
|
+ int numTexParams = unit.textureUnit + 1;
|
|
|
|
+ String message = "Too many texture parameters (" + numTexParams + ") assigned\n to " + toString();
|
|
|
|
+ throw new IllegalStateException(message);
|
|
|
|
+ }
|
|
|
|
+ uniform.setValue(VarType.Int, unit.textureUnit);
|
|
|
|
+ unit.textureUnit++;
|
|
|
|
+ } else {
|
|
|
|
+ uniform.setValue(type, param.getValue());
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ private BindUnits updateShaderMaterialParameters(Renderer renderer, Shader shader, SafeArrayList<MatParamOverride> worldOverrides,
|
|
|
|
+ SafeArrayList<MatParamOverride> forcedOverrides) {
|
|
|
|
+
|
|
|
|
+ bindUnits.textureUnit = 0;
|
|
|
|
+ bindUnits.bufferUnit = 0;
|
|
|
|
+
|
|
if (worldOverrides != null) {
|
|
if (worldOverrides != null) {
|
|
- unit = applyOverrides(renderer, shader, worldOverrides, unit);
|
|
|
|
|
|
+ applyOverrides(renderer, shader, worldOverrides, bindUnits);
|
|
}
|
|
}
|
|
if (forcedOverrides != null) {
|
|
if (forcedOverrides != null) {
|
|
- unit = applyOverrides(renderer, shader, forcedOverrides, unit);
|
|
|
|
|
|
+ applyOverrides(renderer, shader, forcedOverrides, bindUnits);
|
|
}
|
|
}
|
|
|
|
|
|
for (int i = 0; i < paramValues.size(); i++) {
|
|
for (int i = 0; i < paramValues.size(); i++) {
|
|
@@ -847,66 +896,34 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
|
|
MatParam param = paramValues.getValue(i);
|
|
MatParam param = paramValues.getValue(i);
|
|
VarType type = param.getVarType();
|
|
VarType type = param.getVarType();
|
|
|
|
|
|
- if (isBO(type)) {
|
|
|
|
-
|
|
|
|
- final ShaderBufferBlock bufferBlock = shader.getBufferBlock(param.getPrefixedName());
|
|
|
|
- bufferBlock.setBufferObject((BufferObject) param.getValue());
|
|
|
|
-
|
|
|
|
- } else {
|
|
|
|
-
|
|
|
|
- Uniform uniform = shader.getUniform(param.getPrefixedName());
|
|
|
|
- if (uniform.isSetByCurrentMaterial()) {
|
|
|
|
- continue;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (type.isTextureType()) {
|
|
|
|
- try {
|
|
|
|
- renderer.setTexture(unit, (Texture) param.getValue());
|
|
|
|
- } catch (TextureUnitException exception) {
|
|
|
|
- int numTexParams = unit + 1;
|
|
|
|
- String message = "Too many texture parameters ("
|
|
|
|
- + numTexParams + ") assigned\n to " + toString();
|
|
|
|
- throw new IllegalStateException(message);
|
|
|
|
- }
|
|
|
|
- uniform.setValue(VarType.Int, unit);
|
|
|
|
- unit++;
|
|
|
|
- } else {
|
|
|
|
- uniform.setValue(type, param.getValue());
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
|
|
+ updateShaderMaterialParameter(renderer, type, shader, param, bindUnits, false);
|
|
}
|
|
}
|
|
|
|
|
|
- //TODO HACKY HACK remove this when texture unit is handled by the uniform.
|
|
|
|
- return unit;
|
|
|
|
|
|
+ // TODO HACKY HACK remove this when texture unit is handled by the
|
|
|
|
+ // uniform.
|
|
|
|
+ return bindUnits;
|
|
}
|
|
}
|
|
|
|
|
|
- /**
|
|
|
|
- * Returns true if the type is Buffer Object's type.
|
|
|
|
- *
|
|
|
|
- * @param type the material parameter type.
|
|
|
|
- * @return true if the type is Buffer Object's type.
|
|
|
|
- */
|
|
|
|
- private boolean isBO(final VarType type) {
|
|
|
|
- return type == VarType.BufferObject;
|
|
|
|
- }
|
|
|
|
|
|
+
|
|
|
|
|
|
private void updateRenderState(Geometry geometry, RenderManager renderManager, Renderer renderer, TechniqueDef techniqueDef) {
|
|
private void updateRenderState(Geometry geometry, RenderManager renderManager, Renderer renderer, TechniqueDef techniqueDef) {
|
|
|
|
+ RenderState finalRenderState;
|
|
if (renderManager.getForcedRenderState() != null) {
|
|
if (renderManager.getForcedRenderState() != null) {
|
|
- mergedRenderState.copyFrom(renderManager.getForcedRenderState());
|
|
|
|
|
|
+ finalRenderState = mergedRenderState.copyFrom(renderManager.getForcedRenderState());
|
|
} else if (techniqueDef.getRenderState() != null) {
|
|
} else if (techniqueDef.getRenderState() != null) {
|
|
- mergedRenderState.copyFrom(RenderState.DEFAULT);
|
|
|
|
- techniqueDef.getRenderState().copyMergedTo(additionalState, mergedRenderState);
|
|
|
|
|
|
+ finalRenderState = mergedRenderState.copyFrom(RenderState.DEFAULT);
|
|
|
|
+ finalRenderState = techniqueDef.getRenderState().copyMergedTo(additionalState, finalRenderState);
|
|
} else {
|
|
} else {
|
|
- mergedRenderState.copyFrom(RenderState.DEFAULT);
|
|
|
|
- RenderState.DEFAULT.copyMergedTo(additionalState, mergedRenderState);
|
|
|
|
|
|
+ finalRenderState = mergedRenderState.copyFrom(RenderState.DEFAULT);
|
|
|
|
+ finalRenderState = RenderState.DEFAULT.copyMergedTo(additionalState, finalRenderState);
|
|
}
|
|
}
|
|
// test if the face cull mode should be flipped before render
|
|
// test if the face cull mode should be flipped before render
|
|
- if (mergedRenderState.isFaceCullFlippable() && isNormalsBackward(geometry.getWorldScale())) {
|
|
|
|
- mergedRenderState.flipFaceCull();
|
|
|
|
|
|
+ if (finalRenderState.isFaceCullFlippable() && isNormalsBackward(geometry.getWorldScale())) {
|
|
|
|
+ finalRenderState.flipFaceCull();
|
|
}
|
|
}
|
|
- renderer.applyRenderState(mergedRenderState);
|
|
|
|
|
|
+ renderer.applyRenderState(finalRenderState);
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
/**
|
|
/**
|
|
* Returns true if the geometry world scale indicates that normals will be backward.
|
|
* Returns true if the geometry world scale indicates that normals will be backward.
|
|
* @param scalar geometry world scale
|
|
* @param scalar geometry world scale
|
|
@@ -1064,13 +1081,13 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
|
|
renderManager.updateUniformBindings(shader);
|
|
renderManager.updateUniformBindings(shader);
|
|
|
|
|
|
// Set material parameters
|
|
// Set material parameters
|
|
- int unit = updateShaderMaterialParameters(renderer, shader, overrides, renderManager.getForcedMatParams());
|
|
|
|
|
|
+ BindUnits units = updateShaderMaterialParameters(renderer, shader, overrides, renderManager.getForcedMatParams());
|
|
|
|
|
|
// Clear any uniforms not changed by material.
|
|
// Clear any uniforms not changed by material.
|
|
resetUniformsNotSetByCurrent(shader);
|
|
resetUniformsNotSetByCurrent(shader);
|
|
|
|
|
|
// Delegate rendering to the technique
|
|
// Delegate rendering to the technique
|
|
- technique.render(renderManager, shader, geometry, lights, unit);
|
|
|
|
|
|
+ technique.render(renderManager, shader, geometry, lights, units);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|