From 501d2898b7fe9ff7684ba40d45c18dcba4d84c3b Mon Sep 17 00:00:00 2001 From: austin Date: Mon, 19 May 2025 11:14:54 -0400 Subject: [PATCH] visual shader work --- assets/Shaders/FragmentShader.fs | 2 +- docs/src/progress/renderertodo.md | 2 + .../java/electrosphere/engine/Globals.java | 2 +- .../electrosphere/renderer/RenderUtils.java | 8 +- .../renderer/meshgen/MeshLoader.java | 10 +- .../renderer/shader/ShaderUniform.java | 106 +++++++++ .../renderer/shader/VisualShader.java | 211 +++++------------- 7 files changed, 176 insertions(+), 165 deletions(-) create mode 100644 src/main/java/electrosphere/renderer/shader/ShaderUniform.java diff --git a/assets/Shaders/FragmentShader.fs b/assets/Shaders/FragmentShader.fs index d8c99fa6..4573f3f5 100644 --- a/assets/Shaders/FragmentShader.fs +++ b/assets/Shaders/FragmentShader.fs @@ -8,7 +8,7 @@ struct Material { sampler2D diffuse; sampler2D specular; float shininess; -}; +}; in vec3 FragPos; in vec3 ViewFragPos; diff --git a/docs/src/progress/renderertodo.md b/docs/src/progress/renderertodo.md index f9344657..4f5c7d7e 100644 --- a/docs/src/progress/renderertodo.md +++ b/docs/src/progress/renderertodo.md @@ -1895,6 +1895,8 @@ Renderer code cleanup (05/19/2025) Renderer code cleanup +Shader uniform parsing from source code +Visual shader uniform location caching diff --git a/src/main/java/electrosphere/engine/Globals.java b/src/main/java/electrosphere/engine/Globals.java index c1bc7128..68cb4caa 100644 --- a/src/main/java/electrosphere/engine/Globals.java +++ b/src/main/java/electrosphere/engine/Globals.java @@ -287,7 +287,7 @@ public class Globals { //initialize required windows WindowUtils.initBaseWindows(); //init default shaderProgram - defaultMeshShader = VisualShader.smartAssembleShader(false,true); + defaultMeshShader = VisualShader.smartAssembleShader(); //init terrain shader program terrainShaderProgram = VisualShader.loadSpecificShader("/Shaders/entities/terrain2/terrain2.vs", "/Shaders/entities/terrain2/terrain2.fs"); blockShader = VisualShader.loadSpecificShader("/Shaders/entities/block/block.vs", "/Shaders/entities/block/block.fs"); diff --git a/src/main/java/electrosphere/renderer/RenderUtils.java b/src/main/java/electrosphere/renderer/RenderUtils.java index 7b1c126d..3d924cba 100644 --- a/src/main/java/electrosphere/renderer/RenderUtils.java +++ b/src/main/java/electrosphere/renderer/RenderUtils.java @@ -208,7 +208,7 @@ public class RenderUtils { - VisualShader shader = VisualShader.smartAssembleOITProgram(false, true); + VisualShader shader = VisualShader.smartAssembleOITProgram(); particleMesh.setShader(shader); @@ -391,7 +391,7 @@ public class RenderUtils { Material mat = new Material(); mat.setDiffuse(AssetDataStrings.TEXTURE_TEAL_TRANSPARENT); sphereMesh.setMaterial(mat); - sphereMesh.setShader(VisualShader.smartAssembleShader(false, true)); + sphereMesh.setShader(VisualShader.smartAssembleShader()); GL40.glBindVertexArray(0); sphereMesh.setParent(model); model.getMeshes().add(sphereMesh); @@ -446,7 +446,7 @@ public class RenderUtils { Material mat = new Material(); mat.setDiffuse(AssetDataStrings.TEXTURE_TEAL_TRANSPARENT); sphereMesh.setMaterial(mat); - sphereMesh.setShader(VisualShader.smartAssembleShader(false, true)); + sphereMesh.setShader(VisualShader.smartAssembleShader()); GL40.glBindVertexArray(0); sphereMesh.setParent(model); model.getMeshes().add(sphereMesh); @@ -533,7 +533,7 @@ public class RenderUtils { Material mat = new Material(); mat.setDiffuse(AssetDataStrings.TEXTURE_TEAL_TRANSPARENT); sphereMesh.setMaterial(mat); - sphereMesh.setShader(VisualShader.smartAssembleShader(false, true)); + sphereMesh.setShader(VisualShader.smartAssembleShader()); GL40.glBindVertexArray(0); sphereMesh.setParent(model); model.getMeshes().add(sphereMesh); diff --git a/src/main/java/electrosphere/renderer/meshgen/MeshLoader.java b/src/main/java/electrosphere/renderer/meshgen/MeshLoader.java index cc7fd2fa..d4142e2e 100644 --- a/src/main/java/electrosphere/renderer/meshgen/MeshLoader.java +++ b/src/main/java/electrosphere/renderer/meshgen/MeshLoader.java @@ -29,9 +29,6 @@ import electrosphere.renderer.shader.VisualShader; public class MeshLoader { public static Mesh createMeshFromAIScene(AIMesh mesh, ModelPretransforms.MeshMetadata metadata){ - boolean has_bones = false; - boolean apply_lighting = true; - Mesh rVal = new Mesh(mesh.mName().dataString()); // @@ -223,7 +220,6 @@ public class MeshLoader { // PointerBuffer boneBuffer = mesh.mBones(); if(boneBuffer != null){ - has_bones = true; while(boneBuffer.hasRemaining()){ long currentAddr = boneBuffer.get(); AIBone currentBoneData = AIBone.createSafe(currentAddr); @@ -335,9 +331,9 @@ public class MeshLoader { if(!EngineState.EngineFlags.HEADLESS){ - rVal.setShader(VisualShader.smartAssembleShader(has_bones, apply_lighting)); - rVal.setShader(VisualShader.smartAssembleShader(has_bones, apply_lighting)); - rVal.setOITShader(VisualShader.smartAssembleOITProgram(has_bones, apply_lighting)); + rVal.setShader(VisualShader.smartAssembleShader()); + rVal.setShader(VisualShader.smartAssembleShader()); + rVal.setOITShader(VisualShader.smartAssembleOITProgram()); } diff --git a/src/main/java/electrosphere/renderer/shader/ShaderUniform.java b/src/main/java/electrosphere/renderer/shader/ShaderUniform.java new file mode 100644 index 00000000..21f9f309 --- /dev/null +++ b/src/main/java/electrosphere/renderer/shader/ShaderUniform.java @@ -0,0 +1,106 @@ +package electrosphere.renderer.shader; + +import java.util.LinkedList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * A uniform in a shader program + */ +public class ShaderUniform { + + /** + * Pattern for grabbing uniforms + */ + private static final Pattern uniformCapture = Pattern.compile("^\\s*uniform\\s+([a-zA-Z0-9]+)\\s+([a-zA-Z]+)\\s*;\\s*$", Pattern.MULTILINE); + + /** + * Constant for an unknown location + */ + public static final int LOCATION_NOT_KNOWN = -1; + + /** + * The name of the uniform + */ + private String name; + + /** + * The type of the uniform + */ + private String type; + + /** + * The location of this uniform + */ + private int location = ShaderUniform.LOCATION_NOT_KNOWN; + + /** + * Creates a shader uniform + * @param name The name of the uniform + * @param type The type of the uniform + */ + public ShaderUniform(String name, String type){ + this.name = name; + this.type = type; + } + + /** + * Gets the name of the uniform + * @return The name + */ + public String getName() { + return name; + } + + /** + * Gets the type of the uniform + * @return The type + */ + public String getType() { + return type; + } + + /** + * Gets the location of the uniform + * @return The location + */ + public int getLocation() { + return location; + } + + /** + * Sets the location of the uniform + * @param location The location + */ + public void setLocation(int location) { + this.location = location; + } + + /** + * Parses the uniforms from a source file's content + * @param sourceContent The source file's content + * @return The list of uniforms + */ + public static List parseUniforms(String sourceContent){ + List rVal = new LinkedList(); + Matcher matcher = uniformCapture.matcher(sourceContent); + while(matcher.find()){ + String uniformType = matcher.group(1); + String uniformName = matcher.group(2); + if(uniformType.equals("Material")){ + //special flow for materials + ShaderUniform newUniform; + newUniform = new ShaderUniform("material.diffuse", "sampler2D"); + rVal.add(newUniform); + newUniform = new ShaderUniform("material.specular", "sampler2D"); + rVal.add(newUniform); + } else { + ShaderUniform newUniform = new ShaderUniform(uniformName, uniformType); + rVal.add(newUniform); + } + } + return rVal; + } + +} diff --git a/src/main/java/electrosphere/renderer/shader/VisualShader.java b/src/main/java/electrosphere/renderer/shader/VisualShader.java index 722d2964..907b822b 100644 --- a/src/main/java/electrosphere/renderer/shader/VisualShader.java +++ b/src/main/java/electrosphere/renderer/shader/VisualShader.java @@ -29,24 +29,27 @@ public class VisualShader implements Shader { /** * The vertex shader location */ - int vertexShader; + private int vertexShader; /** * The fragment shader location */ - int fragmentShader; + private int fragmentShader; /** * The shader's ID */ - int shaderId; - + private int shaderId; + /** + * Map of uniform name -> data about the uniform + */ + private Map uniformNameMap = new HashMap(); /** * The map of uniform location -> current value of uniform */ - public Map uniformMap = new HashMap(); + public Map uniformValueMap = new HashMap(); /** * The map of path -> already compiled shader @@ -95,84 +98,20 @@ public class VisualShader implements Shader { /** * Smart assembles a shader - * @param ContainsBones true if the mesh contains bones - * @param apply_lighting true if lighting should be applied * @return The visual shader */ - public static VisualShader smartAssembleShader(boolean ContainsBones, boolean apply_lighting){ + public static VisualShader smartAssembleShader(){ //return shader if it has already been compiled - String shaderKey = ContainsBones + "-" + apply_lighting; - if(alreadyCompiledMap.containsKey(shaderKey)){ - return alreadyCompiledMap.get(shaderKey); + String vertShaderPath = "/Shaders/VertexShader.vs"; + String fragShaderPath = "/Shaders/FragmentShader.fs"; + String key = VisualShader.getShaderKey(vertShaderPath, fragShaderPath); + if(alreadyCompiledMap.containsKey(key)){ + return alreadyCompiledMap.get(key); } - - String vertex_shader_path = ""; - if(ContainsBones){ - vertex_shader_path = "/Shaders/VertexShader.vs"; - } else { - vertex_shader_path = "/Shaders/VertexShaderNoBones.vs"; - } - - String fragment_shader_path = "/Shaders/FragmentShader.fs"; - // - //Create ShaderProgram object - // - VisualShader rVal = new VisualShader(); - // - //Read in shader programs - // - String vertexShaderSource = VisualShader.recursivelyPreprocessFile(vertex_shader_path); - String fragmentShaderSource = VisualShader.recursivelyPreprocessFile(fragment_shader_path); - //Creates a new shader object and assigns its 'pointer' to the integer "vertexShader" - rVal.vertexShader = GL40.glCreateShader(GL40.GL_VERTEX_SHADER); - //This alerts openGL to the presence of a vertex shader and points the shader at its source - GL40.glShaderSource(rVal.vertexShader, vertexShaderSource); - //Compiles the source for the vertex shader object - GL40.glCompileShader(rVal.vertexShader); - //The following tests if the vertex shader compiles successfully - int success; - success = GL40.glGetShaderi(rVal.vertexShader, GL40.GL_COMPILE_STATUS); - if (success != GL40.GL_TRUE) { - LoggerInterface.loggerRenderer.WARNING("Vertex Shader failed to compile!"); - LoggerInterface.loggerRenderer.WARNING("Source is: "); - LoggerInterface.loggerRenderer.WARNING(GL40.glGetShaderSource(rVal.vertexShader)); - LoggerInterface.loggerRenderer.ERROR("Runtime Exception", new RuntimeException(GL40.glGetShaderInfoLog(rVal.vertexShader))); - } - //Creates and opengl object for a fragment shader and assigns its 'pointer' to the integer fragmentShader - rVal.fragmentShader = GL40.glCreateShader(GL40.GL_FRAGMENT_SHADER); - //This points the opengl shadder object to its proper source - GL40.glShaderSource(rVal.fragmentShader, fragmentShaderSource); - //This compiles the shader object - GL40.glCompileShader(rVal.fragmentShader); - //This tests for the success of the compile attempt - success = GL40.glGetShaderi(rVal.fragmentShader, GL40.GL_COMPILE_STATUS); - if (success != GL40.GL_TRUE) { - LoggerInterface.loggerRenderer.WARNING("Fragment Shader failed to compile!"); - LoggerInterface.loggerRenderer.WARNING("Source is: "); - LoggerInterface.loggerRenderer.WARNING(GL40.glGetShaderSource(rVal.fragmentShader)); - LoggerInterface.loggerRenderer.ERROR("Runtime Exception", new RuntimeException(GL40.glGetShaderInfoLog(rVal.fragmentShader))); - } - //This creates a shader program opengl object and assigns its 'pointer' to the integer shaderProgram - rVal.shaderId = GL40.glCreateProgram(); - //This attaches the vertex and fragment shaders to the program - GL40.glAttachShader(rVal.shaderId, rVal.vertexShader); - GL40.glAttachShader(rVal.shaderId, rVal.fragmentShader); - //This links the program to the GPU (I think its to the GPU anyway) - GL40.glLinkProgram(rVal.shaderId); - //Tests for the success of the shader program creation - success = GL40.glGetProgrami(rVal.shaderId, GL40.GL_LINK_STATUS); - if (success != GL40.GL_TRUE) { - throw new RuntimeException(GL40.glGetProgramInfoLog(rVal.shaderId)); - } - - //Deletes the individual shader objects to free up memory - GL40.glDeleteShader(rVal.vertexShader); - GL40.glDeleteShader(rVal.fragmentShader); - - - - alreadyCompiledMap.put(shaderKey,rVal); + + VisualShader rVal = VisualShader.loadSpecificShader(vertShaderPath, fragShaderPath); + alreadyCompiledMap.put(key,rVal); //check program status if(!GL45.glIsProgram(rVal.shaderId)){ @@ -189,80 +128,19 @@ public class VisualShader implements Shader { * @param apply_lighting True if should apply lighting * @return The int-pointer to the shader compiled */ - public static VisualShader smartAssembleOITProgram(boolean ContainsBones, boolean apply_lighting){ + public static VisualShader smartAssembleOITProgram(){ //return shader if it has already been compiled - String shaderKey = "oit" + ContainsBones + "-" + apply_lighting; - if(alreadyCompiledMap.containsKey(shaderKey)){ - return alreadyCompiledMap.get(shaderKey); + String vertShaderPath = "/Shaders/core/oit/general/VertexShader.vs"; + String fragShaderPath = "/Shaders/core/oit/general/FragmentShader.fs"; + String key = "oit" + VisualShader.getShaderKey(vertShaderPath, fragShaderPath); + if(alreadyCompiledMap.containsKey(key)){ + return alreadyCompiledMap.get(key); } - - String vertex_shader_path = ""; - if(ContainsBones){ - vertex_shader_path = "/Shaders/core/oit/general/VertexShader.vs"; - } else { - vertex_shader_path = "/Shaders/core/oit/general/VertexShaderNoBones.vs"; - } - - String fragment_shader_path = "/Shaders/core/oit/general/FragmentShader.fs"; - // - //Create ShaderProgram object - // - VisualShader rVal = new VisualShader(); - // - //Read in shader programs - // - String vertexShaderSource = VisualShader.recursivelyPreprocessFile(vertex_shader_path); - String fragmentShaderSource = VisualShader.recursivelyPreprocessFile(fragment_shader_path); - //Creates a new shader object and assigns its 'pointer' to the integer "vertexShader" - rVal.vertexShader = GL40.glCreateShader(GL40.GL_VERTEX_SHADER); - //This alerts openGL to the presence of a vertex shader and points the shader at its source - GL40.glShaderSource(rVal.vertexShader, vertexShaderSource); - //Compiles the source for the vertex shader object - GL40.glCompileShader(rVal.vertexShader); - //The following tests if the vertex shader compiles successfully - int success; - success = GL40.glGetShaderi(rVal.vertexShader, GL40.GL_COMPILE_STATUS); - if (success != GL40.GL_TRUE) { - LoggerInterface.loggerRenderer.WARNING("Vertex Shader failed to compile!"); - LoggerInterface.loggerRenderer.WARNING("Source is: "); - LoggerInterface.loggerRenderer.WARNING(GL40.glGetShaderSource(rVal.vertexShader)); - LoggerInterface.loggerRenderer.ERROR("Runtime Exception", new RuntimeException(GL40.glGetShaderInfoLog(rVal.vertexShader))); - } - //Creates and opengl object for a fragment shader and assigns its 'pointer' to the integer fragmentShader - rVal.fragmentShader = GL40.glCreateShader(GL40.GL_FRAGMENT_SHADER); - //This points the opengl shadder object to its proper source - GL40.glShaderSource(rVal.fragmentShader, fragmentShaderSource); - //This compiles the shader object - GL40.glCompileShader(rVal.fragmentShader); - //This tests for the success of the compile attempt - success = GL40.glGetShaderi(rVal.fragmentShader, GL40.GL_COMPILE_STATUS); - if (success != GL40.GL_TRUE) { - LoggerInterface.loggerRenderer.WARNING("Fragment Shader failed to compile!"); - LoggerInterface.loggerRenderer.WARNING("Source is: "); - LoggerInterface.loggerRenderer.WARNING(GL40.glGetShaderSource(rVal.fragmentShader)); - LoggerInterface.loggerRenderer.ERROR("Runtime Exception", new RuntimeException(GL40.glGetShaderInfoLog(rVal.fragmentShader))); - } - //This creates a shader program opengl object and assigns its 'pointer' to the integer shaderProgram - rVal.shaderId = GL40.glCreateProgram(); - //This attaches the vertex and fragment shaders to the program - GL40.glAttachShader(rVal.shaderId, rVal.vertexShader); - GL40.glAttachShader(rVal.shaderId, rVal.fragmentShader); - //This links the program to the GPU (I think its to the GPU anyway) - GL40. glLinkProgram(rVal.shaderId); - //Tests for the success of the shader program creation - success = GL40.glGetProgrami(rVal.shaderId, GL40.GL_LINK_STATUS); - if(success != GL40.GL_TRUE){ - throw new Error(GL40.glGetProgramInfoLog(rVal.shaderId)); - } - - //Deletes the individual shader objects to free up memory - GL40.glDeleteShader(rVal.vertexShader); - GL40.glDeleteShader(rVal.fragmentShader); - - - - alreadyCompiledMap.put(shaderKey,rVal); + + + VisualShader rVal = VisualShader.loadSpecificShader(vertShaderPath, fragShaderPath); + alreadyCompiledMap.put(key,rVal); //check program status rVal.validate(); @@ -425,6 +303,14 @@ public class VisualShader implements Shader { GL40.glDeleteShader(rVal.vertexShader); GL40.glDeleteShader(rVal.fragmentShader); Globals.renderingEngine.checkError(); + + //Parse all the uniformns from the source code + for(ShaderUniform uniform : ShaderUniform.parseUniforms(vertexShaderSource)){ + rVal.uniformNameMap.put(uniform.getName(),uniform); + } + for(ShaderUniform uniform : ShaderUniform.parseUniforms(fragmentShaderSource)){ + rVal.uniformNameMap.put(uniform.getName(),uniform); + } //check program status if(!GL45.glIsProgram(rVal.shaderId)){ @@ -444,10 +330,10 @@ public class VisualShader implements Shader { // //Error checking if(uniformName == null || uniformName.equals("")){ - throw new IllegalArgumentException("Trying to set invalid uniform name"); + throw new Error("Trying to set invalid uniform name"); } if(this.getId() != openGLState.getActiveShader().getId()){ - throw new IllegalStateException("Trying to set uniform on shader that is not active"); + throw new Error("Trying to set uniform on shader that is not active"); } // @@ -464,7 +350,7 @@ public class VisualShader implements Shader { if(uniformLocation == INVALID_UNIFORM_NAME){ LoggerInterface.loggerRenderer.DEBUG_LOOP("Searched for uniform in a shader that does not contain it. Uniform name: \"" + uniformName + "\""); } else { - ShaderUtils.setUniform(openGLState, this.uniformMap, uniformLocation, value); + ShaderUtils.setUniform(openGLState, this.uniformValueMap, uniformLocation, value); } } @@ -474,6 +360,17 @@ public class VisualShader implements Shader { * @return The location of the uniform */ public int getUniformLocation(String uniformName){ + if(this.uniformNameMap.containsKey(uniformName)){ + ShaderUniform uniform = this.uniformNameMap.get(uniformName); + if(uniform.getLocation() == ShaderUniform.LOCATION_NOT_KNOWN){ + int rVal = GL40.glGetUniformLocation(this.getId(), uniformName); + if(Globals.renderingEngine.checkError()){ + LoggerInterface.loggerRenderer.WARNING("Uniform failed with shader id " + this.getId()); + } + uniform.setLocation(rVal); + } + return uniform.getLocation(); + } int rVal = GL40.glGetUniformLocation(this.getId(), uniformName); if(Globals.renderingEngine.checkError()){ LoggerInterface.loggerRenderer.WARNING("Uniform failed with shader id " + this.getId()); @@ -481,6 +378,16 @@ public class VisualShader implements Shader { return rVal; } + /** + * Gets a shader key + * @param vertShader The vertex shader + * @param fragShader The fragment shader + * @return The key + */ + private static String getShaderKey(String vertShader, String fragShader){ + return vertShader + "_" + fragShader; + } + @Override public int getId() {