diff --git a/assets/Data/game/blockTypes.json b/assets/Data/game/blockTypes.json index dc98d10f..cbac7ec1 100644 --- a/assets/Data/game/blockTypes.json +++ b/assets/Data/game/blockTypes.json @@ -8,6 +8,11 @@ "id" : 1, "name" : "brick", "texture" : "/Textures/Ground/Dirt2_256.png" + }, + { + "id" : 2, + "name" : "grass", + "texture" : "/Textures/Ground/GrassTileable256.png" } ] } \ No newline at end of file diff --git a/assets/Shaders/entities/blocksingle/block.fs b/assets/Shaders/entities/blocksingle/block.fs new file mode 100644 index 00000000..9906fadb --- /dev/null +++ b/assets/Shaders/entities/blocksingle/block.fs @@ -0,0 +1,96 @@ +#version 450 core +#extension GL_ARB_shading_language_include : require +#include "../../lib/lights.fs" + +//texture defines +#define ATLAS_ELEMENT_DIM 256.0 +#define ATLAS_DIM 8192.0 +#define ATLAS_EL_PER_ROW 32 +#define ATLAS_NORMALIZED_ELEMENT_WIDTH 0.031 //within the single texture within the atlas, we use this so we never go over the end of the texture +#define ATLAS_NORMALIZED_ELEMENT_WIDTH_FULL 0.03125 //used to properly shift from texture to texture in the atlas + + +struct Material { + sampler2D diffuse; + sampler2D specular; + float shininess; +}; + +in vec3 FragPos; +in vec3 ViewFragPos; +in vec3 Normal; +in vec2 uv; +in vec4 FragPosLightSpace; + + +uniform int blockAtlasIndex; //index of the block type in the texture atlas +uniform vec3 viewPos; +uniform Material material; + +/** +Used for light cluster calculation +*/ +uniform mat4 view; + +/** +The output +*/ +out vec4 FragColor; + + +// function prototypes +vec3 getColor(vec2 uv, vec3 normal, int blockIndex, Material material); + +void main(){ + vec3 norm = normalize(Normal); + vec3 viewDir = normalize(viewPos - FragPos); + + //grab light intensity + vec3 lightIntensity = vec3(calcLightIntensityTotal(norm)); + + //get color of base texture + vec3 textureColor = getColor(uv, norm, blockAtlasIndex, material); + + //shadow + float shadow = ShadowCalculation(FragPosLightSpace, normalize(-directLight.direction), -norm); + + // + //point light calculations + uint clusterIndex = findCluster(ViewFragPos, zNear, zFar); + uint pointLightCount = clusters[clusterIndex].count; + for(int i = 0; i < pointLightCount; i++){ + uint pointLightIndex = clusters[clusterIndex].lightIndices[i]; + PointLight pointLight = pointLight[pointLightIndex]; + lightIntensity = lightIntensity + CalcPointLight(pointLight, norm, FragPos, viewDir); + } + //error checking on light clusters + if(pointLightCount > MAX_LIGHTS_PER_CLUSTER){ + FragColor = vec4(1.0f,0.0f,0.0f,1); + return; + } + + //calculate final color + vec3 finalColor = textureColor * lightIntensity * max(shadow,0.4); + + //this final calculation is for transparency + FragColor = vec4(finalColor, 1); +} + + +/** + * The function that gets the texture color based on the triplanar texture mapping and the voxel type at each point along the vert. + * See the triplanar mapping wiki article for an explanation of math involved. + */ +vec3 getColor(vec2 uv, vec3 normal, int blockIndex, Material material){ + + //the uv of the texture clamped within the atlas + vec2 actualUv = vec2( + (fract(uv.x) * ATLAS_NORMALIZED_ELEMENT_WIDTH) + (mod(blockIndex,ATLAS_EL_PER_ROW) * ATLAS_NORMALIZED_ELEMENT_WIDTH_FULL), + (fract(uv.y) * ATLAS_NORMALIZED_ELEMENT_WIDTH) + (round(blockIndex / ATLAS_EL_PER_ROW) * ATLAS_NORMALIZED_ELEMENT_WIDTH_FULL) + ); + //albedo for the X texture + vec3 color = texture(material.diffuse, actualUv).rgb; + + + return color; +} \ No newline at end of file diff --git a/assets/Shaders/entities/blocksingle/block.vs b/assets/Shaders/entities/blocksingle/block.vs new file mode 100644 index 00000000..49244d7d --- /dev/null +++ b/assets/Shaders/entities/blocksingle/block.vs @@ -0,0 +1,53 @@ +//Vertex Shader +#version 330 core + +//defines +#define TEXTURE_MAP_SCALE 1.0 +#define MODEL_TOTAL_DIM 16.0 + + +//input buffers +layout (location = 0) in vec3 aPos; +layout (location = 1) in vec3 aNormal; +layout (location = 4) in vec2 aTex; + + +//coordinate space transformation matrices +uniform mat4 transform; +uniform mat4 model; +uniform mat4 view; +uniform mat4 projection; +uniform mat4 lightSpaceMatrix; + + + +//output buffers +out vec3 Normal; +out vec3 FragPos; +out vec3 ViewFragPos; +out vec2 uv; +out vec4 FragPosLightSpace; + + + + +void main() { + //normalize posiiton and normal + vec4 FinalVertex = vec4(aPos, 1.0); + vec4 FinalNormal = vec4(aNormal, 1.0); + + + //push frag, normal, and texture positions to fragment shader + FragPos = vec3(model * FinalVertex); + ViewFragPos = vec3(view * model * FinalVertex); + Normal = mat3(transpose(inverse(model))) * aNormal; + uv = aTex; + + + //shadow map stuff + FragPosLightSpace = lightSpaceMatrix * vec4(FragPos, 1.0); + + + //set final position with opengl space + gl_Position = projection * view * model * FinalVertex; +} diff --git a/docs/src/progress/renderertodo.md b/docs/src/progress/renderertodo.md index 02310f03..6f3f8001 100644 --- a/docs/src/progress/renderertodo.md +++ b/docs/src/progress/renderertodo.md @@ -1312,6 +1312,9 @@ Update flush method in tests to account for faster loading Allow pre-setting queued asset path Fix asset manager texture queueing (was not pushing queued textures into loaded texture map) Model for single block (hooked up to item as well) +Single block model custom shader +Data-defined actor per-mesh uniform overrides +Proper textures on each block item diff --git a/src/main/java/electrosphere/engine/assetmanager/AssetDataStrings.java b/src/main/java/electrosphere/engine/assetmanager/AssetDataStrings.java index 0715b568..64ad467b 100644 --- a/src/main/java/electrosphere/engine/assetmanager/AssetDataStrings.java +++ b/src/main/java/electrosphere/engine/assetmanager/AssetDataStrings.java @@ -14,6 +14,8 @@ public class AssetDataStrings { */ public static final String SHADER_DEFAULT_VERT = "Shaders/VertexShader.vs"; public static final String SHADER_DEFAULT_FRAG = "Shaders/FragmentShader.fs"; + public static final String SHADER_BLOCK_SINGLE_VERT = "Shaders/entities/blocksingle/block.vs"; + public static final String SHADER_BLOCK_SINGLE_FRAG = "Shaders/entities/blocksingle/block.fs"; /** * The basic geometry of the engine diff --git a/src/main/java/electrosphere/engine/loadingthreads/InitialAssetLoading.java b/src/main/java/electrosphere/engine/loadingthreads/InitialAssetLoading.java index ec685e9b..b6e7146e 100644 --- a/src/main/java/electrosphere/engine/loadingthreads/InitialAssetLoading.java +++ b/src/main/java/electrosphere/engine/loadingthreads/InitialAssetLoading.java @@ -16,9 +16,11 @@ import electrosphere.engine.assetmanager.AssetDataStrings; import electrosphere.engine.assetmanager.queue.QueuedTexture; import electrosphere.game.data.block.BlockData; import electrosphere.game.data.block.BlockType; +import electrosphere.game.data.item.Item; import electrosphere.game.data.voxel.VoxelData; import electrosphere.game.data.voxel.VoxelType; import electrosphere.logger.LoggerInterface; +import electrosphere.renderer.RenderUtils; import electrosphere.renderer.texture.TextureAtlas; import electrosphere.util.FileUtils; @@ -123,6 +125,13 @@ public class InitialAssetLoading { //put coords in map Globals.blockTextureAtlas.putTypeCoord(type.getId(),iterator); + //once the atlas has been created, need to check if items have already been created + //if items have been created, must update the hard-coded shader values to reflect the actual atlas values + if(Globals.gameConfigCurrent.getItemMap().getItem(type.getName()) != null){ + Item item = Globals.gameConfigCurrent.getItemMap().getItem(type.getName()); + item.getGraphicsTemplate().getModel().getUniforms().get(RenderUtils.MESH_NAME_BLOCK_SINGLE).put("blockAtlasIndex",iterator); + } + //iterate iterator++; } diff --git a/src/main/java/electrosphere/entity/types/common/CommonEntityUtils.java b/src/main/java/electrosphere/entity/types/common/CommonEntityUtils.java index 96a99506..77522876 100644 --- a/src/main/java/electrosphere/entity/types/common/CommonEntityUtils.java +++ b/src/main/java/electrosphere/entity/types/common/CommonEntityUtils.java @@ -1,5 +1,8 @@ package electrosphere.entity.types.common; +import java.util.Map; +import java.util.Set; + import org.joml.Quaterniond; import org.joml.Vector3d; import org.ode4j.ode.DBody; @@ -158,9 +161,24 @@ public class CommonEntityUtils { if(graphicsTemplate.getModel() != null && graphicsTemplate.getModel().getIdleData() != null){ ClientIdleTree.attachTree(entity, graphicsTemplate.getModel().getIdleData()); } + if(graphicsTemplate.getModel() != null && graphicsTemplate.getModel().getUniforms() != null){ + Actor creatureActor = EntityUtils.getActor(entity); + Map> meshUniformMap = graphicsTemplate.getModel().getUniforms(); + Set meshNames = meshUniformMap.keySet(); + for(String meshName : meshNames){ + Map uniforms = meshUniformMap.get(meshName); + Set uniformNames = uniforms.keySet(); + for(String uniformName : uniformNames){ + Object value = uniforms.get(uniformName); + creatureActor.setUniformOnMesh(meshName, uniformName, value); + } + } + } } Actor creatureActor = EntityUtils.getActor(entity); + //apply uniforms if they are pre-set + /// /// /// HITBOX DATA diff --git a/src/main/java/electrosphere/game/data/graphics/NonproceduralModel.java b/src/main/java/electrosphere/game/data/graphics/NonproceduralModel.java index 968f4931..1ac3e472 100644 --- a/src/main/java/electrosphere/game/data/graphics/NonproceduralModel.java +++ b/src/main/java/electrosphere/game/data/graphics/NonproceduralModel.java @@ -1,5 +1,7 @@ package electrosphere.game.data.graphics; +import java.util.Map; + import electrosphere.game.data.creature.type.IdleData; /** @@ -17,22 +19,59 @@ public class NonproceduralModel { */ IdleData idleData; + /** + * Uniform values set ahead of time + */ + Map> uniforms; + + /** + * Gets the path of the model + * @return The path of the model + */ public String getPath() { return path; } + /** + * Sets the path of the model + * @param path The path of the model + */ public void setPath(String path) { this.path = path; } + /** + * Gets the idle data of the model + * @return The idle data of the model + */ public IdleData getIdleData() { return idleData; } + /** + * Sets the idle data of the model + * @param idleData The idle data of the model + */ public void setIdleData(IdleData idleData) { this.idleData = idleData; } + /** + * Gets the uniforms set on the model + * @return The map of uniform name -> uniform value, or null if no uniforms are set + */ + public Map> getUniforms(){ + return uniforms; + } + + /** + * Sets the uniform map for the model + * @param uniforms The map of uniform name -> uniform value + */ + public void setUniforms(Map> uniforms){ + this.uniforms = uniforms; + } + } diff --git a/src/main/java/electrosphere/game/data/item/Item.java b/src/main/java/electrosphere/game/data/item/Item.java index 64d649a9..1f0bdbca 100644 --- a/src/main/java/electrosphere/game/data/item/Item.java +++ b/src/main/java/electrosphere/game/data/item/Item.java @@ -1,13 +1,17 @@ package electrosphere.game.data.item; import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; +import electrosphere.engine.Globals; import electrosphere.engine.assetmanager.AssetDataStrings; import electrosphere.game.data.block.BlockType; import electrosphere.game.data.common.CommonEntityType; import electrosphere.game.data.common.item.SpawnItemDescription; import electrosphere.game.data.graphics.GraphicsTemplate; import electrosphere.game.data.graphics.NonproceduralModel; +import electrosphere.renderer.RenderUtils; /** * Data on a given item @@ -127,6 +131,13 @@ public class Item extends CommonEntityType { blockItemGraphicsTemplate.setModel(modelData); rVal.setGraphicsTemplate(blockItemGraphicsTemplate); + //set uniforms for the model + Map> meshUniformMap = new HashMap>(); + Map uniforms = new HashMap(); + uniforms.put("blockAtlasIndex",Globals.blockTextureAtlas.getVoxelTypeOffset(blockType.getId())); + meshUniformMap.put(RenderUtils.MESH_NAME_BLOCK_SINGLE,uniforms); + modelData.setUniforms(meshUniformMap); + //set usage ItemUsage usage = new ItemUsage(); diff --git a/src/main/java/electrosphere/renderer/RenderUtils.java b/src/main/java/electrosphere/renderer/RenderUtils.java index ed07de9d..bcd82039 100644 --- a/src/main/java/electrosphere/renderer/RenderUtils.java +++ b/src/main/java/electrosphere/renderer/RenderUtils.java @@ -27,6 +27,11 @@ import org.lwjgl.util.par.ParShapesMesh; * Utilities to assist with rendering */ public class RenderUtils { + + /** + * Name of the mesh for the single block model + */ + public static final String MESH_NAME_BLOCK_SINGLE = "cube"; @@ -542,7 +547,7 @@ public class RenderUtils { */ public static Model createBlockSingleModel(){ Model model = new Model(); - Mesh cubeMesh = new Mesh("cube"); + Mesh cubeMesh = new Mesh(RenderUtils.MESH_NAME_BLOCK_SINGLE); cubeMesh.generateVAO(); //buffer coords @@ -615,7 +620,7 @@ public class RenderUtils { Material mat = new Material(); mat.set_diffuse(AssetDataStrings.TEXTURE_BLOCK_ATLAS); cubeMesh.setMaterial(mat); - cubeMesh.setShader(VisualShader.smartAssembleShader(false, true)); + cubeMesh.setShader(VisualShader.loadSpecificShader(AssetDataStrings.SHADER_BLOCK_SINGLE_VERT, AssetDataStrings.SHADER_BLOCK_SINGLE_FRAG)); GL40.glBindVertexArray(0); cubeMesh.setParent(model); model.getMeshes().add(cubeMesh); diff --git a/src/main/java/electrosphere/renderer/actor/Actor.java b/src/main/java/electrosphere/renderer/actor/Actor.java index 56ed4572..995720b8 100644 --- a/src/main/java/electrosphere/renderer/actor/Actor.java +++ b/src/main/java/electrosphere/renderer/actor/Actor.java @@ -7,6 +7,7 @@ import electrosphere.game.data.creature.type.bonegroups.BoneGroup; import electrosphere.logger.LoggerInterface; import electrosphere.renderer.OpenGLState; import electrosphere.renderer.RenderPipelineState; +import electrosphere.renderer.actor.ActorUniformMap.UniformValue; import electrosphere.renderer.model.Bone; import electrosphere.renderer.model.Model; import electrosphere.renderer.texture.Texture; @@ -33,6 +34,7 @@ import org.joml.Vector4d; * - Texture overrides * - Bone overrides * - Static Morphs (think scaling something, but from creatures.json) + * - Static uniforms per mesh */ public class Actor { @@ -72,6 +74,11 @@ public class Actor { //The list of bone groups List boneGroups; + /** + * A map of mesh -> uniforms to apply to the mesh + */ + ActorUniformMap uniformMap = new ActorUniformMap(); + //Controls whether the actor should obey frustum culling boolean frustumCull = true; @@ -441,6 +448,15 @@ public class Actor { overrideTextureObject.bind(openGLState); } } + //apply uniform overrides + if(this.uniformMap.getMeshes() != null && this.uniformMap.getMeshes().size() > 0){ + for(String meshName : this.uniformMap.getMeshes()){ + List uniforms = this.uniformMap.getUniforms(meshName); + for(UniformValue uniform : uniforms){ + model.pushUniformToMesh(meshName, uniform.getUniformName(), uniform.getValue()); + } + } + } model.draw(renderPipelineState,openGLState); model.getShaderMask().clear(); model.setTextureMask(null); @@ -624,6 +640,16 @@ public class Actor { return boneRotators.get(bone); } + /** + * Sets the value of a uniform on a given mesh within this actor + * @param meshName The name of the mesh + * @param uniformName The name of the uniform + * @param value The value of the uniform + */ + public void setUniformOnMesh(String meshName, String uniformName, Object value){ + this.uniformMap.setUniform(meshName, uniformName, value); + } + public void setActorStaticMorph(ActorStaticMorph staticMorph){ this.staticMorph = staticMorph; } diff --git a/src/main/java/electrosphere/renderer/actor/ActorUniformMap.java b/src/main/java/electrosphere/renderer/actor/ActorUniformMap.java new file mode 100644 index 00000000..1b05d0c9 --- /dev/null +++ b/src/main/java/electrosphere/renderer/actor/ActorUniformMap.java @@ -0,0 +1,97 @@ +package electrosphere.renderer.actor; + +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * Map of mesh -> uniforms to apply to that mesh + */ +public class ActorUniformMap { + + /** + * Map of mesh -> uniforms to push to that mesh + */ + Map> meshMap = new HashMap>(); + + /** + * Sets the value of a uniform for a given mesh + * @param meshName The name of the mesh + * @param uniformName The name of the uniform + * @param value The value of the uniform + */ + public void setUniform(String meshName, String uniformName, Object value){ + List uniforms = null; + if(meshMap.containsKey(meshName)){ + uniforms = meshMap.get(meshName); + } else { + uniforms = new LinkedList(); + meshMap.put(meshName,uniforms); + } + uniforms.add(new UniformValue(uniformName,value)); + } + + /** + * Gets the list of uniforms to apply to a given mesh + * @param meshName The name of the mesh + * @return The list of uniforms to apply to the mesh, or null if no uniforms are defined + */ + public List getUniforms(String meshName){ + return meshMap.get(meshName); + } + + /** + * Gets the set of names of meshes that have uniforms set + * @return The set of mesh names that have uniforms set + */ + public Set getMeshes(){ + return meshMap.keySet(); + } + + + /** + * A uniform value + */ + public static class UniformValue { + + /** + * The name of the uniform + */ + String uniformName; + + /** + * The value of the uniform + */ + Object value; + + /** + * Constructor + * @param uniformName Name of the uniform + * @param value Value of the uniform + */ + public UniformValue(String uniformName, Object value){ + this.uniformName = uniformName; + this.value = value; + } + + /** + * Gets the name of the uniform + * @return The name of the uniform + */ + public String getUniformName() { + return uniformName; + } + + /** + * Gets the value of the uniform + * @return The value of the uniform + */ + public Object getValue() { + return value; + } + + } + +} diff --git a/src/main/java/electrosphere/renderer/model/Mesh.java b/src/main/java/electrosphere/renderer/model/Mesh.java index 0096901e..e0a5aa5a 100644 --- a/src/main/java/electrosphere/renderer/model/Mesh.java +++ b/src/main/java/electrosphere/renderer/model/Mesh.java @@ -322,17 +322,20 @@ public class Mesh { } if(currentUniformRaw instanceof Matrix4d){ Matrix4d currentUniform = (Matrix4d)currentUniformRaw; - GL40.glUniformMatrix4dv(glGetUniformLocation(openGLState.getActiveShader().getId(), key), false, currentUniform.get(new double[16])); + openGLState.getActiveShader().setUniform(openGLState, key, currentUniform); + // GL40.glUniformMatrix4dv(glGetUniformLocation(openGLState.getActiveShader().getId(), key), false, currentUniform.get(new double[16])); Globals.renderingEngine.checkError(); } if(currentUniformRaw instanceof Vector3f){ Vector3f currentUniform = (Vector3f)currentUniformRaw; - glUniform3fv(glGetUniformLocation(openGLState.getActiveShader().getId(), key), currentUniform.get(BufferUtils.createFloatBuffer(3))); + openGLState.getActiveShader().setUniform(openGLState, key, currentUniform); + // glUniform3fv(glGetUniformLocation(openGLState.getActiveShader().getId(), key), currentUniform.get(BufferUtils.createFloatBuffer(3))); Globals.renderingEngine.checkError(); } if(currentUniformRaw instanceof Integer){ - int currentInform = (Integer)currentUniformRaw; - glUniform1i(glGetUniformLocation(openGLState.getActiveShader().getId(), key), currentInform); + int currentUniform = (Integer)currentUniformRaw; + openGLState.getActiveShader().setUniform(openGLState, key, currentUniform); + // glUniform1i(glGetUniformLocation(openGLState.getActiveShader().getId(), key), currentUniform); Globals.renderingEngine.checkError(); } } diff --git a/src/main/java/electrosphere/renderer/pipelines/MainContentPipeline.java b/src/main/java/electrosphere/renderer/pipelines/MainContentPipeline.java index 0b23c634..ddb2099c 100644 --- a/src/main/java/electrosphere/renderer/pipelines/MainContentPipeline.java +++ b/src/main/java/electrosphere/renderer/pipelines/MainContentPipeline.java @@ -55,7 +55,7 @@ public class MainContentPipeline implements RenderPipeline { renderPipelineState.setSelectedShader(SelectedShaderEnum.PRIMARY); renderPipelineState.setUseMeshShader(true); renderPipelineState.setBufferStandardUniforms(true); - renderPipelineState.setBufferNonStandardUniforms(false); + renderPipelineState.setBufferNonStandardUniforms(true); //true so that pre-programmed (in data) uniforms are pushed to gpu when rendering each mesh renderPipelineState.setUseMaterial(true); renderPipelineState.setUseShadowMap(true); renderPipelineState.setUseBones(true);