diff --git a/jme3-core/src/main/java/com/jme3/shader/ShaderVariable.java b/jme3-core/src/main/java/com/jme3/shader/ShaderVariable.java index 8a9bbaf266..0a7f9b5078 100644 --- a/jme3-core/src/main/java/com/jme3/shader/ShaderVariable.java +++ b/jme3-core/src/main/java/com/jme3/shader/ShaderVariable.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009-2012 jMonkeyEngine + * Copyright (c) 2009-2025 jMonkeyEngine * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -31,42 +31,69 @@ */ package com.jme3.shader; +/** + * Represents a generic variable within a shader program. + *

+ * This abstract class provides the fundamental properties shared by different + * types of shader variables, such as uniforms or attributes. + */ public class ShaderVariable { - public static final int LOC_UNKNOWN = -2, - LOC_NOT_DEFINED = -1; - - // if -2, location not known - // if -1, not defined in shader - // if >= 0, uniform defined and available. - protected int location = LOC_UNKNOWN; + public static final int LOC_UNKNOWN = -2; + public static final int LOC_NOT_DEFINED = -1; + /** + *

+ */ + protected int location = LOC_UNKNOWN; /** * Name of the uniform as was declared in the shader. * E.g. name = "g_WorldMatrix" if the declaration was * "uniform mat4 g_WorldMatrix;". */ protected String name = null; - /** * True if the shader value was changed. */ - protected boolean updateNeeded = true;; + protected boolean updateNeeded = true; - - public void setLocation(int location){ + /** + * Sets the location of this shader variable. + * + * @param location The integer location of the variable in the shader. + */ + public void setLocation(int location) { this.location = location; } - public int getLocation(){ + /** + * Returns the location of this shader variable. + * + * @return The integer location of the variable. + */ + public int getLocation() { return location; } - public void setName(String name){ + /** + * Sets the name of this shader variable as it's defined in the shader code. + * + * @param name The string name of the variable. + */ + public void setName(String name) { this.name = name; } - public String getName(){ + /** + * Returns the name of this shader variable. + * + * @return The string name of the variable. + */ + public String getName() { return name; } diff --git a/jme3-core/src/main/java/com/jme3/shader/Uniform.java b/jme3-core/src/main/java/com/jme3/shader/Uniform.java index ebd42f9605..6662ee681d 100644 --- a/jme3-core/src/main/java/com/jme3/shader/Uniform.java +++ b/jme3-core/src/main/java/com/jme3/shader/Uniform.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009-2024 jMonkeyEngine + * Copyright (c) 2009-2025 jMonkeyEngine * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -32,17 +32,33 @@ package com.jme3.shader; import com.jme3.material.Material.BindUnits; -import com.jme3.math.*; +import com.jme3.math.ColorRGBA; +import com.jme3.math.Matrix3f; +import com.jme3.math.Matrix4f; +import com.jme3.math.Quaternion; +import com.jme3.math.Vector2f; +import com.jme3.math.Vector3f; +import com.jme3.math.Vector4f; import com.jme3.util.BufferUtils; import com.jme3.util.TempVars; -import java.lang.reflect.InvocationTargetException; -import java.nio.*; +import java.nio.Buffer; +import java.nio.FloatBuffer; +import java.nio.IntBuffer; +import java.util.Objects; + +/** + * Represents a uniform variable in a shader program. + *

+ * A uniform is a way to pass data from the CPU to the GPU. This class manages + * the value of the uniform, its type, and its binding to renderer values. + */ public class Uniform extends ShaderVariable { private static final Integer ZERO_INT = 0; - private static final Float ZERO_FLT = Float.valueOf(0); - private static final FloatBuffer ZERO_BUF = BufferUtils.createFloatBuffer(4*4); + private static final Float ZERO_FLT = 0.0f; + // Pre-allocated buffer for clearing multiData to zeros efficiently. + private static final FloatBuffer ZERO_BUF = BufferUtils.createFloatBuffer(16); //Max 4x4 matrix elements /** * Currently set value of the uniform. @@ -73,80 +89,116 @@ public class Uniform extends ShaderVariable { @Override public int hashCode() { - int hash = 5; - hash = 31 * hash + (this.value != null ? this.value.hashCode() : 0); - hash = 31 * hash + (this.varType != null ? this.varType.hashCode() : 0); - hash = 31 * hash + (this.binding != null ? this.binding.hashCode() : 0); - return hash; + return Objects.hash(value, varType, binding); } @Override public boolean equals(Object obj) { + if (!(obj instanceof Uniform)) { + return false; + } + if (this == obj) { return true; } - if (obj == null) { - return false; - } + final Uniform other = (Uniform) obj; - if (this.value != other.value && (this.value == null || !this.value.equals(other.value))) { - return false; - } - return this.binding == other.binding && this.varType == other.varType; + return Objects.equals(this.value, other.value) && + this.binding == other.binding && + this.varType == other.varType; } @Override - public String toString(){ + public String toString() { StringBuilder sb = new StringBuilder(); sb.append("Uniform[name="); sb.append(name); - if (varType != null){ + if (varType != null) { sb.append(", type="); sb.append(varType); sb.append(", value="); sb.append(value); - }else{ + } else { sb.append(", value="); } sb.append("]"); return sb.toString(); } - public void setBinding(UniformBinding binding){ + /** + * Sets the {@link UniformBinding} for this uniform. + * + * @param binding The UniformBinding to set. + */ + public void setBinding(UniformBinding binding) { this.binding = binding; } - public UniformBinding getBinding(){ + /** + * Returns the {@link UniformBinding} associated with this uniform. + * + * @return The UniformBinding, or null if it's a user-defined uniform. + */ + public UniformBinding getBinding() { return binding; } + /** + * Returns the {@link VarType} of this uniform. + * + * @return The variable type of the uniform. + */ public VarType getVarType() { return varType; } - public Object getValue(){ + /** + * Returns the currently set value of the uniform. + * + * @return The value of the uniform. + */ + public Object getValue() { return value; } + /** + * Returns the {@link FloatBuffer} containing the multi-data for array or matrix uniforms. + * + * @return The FloatBuffer containing the uniform data, or null if not applicable. + */ public FloatBuffer getMultiData() { return multiData; } + /** + * Checks if this uniform's value was set by the current material. + * + * @return true if set by the current material, false otherwise. + */ public boolean isSetByCurrentMaterial() { return setByCurrentMaterial; } - public void clearSetByCurrentMaterial(){ + /** + * Clears the {@code setByCurrentMaterial} flag, indicating that the uniform + * was not set by the current material. This is typically called when + * a material is unbound. + */ + public void clearSetByCurrentMaterial() { setByCurrentMaterial = false; } - public void clearValue(){ + /** + * Clears the current value of the uniform, resetting it to a default + * "zero" or identity state based on its {@link VarType}. + */ + public void clearValue() { updateNeeded = true; - if (multiData != null){ + if (multiData != null) { multiData.clear(); - while (multiData.remaining() > 0){ + while (multiData.remaining() > 0) { ZERO_BUF.clear(); ZERO_BUF.limit(Math.min(multiData.remaining(), 16)); multiData.put(ZERO_BUF); @@ -161,34 +213,34 @@ public void clearValue(){ return; } - switch (varType){ + switch (varType) { case Int: - this.value = ZERO_INT; + value = ZERO_INT; break; case Boolean: - this.value = Boolean.FALSE; + value = Boolean.FALSE; break; case Float: - this.value = ZERO_FLT; + value = ZERO_FLT; break; case Vector2: - if (this.value != null) { - ((Vector2f) this.value).set(Vector2f.ZERO); + if (value != null) { + ((Vector2f) value).set(Vector2f.ZERO); } break; case Vector3: - if (this.value != null) { - ((Vector3f) this.value).set(Vector3f.ZERO); + if (value != null) { + ((Vector3f) value).set(Vector3f.ZERO); } break; case Vector4: - if (this.value != null) { - if (this.value instanceof ColorRGBA) { - ((ColorRGBA) this.value).set(ColorRGBA.BlackNoAlpha); - } else if (this.value instanceof Vector4f) { - ((Vector4f) this.value).set(Vector4f.ZERO); + if (value != null) { + if (value instanceof ColorRGBA) { + ((ColorRGBA) value).set(ColorRGBA.BlackNoAlpha); + } else if (value instanceof Vector4f) { + ((Vector4f) value).set(Vector4f.ZERO); } else { - ((Quaternion) this.value).set(Quaternion.ZERO); + ((Quaternion) value).set(Quaternion.ZERO); } } break; @@ -198,8 +250,15 @@ public void clearValue(){ } } - public void setValue(VarType type, Object value) { - assert !(value instanceof BindUnits); + /** + * Sets the value of the uniform. + * The type of the value must match the uniform's expected {@link VarType}. + * + * @param type The {@link VarType} of the value being set. + * @param uValue The new value for the uniform. Cannot be null. + */ + public void setValue(VarType type, Object uValue) { + assert !(uValue instanceof BindUnits); if (location == LOC_NOT_DEFINED) { return; } @@ -208,18 +267,18 @@ public void setValue(VarType type, Object value) { throw new IllegalArgumentException("Expected a " + varType.name() + " value!"); } - if (value == null) { + if (uValue == null) { throw new IllegalArgumentException("for uniform " + name + ": value cannot be null"); } setByCurrentMaterial = true; - switch (type){ + switch (type) { case Matrix3: - if (value.equals(this.value)) { + if (uValue.equals(this.value)) { return; } - Matrix3f m3 = (Matrix3f) value; + Matrix3f m3 = (Matrix3f) uValue; if (multiData == null) { multiData = BufferUtils.createFloatBuffer(9); } @@ -228,14 +287,14 @@ public void setValue(VarType type, Object value) { if (this.value == null) { this.value = new Matrix3f(m3); } else { - ((Matrix3f)this.value).set(m3); + ((Matrix3f) this.value).set(m3); } break; case Matrix4: - if (value.equals(this.value)) { + if (uValue.equals(this.value)) { return; } - Matrix4f m4 = (Matrix4f) value; + Matrix4f m4 = (Matrix4f) uValue; if (multiData == null) { multiData = BufferUtils.createFloatBuffer(16); } @@ -244,21 +303,21 @@ public void setValue(VarType type, Object value) { if (this.value == null) { this.value = new Matrix4f(m4); } else { - ((Matrix4f)this.value).copy(m4); + ((Matrix4f) this.value).copy(m4); } break; case IntArray: - int[] ia = (int[]) value; + int[] ia = (int[]) uValue; if (this.value == null) { this.value = BufferUtils.createIntBuffer(ia); } else { - this.value = BufferUtils.ensureLargeEnough((IntBuffer)this.value, ia.length); - ((IntBuffer)this.value).put(ia); + this.value = BufferUtils.ensureLargeEnough((IntBuffer) this.value, ia.length); + ((IntBuffer) this.value).put(ia); } - ((IntBuffer)this.value).clear(); + ((IntBuffer) this.value).clear(); break; case FloatArray: - float[] fa = (float[]) value; + float[] fa = (float[]) uValue; if (multiData == null) { multiData = BufferUtils.createFloatBuffer(fa); } else { @@ -268,7 +327,7 @@ public void setValue(VarType type, Object value) { multiData.clear(); break; case Vector2Array: - Vector2f[] v2a = (Vector2f[]) value; + Vector2f[] v2a = (Vector2f[]) uValue; if (multiData == null) { multiData = BufferUtils.createFloatBuffer(v2a); } else { @@ -280,7 +339,7 @@ public void setValue(VarType type, Object value) { multiData.clear(); break; case Vector3Array: - Vector3f[] v3a = (Vector3f[]) value; + Vector3f[] v3a = (Vector3f[]) uValue; if (multiData == null) { multiData = BufferUtils.createFloatBuffer(v3a); } else { @@ -292,7 +351,7 @@ public void setValue(VarType type, Object value) { multiData.clear(); break; case Vector4Array: - Vector4f[] v4a = (Vector4f[]) value; + Vector4f[] v4a = (Vector4f[]) uValue; if (multiData == null) { multiData = BufferUtils.createFloatBuffer(v4a); } else { @@ -304,7 +363,7 @@ public void setValue(VarType type, Object value) { multiData.clear(); break; case Matrix3Array: - Matrix3f[] m3a = (Matrix3f[]) value; + Matrix3f[] m3a = (Matrix3f[]) uValue; if (multiData == null) { multiData = BufferUtils.createFloatBuffer(m3a.length * 9); } else { @@ -316,7 +375,7 @@ public void setValue(VarType type, Object value) { multiData.clear(); break; case Matrix4Array: - Matrix4f[] m4a = (Matrix4f[]) value; + Matrix4f[] m4a = (Matrix4f[]) uValue; if (multiData == null) { multiData = BufferUtils.createFloatBuffer(m4a.length * 16); } else { @@ -328,50 +387,50 @@ public void setValue(VarType type, Object value) { multiData.clear(); break; case Vector2: - if (value.equals(this.value)) { + if (uValue.equals(this.value)) { return; } if (this.value == null) { - this.value = new Vector2f((Vector2f) value); + this.value = new Vector2f((Vector2f) uValue); } else { - ((Vector2f) this.value).set((Vector2f) value); + ((Vector2f) this.value).set((Vector2f) uValue); } break; case Vector3: - if (value.equals(this.value)) { + if (uValue.equals(this.value)) { return; } if (this.value == null) { - this.value = new Vector3f((Vector3f) value); + this.value = new Vector3f((Vector3f) uValue); } else { - ((Vector3f) this.value).set((Vector3f) value); + ((Vector3f) this.value).set((Vector3f) uValue); } break; case Vector4: - if (value.equals(this.value)) { + if (uValue.equals(this.value)) { return; } - TempVars vars = TempVars.get(); - Vector4f vec4 = vars.vect4f1; //handle the null case if (this.value == null) { try { - this.value = value.getClass().getDeclaredConstructor().newInstance(); - } catch (InstantiationException | IllegalAccessException - | IllegalArgumentException | InvocationTargetException - | NoSuchMethodException | SecurityException e) { - throw new IllegalArgumentException("Cannot instantiate param of class " + value.getClass().getCanonicalName(), e); + this.value = uValue.getClass().getDeclaredConstructor().newInstance(); + } catch (ReflectiveOperationException | IllegalArgumentException | SecurityException e) { + throw new IllegalArgumentException("Cannot instantiate param of class " + uValue.getClass().getCanonicalName(), e); } } + + TempVars vars = TempVars.get(); + Vector4f vec4 = vars.vect4f1; + //feed the pivot vec 4 with the correct value - if (value instanceof ColorRGBA) { - ColorRGBA c = (ColorRGBA) value; + if (uValue instanceof ColorRGBA) { + ColorRGBA c = (ColorRGBA) uValue; vec4.set(c.r, c.g, c.b, c.a); - } else if (value instanceof Vector4f) { - vec4.set((Vector4f) value); + } else if (uValue instanceof Vector4f) { + vec4.set((Vector4f) uValue); } else { - Quaternion q = (Quaternion) value; + Quaternion q = (Quaternion) uValue; vec4.set(q.getX(), q.getY(), q.getZ(), q.getW()); } @@ -385,17 +444,17 @@ public void setValue(VarType type, Object value) { } vars.release(); break; - // Only use check if equals optimization for primitive values + // Only use check if equals optimization for primitive values case Int: case Float: case Boolean: - if (value.equals(this.value)) { + if (uValue.equals(this.value)) { return; } - this.value = value; + this.value = uValue; break; default: - this.value = value; + this.value = uValue; break; } @@ -407,8 +466,14 @@ public void setValue(VarType type, Object value) { updateNeeded = true; } - public void setVector4Length(int length){ - if (location == -1) { + /** + * Pre-allocates or resizes the {@link FloatBuffer multiData} for a {@code Vector4Array} + * uniform to a specified length. + * + * @param length The desired number of Vector4 elements in the array. + */ + public void setVector4Length(int length) { + if (location == LOC_NOT_DEFINED) { return; } @@ -419,8 +484,20 @@ public void setVector4Length(int length){ setByCurrentMaterial = true; } - public void setVector4InArray(float x, float y, float z, float w, int index){ - if (location == -1) { + /** + * Sets the components of a specific {@code Vector4} element within a {@code Vector4Array} + * uniform's {@link FloatBuffer multiData}. + * This method requires the uniform to be of type {@code Vector4Array}. + * + * @param x The x-component of the Vector4. + * @param y The y-component of the Vector4. + * @param z The z-component of the Vector4. + * @param w The w-component of the Vector4. + * @param index The index of the Vector4 element to set within the array. + * @throws IllegalArgumentException if the uniform is not of type {@code Vector4Array}. + */ + public void setVector4InArray(float x, float y, float z, float w, int index) { + if (location == LOC_NOT_DEFINED) { return; } @@ -435,24 +512,47 @@ public void setVector4InArray(float x, float y, float z, float w, int index){ setByCurrentMaterial = true; } - public boolean isUpdateNeeded(){ + /** + * Checks if the uniform's value needs to be updated on the GPU. + * + * @return true if an update is needed, false otherwise. + */ + public boolean isUpdateNeeded() { return updateNeeded; } - public void clearUpdateNeeded(){ + /** + * Clears the {@code updateNeeded} flag, indicating that the uniform's value + * has been successfully sent to the GPU. + */ + public void clearUpdateNeeded() { updateNeeded = false; } - public void reset(){ + /** + * Resets the uniform's state. This includes clearing the + * {@code setByCurrentMaterial} flag, setting its location to undefined, + * and marking it as needing an update. This is typically used when a shader + * is unbound or reloaded. + */ + public void reset() { setByCurrentMaterial = false; - location = -2; + location = LOC_UNKNOWN; updateNeeded = true; } + /** + * Deletes any native direct buffers associated with this uniform's value. + * This is crucial for releasing native memory resources when the uniform + * is no longer needed. + */ public void deleteNativeBuffers() { if (value instanceof Buffer) { - BufferUtils.destroyDirectBuffer((Buffer)value); - value = null; // ???? + BufferUtils.destroyDirectBuffer((Buffer) value); + // It's important to nullify the reference after destroying the buffer + // to allow the Java garbage collector to reclaim the Uniform object + // itself if no other references exist. + value = null; } } }