diff --git a/assets/Data/entity/objects/game_objects.json b/assets/Data/entity/objects/game_objects.json index 30dcfdde..94cec451 100644 --- a/assets/Data/entity/objects/game_objects.json +++ b/assets/Data/entity/objects/game_objects.json @@ -7,6 +7,29 @@ "tokens": [ "SPAWNPOINT" ] + }, + { + "id" : "particleEmitterTest", + "particleEmitter": { + "maxLife": 40, + "lifeCurrent": 0, + "particleVelocity": { + "x": 0.0, + "y": 0.1, + "z": 0.0 + }, + "acceleration": -0.01, + "texture": "", + "size": 0.3, + "color": { + "x": 0.5, + "y": 0.5, + "z": 0.5 + }, + "frequency": 1 + }, + "tokens": [ + ] } ], diff --git a/assets/Data/entity/particles.json b/assets/Data/entity/particles.json index 175e89e0..4a207b27 100644 --- a/assets/Data/entity/particles.json +++ b/assets/Data/entity/particles.json @@ -4,7 +4,11 @@ "name" : "blood", "maxLife" : 7, "lifeCurrent" : 0, - "velocity" : 0.3, + "velocity" : { + "x": 0.3, + "y": 0.3, + "z": 0.3 + }, "acceleration" : -0.1, "size": 0.15, "texture" : "Textures/bloodsplat1.png" diff --git a/assets/Shaders/entities/particle/particle.fs b/assets/Shaders/entities/particle/particle.fs new file mode 100644 index 00000000..9a1a6082 --- /dev/null +++ b/assets/Shaders/entities/particle/particle.fs @@ -0,0 +1,297 @@ +#version 450 core + +//foliage.fs + +/** +Bind points for different SSBOs +*/ +#define CLUSTER_SSBO_BIND_POINT 1 +#define POINT_LIGHT_SSBO_BIND_POINT 2 +#define DIRECT_LIGHT_SSBO_BIND_POINT 3 + + +/** +Maximum number of point lights +*/ +#define MAX_POINT_LIGHTS 512 + +/** +Maximum number of lights per cluster +*/ +#define MAX_LIGHTS_PER_CLUSTER 100 + +/** +The direct global light +*/ +struct DirectLight { + vec3 direction; + vec3 color; +}; + +/** +A point light +*/ +struct PointLight { + vec4 position; + vec4 color; + float constant; + float linear; + float quadratic; + float radius; +}; + +/** +A light cluster +*/ +struct Cluster { + vec4 minPoint; + vec4 maxPoint; + uint count; + uint lightIndices[MAX_LIGHTS_PER_CLUSTER]; +}; + +out vec4 FragColor; + + +layout(std430, binding = CLUSTER_SSBO_BIND_POINT) restrict buffer clusterGridSSBO { + Cluster clusters[]; +}; + +layout(std430, binding = POINT_LIGHT_SSBO_BIND_POINT) restrict buffer pointLightSSBO { + PointLight pointLight[]; +}; + +layout(std430, binding = DIRECT_LIGHT_SSBO_BIND_POINT) restrict buffer dirLightSSBO { + DirectLight directLight; +}; + +struct Material { + sampler2D diffuse; + sampler2D specular; + float shininess; +}; + +in vec3 FragPos; +in vec3 ViewFragPos; +in vec3 Normal; +in vec2 TexCoord; +in vec4 FragPosLightSpace; +in vec4 color; + + +uniform vec3 viewPos; +// uniform DirLight dirLight; +// uniform PointLight pointLights[NR_POINT_LIGHTS]; +// uniform SpotLight spotLight; +uniform Material material; + +//texture stuff +// uniform sampler2D ourTexture; +uniform int hasTransparency; +// uniform sampler2D specularTexture; + +//light depth map +uniform sampler2D shadowMap; + +/** +Used for light cluster calculation +*/ +uniform float zNear; +uniform float zFar; +uniform uvec3 gridSize; +uniform uvec2 screenDimensions; +uniform mat4 view; + + +// function prototypes +uint findCluster(vec3 FragPos, float zNear, float zFar); +vec3 CalcPointLight(PointLight pointLight, vec3 normal, vec3 fragPos, vec3 viewDir); +float calcLightIntensityTotal(vec3 normal); +float ShadowCalculation(vec4 fragPosLightSpace, vec3 lightDir, vec3 normal); +float easeIn(float interpolator); +float easeOut(float interpolator); + +void main(){ + vec3 viewDir = normalize(viewPos - FragPos); + + //grab light intensity + vec3 lightIntensity = vec3(calcLightIntensityTotal(Normal)); + + //get color of base texture + // vec3 textureColor = vec3((norm.x + 1) / 2.0, norm.y, 1.0 - (norm.x + 1) / 2.0); + vec3 textureColor = color.rgb; + // vec3 textureColor = vec3(0.17647,0.4,0.09411);//texture(material.diffuse, TexCoord).rgb; + + //shadow + float shadow = ShadowCalculation(FragPosLightSpace, normalize(-directLight.direction), Normal); + + // + //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, Normal, 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); + // vec3 lightAmount = CalcDirLight(norm, viewDir); + // for(int i = 0; i < NR_POINT_LIGHTS; i++){ + // lightAmount += CalcPointLight(i, norm, FragPos, viewDir); + // } + + //this final calculation is for transparency + FragColor = vec4(finalColor, 1.0);//texture(ourTexture, TexCoord);//vec4(result, 1.0); +} + +// calculates the color when using a directional light. +// vec3 CalcDirLight(vec3 normal, vec3 viewDir){ +// vec3 lightDir = normalize(-dLDirection); +// // diffuse shading +// float diff = max(dot(normal, lightDir), 0.0); +// // specular shading +// // vec3 reflectDir = reflect(-lightDir, normal); +// // float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess); +// // combine results +// vec3 texColor = texture(material.diffuse, TexCoord).rgb; +// vec3 diffuse = dLDiffuse * diff; +// //vec3 specular = light.specular * spec * vec3(texture(material.specular, TexCoord).rgb); + + +// float shadow = ShadowCalculation(FragPosLightSpace, lightDir, normal); + +// return ( dLAmbient + (1.0-shadow) * diffuse ) * texColor;// + specular); +// } + +// +float calcLightIntensityAmbient(){ + //calculate average of ambient light + float avg = (directLight.color.x + directLight.color.y + directLight.color.z)/3.0; + return avg; +} + +// +float calcLightIntensityDir(vec3 normal){ + vec3 lightDir = normalize(-directLight.direction); + // diffuse shading + float diff = max(dot(normal, lightDir), 0.0); + + return diff; +} + +// +float calcLightIntensityTotal(vec3 normal){ + //ambient intensity + float ambientLightIntensity = calcLightIntensityAmbient(); + + //get direct intensity + float directLightIntensity = calcLightIntensityDir(normal); + + //sum + float total = ambientLightIntensity + directLightIntensity; + return total; +} + +// +vec3 getTotalLightColor(vec3 normal){ + //get the direct light color adjusted for intensity + vec3 diffuseLightColor = directLight.color * calcLightIntensityDir(normal); + + //sum light colors + vec3 totalLightColor = diffuseLightColor; + return totalLightColor; +} + +vec3 CalcPointLight(PointLight pointLight, vec3 normal, vec3 fragPos, vec3 viewDir){ + vec3 lightDir = normalize(pointLight.position.xyz - fragPos); + // diffuse shading + float diff = max(dot(normal, lightDir), 0.0); + // specular shading + // vec3 reflectDir = reflect(-lightDir, normal); + // float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess); + // attenuation + float distance = length(pointLight.position.xyz - fragPos); + float attenuation = 1.0 / (pointLight.constant + pointLight.linear * distance + pointLight.quadratic * (distance * distance)); + if(distance > pointLight.radius){ + attenuation = 0; + } + // combine results + vec3 ambient = pointLight.color.xyz;// * vec4(texture(material.diffuse, TexCoord)).xyz; + vec3 diffuse = pointLight.color.xyz * diff;// * vec4(texture(material.diffuse, TexCoord)).xyz; + // vec3 specular = pLspecular[i] * spec;// * vec4(texture(material.specular, TexCoord)).xyz; + ambient = ambient * attenuation; + diffuse = diffuse * attenuation; + // specular *= attenuation; + vec3 specular = vec3(0,0,0); + + vec3 finalValue = vec3(0); + if(distance < pointLight.radius){ + finalValue = (ambient + diffuse + specular); + finalValue = vec3(max(finalValue.x,0),max(finalValue.y,0),max(finalValue.z,0)); + } + + return finalValue; +} + +/** +Finds the light cluster this fragment belongs to +*/ +uint findCluster(vec3 viewspaceFragPos, float zNear, float zFar){ + uint zTile = uint((log(abs(viewspaceFragPos.z) / zNear) * gridSize.z) / log(zFar / zNear)); + vec2 tileSize = screenDimensions / gridSize.xy; + uvec3 tile = uvec3(gl_FragCoord.xy / tileSize, zTile); + return tile.x + (tile.y * gridSize.x) + (tile.z * gridSize.x * gridSize.y); +} + +float ShadowCalculation(vec4 fragPosLightSpace, vec3 lightDir, vec3 normal){ + + // perform perspective divide + vec3 projCoords = fragPosLightSpace.xyz / fragPosLightSpace.w; + + //transform to NDC + projCoords = projCoords * 0.5 + 0.5; + + //get closest depth from light's POV + float closestDepth = texture(shadowMap, projCoords.xy).r; + + //get depth of current fragment + float currentDepth = projCoords.z; + + //calculate bias + float bias = max(0.05 * (1.0 - dot(normal, lightDir)), 0.005); + + //calculate shadow value + float shadow = currentDepth - bias > closestDepth ? 1.0 : 0.0; + + if(projCoords.z > 1.0){ + shadow = 0.0; + } + + //calculate dot product, if it is >0 we know they're parallel-ish therefore should disregard the shadow mapping + //ie the fragment is already facing away from the light source + float dotprod = dot(normalize(lightDir),normalize(normal)); + + if(dotprod > 0.0){ + shadow = 0.0; + } + + // shadow = currentDepth; + + return shadow; +} + + +float easeIn(float interpolator){ + return interpolator * interpolator; +} + +float easeOut(float interpolator){ + return 1 - easeIn(1 - interpolator); +} \ No newline at end of file diff --git a/assets/Shaders/entities/particle/particle.vs b/assets/Shaders/entities/particle/particle.vs new file mode 100644 index 00000000..cd469ce1 --- /dev/null +++ b/assets/Shaders/entities/particle/particle.vs @@ -0,0 +1,76 @@ +//Vertex Shader +#version 450 core + +/** +Bind points for different SSBOs +*/ +#define PARTICLE_SSBO_BIND_POINT 4 + +/** +A point light +*/ +struct ParticleData { + mat4 model; + vec4 color; +}; + + +//input buffers +layout (location = 0) in vec3 aPos; +layout (location = 1) in vec3 aNormal; +layout (location = 4) in vec2 aTex; + +layout(std430, binding = PARTICLE_SSBO_BIND_POINT) restrict buffer particleSSBO { + ParticleData particleData[]; +}; + + +//coordinate space transformation matrices +uniform mat4 transform; +uniform mat4 view; +uniform mat4 projection; +uniform mat4 lightSpaceMatrix; + + + +//output buffers +out vec3 Normal; +out vec3 FragPos; +out vec3 ViewFragPos; +out vec2 TexCoord; +out vec4 FragPosLightSpace; +out vec4 color; + + + + +void main() { + + ParticleData currentData = particleData[gl_InstanceID]; + + mat4 model = currentData.model; + color = currentData.color; + + //normalize posiiton and normal + vec4 FinalVertex = vec4(aPos, 1.0); + vec4 FinalNormal = vec4(aNormal, 1.0); + + //make sure the W component is 1.0 + FinalVertex = vec4(FinalVertex.xyz, 1.0); + FinalNormal = vec4(FinalNormal.xyz, 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; + TexCoord = 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/buildNumber.properties b/buildNumber.properties index 14badf94..106ed524 100644 --- a/buildNumber.properties +++ b/buildNumber.properties @@ -1,3 +1,3 @@ #maven.buildNumber.plugin properties file -#Thu Sep 19 15:35:07 EDT 2024 -buildNumber=339 +#Thu Sep 19 21:21:28 EDT 2024 +buildNumber=351 diff --git a/docs/src/progress/renderertodo.md b/docs/src/progress/renderertodo.md index 3c744f20..250c8c02 100644 --- a/docs/src/progress/renderertodo.md +++ b/docs/src/progress/renderertodo.md @@ -811,6 +811,7 @@ Fix terrain editing across chunk borders on server - This is because the ray on the client doesn't intersect at the border (because the physics generation isn't working on chunk-end) - Also because the client doesn't scan border chunks to see if they should update Fix shader program bug with no-bone variants +Particle Emitter work (it renders! it has management!) # TODO diff --git a/src/main/java/electrosphere/client/entity/instance/InstanceTemplate.java b/src/main/java/electrosphere/client/entity/instance/InstanceTemplate.java index 96ed4a86..076b3844 100644 --- a/src/main/java/electrosphere/client/entity/instance/InstanceTemplate.java +++ b/src/main/java/electrosphere/client/entity/instance/InstanceTemplate.java @@ -2,6 +2,7 @@ package electrosphere.client.entity.instance; import java.util.Map; +import electrosphere.renderer.actor.instance.StridedInstanceData; import electrosphere.renderer.buffer.ShaderAttribute; import electrosphere.renderer.buffer.HomogenousUniformBuffer.HomogenousBufferTypes; @@ -19,6 +20,16 @@ public class InstanceTemplate { //The map of all attributes for instanced foliage protected Map attributes; + /** + * The strided instance data + */ + protected StridedInstanceData stridedInstanceData; + + /** + * The bind point for the data + */ + int dataBindPoint; + //shader paths protected String vertexPath; protected String fragmentPath; @@ -42,6 +53,26 @@ public class InstanceTemplate { return rVal; } + /** + * Creates a template to create instanced actors off of + * @param capacity The number of actors that can be drawn per frame (going over this limit will result in drawing the closest ${capacity} number of actors) + * @param modelPath The path for the model of the instance + * @param vertexPath The vertex shader path + * @param fragmentPath The fragment shader path + * @param stridedInstanceData The strided instance data to use + * @return The template + */ + public static InstanceTemplate createInstanceTemplate(int capacity, String modelPath, String vertexPath, String fragmentPath, StridedInstanceData stridedInstanceData, int bindPoint){ + InstanceTemplate rVal = new InstanceTemplate(); + rVal.capacity = capacity; + rVal.modelPath = modelPath; + rVal.vertexPath = vertexPath; + rVal.fragmentPath = fragmentPath; + rVal.stridedInstanceData = stridedInstanceData; + rVal.dataBindPoint = bindPoint; + return rVal; + } + } diff --git a/src/main/java/electrosphere/client/entity/instance/InstancedEntityUtils.java b/src/main/java/electrosphere/client/entity/instance/InstancedEntityUtils.java index 582aed0e..6fd4d1ff 100644 --- a/src/main/java/electrosphere/client/entity/instance/InstancedEntityUtils.java +++ b/src/main/java/electrosphere/client/entity/instance/InstancedEntityUtils.java @@ -23,12 +23,24 @@ public class InstancedEntityUtils { * @return The instanced actor that is attached to this entity */ public static InstancedActor makeEntityInstanced(Entity entity, InstanceTemplate template){ - InstancedActor instancedActor = Globals.clientInstanceManager.createInstancedActor( - template.modelPath, - template.vertexPath, - template.fragmentPath, - template.attributes, - template.capacity); + InstancedActor instancedActor = null; + if(template.attributes != null){ + instancedActor = Globals.clientInstanceManager.createInstancedActor( + template.modelPath, + template.vertexPath, + template.fragmentPath, + template.attributes, + template.capacity + ); + } else if(template.stridedInstanceData != null) { + instancedActor = Globals.clientInstanceManager.createInstancedActor( + template.modelPath, + template.vertexPath, + template.fragmentPath, + template.stridedInstanceData, + template.capacity + ); + } entity.putData(EntityDataStrings.INSTANCED_ACTOR, instancedActor); entity.putData(EntityDataStrings.DATA_STRING_POSITION, new Vector3d(0,0,0)); entity.putData(EntityDataStrings.DATA_STRING_ROTATION, new Quaterniond().identity()); @@ -50,12 +62,24 @@ public class InstancedEntityUtils { * @return The instanced actor that is attached to this entity */ public static InstancedActor makeEntityInstancedWithModelTransform(Entity entity, InstanceTemplate template, ShaderAttribute modelTransformAttribute){ - InstancedActor instancedActor = Globals.clientInstanceManager.createInstancedActor( - template.modelPath, - template.vertexPath, - template.fragmentPath, - template.attributes, - template.capacity); + InstancedActor instancedActor = null; + if(template.attributes != null){ + instancedActor = Globals.clientInstanceManager.createInstancedActor( + template.modelPath, + template.vertexPath, + template.fragmentPath, + template.attributes, + template.capacity + ); + } else if(template.stridedInstanceData != null) { + instancedActor = Globals.clientInstanceManager.createInstancedActor( + template.modelPath, + template.vertexPath, + template.fragmentPath, + template.stridedInstanceData, + template.capacity + ); + } entity.putData(EntityDataStrings.INSTANCED_ACTOR, instancedActor); entity.putData(EntityDataStrings.DATA_STRING_POSITION, new Vector3d(0,0,0)); entity.putData(EntityDataStrings.DATA_STRING_ROTATION, new Quaterniond().identity()); diff --git a/src/main/java/electrosphere/client/entity/particle/ParticleService.java b/src/main/java/electrosphere/client/entity/particle/ParticleService.java index d345969d..b6759aaf 100644 --- a/src/main/java/electrosphere/client/entity/particle/ParticleService.java +++ b/src/main/java/electrosphere/client/entity/particle/ParticleService.java @@ -3,10 +3,21 @@ package electrosphere.client.entity.particle; import java.util.Arrays; import java.util.List; +import org.joml.Vector3d; + +import electrosphere.client.entity.instance.InstanceTemplate; +import electrosphere.client.entity.instance.InstancedEntityUtils; +import electrosphere.engine.assetmanager.AssetDataStrings; import electrosphere.engine.signal.Signal; import electrosphere.engine.signal.SignalServiceImpl; +import electrosphere.entity.Entity; +import electrosphere.entity.EntityCreationUtils; +import electrosphere.entity.EntityUtils; +import electrosphere.entity.state.client.particle.ClientParticleTree; +import electrosphere.game.data.particle.ParticleData; import electrosphere.renderer.actor.instance.StridedInstanceData; import electrosphere.renderer.buffer.HomogenousUniformBuffer.HomogenousBufferTypes; +import electrosphere.renderer.buffer.ShaderAttribute; /** * The particle service @@ -18,16 +29,20 @@ public class ParticleService extends SignalServiceImpl { */ static final int MAX_PARTICLES = 1000; + /** + * The bind point for the particle ssbo + */ + static final int PARTICLE_SSBO_BIND_POINT = 4; + /** * Path to the vertex shader */ - static final String VERTEX_SHADER_PATH = ""; + static final String VERTEX_SHADER_PATH = "Shaders/entities/particle/particle.vs"; /** * Path to the fragment shader */ - static final String FRAGMENT_SHADER_PATH = ""; - + static final String FRAGMENT_SHADER_PATH = "Shaders/entities/particle/particle.fs"; /** @@ -35,6 +50,16 @@ public class ParticleService extends SignalServiceImpl { */ StridedInstanceData particleInstanceData; + /** + * The template to use when creating instanced actors + */ + InstanceTemplate instanceTemplate; + + + ShaderAttribute modelAttrib; + + ShaderAttribute colorAttrib; + /** * Constructor */ @@ -55,17 +80,14 @@ public class ParticleService extends SignalServiceImpl { boolean rVal = false; switch(signal.getType()){ case RENDERING_ENGINE_READY: { - List types = Arrays.asList(new HomogenousBufferTypes[]{ - //position - HomogenousBufferTypes.VEC3D, - //rotation - HomogenousBufferTypes.VEC4D, - //scale - HomogenousBufferTypes.VEC3D, - //texture index - HomogenousBufferTypes.INT, + modelAttrib = new ShaderAttribute("model", HomogenousBufferTypes.MAT4F); + colorAttrib = new ShaderAttribute("color", HomogenousBufferTypes.VEC4F); + List types = Arrays.asList(new ShaderAttribute[]{ + modelAttrib, + colorAttrib, }); - this.particleInstanceData = new StridedInstanceData(MAX_PARTICLES,types,VERTEX_SHADER_PATH,FRAGMENT_SHADER_PATH); + this.particleInstanceData = new StridedInstanceData(MAX_PARTICLES,PARTICLE_SSBO_BIND_POINT,types,VERTEX_SHADER_PATH,FRAGMENT_SHADER_PATH); + this.instanceTemplate = InstanceTemplate.createInstanceTemplate(MAX_PARTICLES, AssetDataStrings.MODEL_PARTICLE, VERTEX_SHADER_PATH, FRAGMENT_SHADER_PATH, particleInstanceData, PARTICLE_SSBO_BIND_POINT); rVal = true; } break; default: { @@ -73,5 +95,35 @@ public class ParticleService extends SignalServiceImpl { } return rVal; } + + /** + * Gets the instance data for the particles + * @return The instance data + */ + public StridedInstanceData getInstanceData(){ + return this.particleInstanceData; + } + + /** + * Spawns a particle + * @param data The particle data for the particle + * @param position The position of the particle + * @return The particle + */ + public Entity spawn(ParticleData data, Vector3d position){ + Entity rVal = EntityCreationUtils.createClientSpatialEntity(); + InstancedEntityUtils.makeEntityInstanced(rVal, instanceTemplate); + EntityUtils.getPosition(rVal).set(position); + ClientParticleTree.attachTree(rVal, data); + return rVal; + } + + public ShaderAttribute getModelAttrib(){ + return modelAttrib; + } + + public ShaderAttribute getColorAttrib(){ + return colorAttrib; + } } diff --git a/src/main/java/electrosphere/client/entity/particle/ParticleUtils.java b/src/main/java/electrosphere/client/entity/particle/ParticleUtils.java index f318ffd4..139eec42 100644 --- a/src/main/java/electrosphere/client/entity/particle/ParticleUtils.java +++ b/src/main/java/electrosphere/client/entity/particle/ParticleUtils.java @@ -1,14 +1,14 @@ package electrosphere.client.entity.particle; import electrosphere.engine.Globals; +import electrosphere.engine.assetmanager.AssetDataStrings; import electrosphere.entity.DrawableUtils; import electrosphere.entity.Entity; import electrosphere.entity.EntityCreationUtils; import electrosphere.entity.EntityDataStrings; import electrosphere.entity.EntityTags; import electrosphere.entity.EntityUtils; -import electrosphere.entity.state.ParticleTree; -import electrosphere.entity.state.particle.ClientParticleTree; +import electrosphere.entity.state.client.particle.ClientParticleTree; import electrosphere.game.data.particle.ParticleData; import electrosphere.renderer.actor.Actor; import electrosphere.renderer.actor.ActorTextureMask; @@ -16,7 +16,6 @@ import electrosphere.renderer.actor.ActorTextureMask; import java.util.Arrays; import org.joml.Vector3d; -import org.joml.Vector3f; /** * Particle utility functions @@ -27,15 +26,15 @@ public class ParticleUtils { - public static Entity clientSpawnStaticBillboardParticle(){ - Entity rVal = EntityCreationUtils.createClientSpatialEntity(); - EntityCreationUtils.makeEntityDrawable(rVal, Globals.particleBillboardModel); - ParticleTree particleTree = new ParticleTree(rVal, 10, new Vector3f(0,0,0), 0, 0, false); - rVal.putData(EntityDataStrings.TREE_CLIENTPARTICLETREE, particleTree); - rVal.putData(EntityDataStrings.IS_PARTICLE, true); - Globals.clientSceneWrapper.getScene().registerEntityToTag(rVal, EntityTags.PARTICLE); - return rVal; - } + // public static Entity clientSpawnStaticBillboardParticle(){ + // Entity rVal = EntityCreationUtils.createClientSpatialEntity(); + // EntityCreationUtils.makeEntityDrawable(rVal, Globals.particleBillboardModel); + // ClientParticleTree particleTree = new ClientParticleTree(rVal, 10, new Vector3f(0,0,0), 0, 0, false); + // rVal.putData(EntityDataStrings.TREE_CLIENTPARTICLETREE, particleTree); + // rVal.putData(EntityDataStrings.IS_PARTICLE, true); + // Globals.clientSceneWrapper.getScene().registerEntityToTag(rVal, EntityTags.PARTICLE); + // return rVal; + // } /** * Spawns a billboard particle @@ -45,7 +44,7 @@ public class ParticleUtils { */ public static Entity clientSpawnBillboardParticle(ParticleData data, Vector3d destination){ Entity rVal = EntityCreationUtils.createClientSpatialEntity(); - EntityCreationUtils.makeEntityDrawable(rVal, Globals.particleBillboardModel); + EntityCreationUtils.makeEntityDrawable(rVal, AssetDataStrings.MODEL_PARTICLE); //actor logic Actor particleActor = EntityUtils.getActor(rVal); diff --git a/src/main/java/electrosphere/client/sim/ClientSimulation.java b/src/main/java/electrosphere/client/sim/ClientSimulation.java index 39e3afe5..9803dd6a 100644 --- a/src/main/java/electrosphere/client/sim/ClientSimulation.java +++ b/src/main/java/electrosphere/client/sim/ClientSimulation.java @@ -76,6 +76,7 @@ public class ClientSimulation { // //update attached entity positions AttachUtils.clientUpdateAttachedEntityPositions(); + Globals.particleService.handleAllSignals(); // //Hitbox stuff Globals.profiler.beginCpuSample("update hitboxes"); diff --git a/src/main/java/electrosphere/engine/Globals.java b/src/main/java/electrosphere/engine/Globals.java index 64ab1769..0316f900 100644 --- a/src/main/java/electrosphere/engine/Globals.java +++ b/src/main/java/electrosphere/engine/Globals.java @@ -292,7 +292,6 @@ public class Globals { // // Particle stuff // - public static String particleBillboardModel; public static ParticleDefinition particleDefinition; public static ParticleService particleService; @@ -619,7 +618,7 @@ public class Globals { fontManager.loadFonts(); assetManager.registerModelToSpecificString(RenderUtils.createBitmapCharacter(), AssetDataStrings.BITMAP_CHARACTER_MODEL); //particle billboard model - particleBillboardModel = assetManager.registerModel(RenderUtils.createParticleModel()); + assetManager.registerModelToSpecificString(RenderUtils.createParticleModel(), AssetDataStrings.MODEL_PARTICLE); //initialize required windows WindowUtils.initBaseWindows(); //init default shaderProgram diff --git a/src/main/java/electrosphere/engine/assetmanager/AssetDataStrings.java b/src/main/java/electrosphere/engine/assetmanager/AssetDataStrings.java index 0c21beb3..bc67d98f 100644 --- a/src/main/java/electrosphere/engine/assetmanager/AssetDataStrings.java +++ b/src/main/java/electrosphere/engine/assetmanager/AssetDataStrings.java @@ -15,6 +15,7 @@ public class AssetDataStrings { public static final String UNITSPHERE = "unitSphere"; public static final String UNITCYLINDER = "unitCylinder"; public static final String UNITCUBE = "unitCube"; + public static final String MODEL_PARTICLE = "particle"; /** * UI generic audio diff --git a/src/main/java/electrosphere/entity/ClientEntityUtils.java b/src/main/java/electrosphere/entity/ClientEntityUtils.java index a006930e..20b67eb8 100644 --- a/src/main/java/electrosphere/entity/ClientEntityUtils.java +++ b/src/main/java/electrosphere/entity/ClientEntityUtils.java @@ -3,6 +3,7 @@ package electrosphere.entity; import org.joml.Quaterniond; import org.joml.Vector3d; +import electrosphere.engine.Globals; import electrosphere.entity.types.collision.CollisionObjUtils; /** @@ -28,7 +29,8 @@ public class ClientEntityUtils { */ public static void destroyEntity(Entity entity){ //check for client-specific stuff - + Globals.renderingEngine.getLightManager().destroyPointLight(entity); + //deregister all behavior trees EntityUtils.cleanUpEntity(entity); } diff --git a/src/main/java/electrosphere/entity/EntityDataStrings.java b/src/main/java/electrosphere/entity/EntityDataStrings.java index 5a56ecf4..6aa7f748 100644 --- a/src/main/java/electrosphere/entity/EntityDataStrings.java +++ b/src/main/java/electrosphere/entity/EntityDataStrings.java @@ -260,9 +260,10 @@ public class EntityDataStrings { public static final String TREE_SERVEREQUIPSTATE = "treeServerEquipState"; /* - Light state + Client-only components */ public static final String TREE_CLIENTLIGHTSTATE = "treeClientLightState"; + public static final String TREE_CLIENTPARTICLEEMITTERSTATE = "treeClientParticleEmitterState"; /* Inventory in general diff --git a/src/main/java/electrosphere/entity/state/ParticleTree.java b/src/main/java/electrosphere/entity/state/ParticleTree.java deleted file mode 100644 index 2be5b3fe..00000000 --- a/src/main/java/electrosphere/entity/state/ParticleTree.java +++ /dev/null @@ -1,68 +0,0 @@ -package electrosphere.entity.state; - -import electrosphere.entity.Entity; -import electrosphere.entity.EntityUtils; -import electrosphere.entity.btree.BehaviorTree; - -import org.joml.Vector3d; -import org.joml.Vector3f; - -/** - * Particle b tree - */ -public class ParticleTree implements BehaviorTree { - Entity parent; - boolean hasLife = true; - int maxLife; - int lifeCurrent; - Vector3f destination; - float velocity; - float acceleration; - - public ParticleTree(Entity parent, int maxLife, Vector3f destination, float velocity, float acceleration, boolean hasLife){ - this.parent = parent; - this.maxLife = maxLife; - this.destination = destination; - this.velocity = velocity; - this.acceleration = acceleration; - this.hasLife = hasLife; - lifeCurrent = maxLife; - } - - public int getMaxLife() { - return maxLife; - } - - public int getLifeCurrent() { - return lifeCurrent; - } - - public Vector3f getDestination() { - return destination; - } - - public float getVelocity() { - return velocity; - } - - public float getAcceleration() { - return acceleration; - } - - public void simulate(float deltaTime){ - Vector3d parentPosition = EntityUtils.getPosition(parent); - parentPosition.add(new Vector3f(destination).mul(velocity)); - velocity = velocity - acceleration; - if(velocity < 0){ - velocity = 0; - acceleration = 0; - } - if(hasLife){ - lifeCurrent--; - if(lifeCurrent <= 0){ - EntityUtils.cleanUpEntity(parent); - } - } - } - -} diff --git a/src/main/java/electrosphere/entity/state/client/particle/ClientParticleEmitterComponent.java b/src/main/java/electrosphere/entity/state/client/particle/ClientParticleEmitterComponent.java new file mode 100644 index 00000000..6965bbb8 --- /dev/null +++ b/src/main/java/electrosphere/entity/state/client/particle/ClientParticleEmitterComponent.java @@ -0,0 +1,107 @@ +package electrosphere.entity.state.client.particle; + +import org.joml.Vector3d; + +import electrosphere.engine.Globals; +import electrosphere.entity.Entity; +import electrosphere.entity.EntityDataStrings; +import electrosphere.entity.EntityUtils; +import electrosphere.entity.btree.BehaviorTree; +import electrosphere.entity.state.equip.ClientEquipState; +import electrosphere.game.data.particle.ParticleData; +import electrosphere.game.data.particle.ParticleEmitter; + +/** + * A component that causes the entity to emit particles + */ +public class ClientParticleEmitterComponent implements BehaviorTree { + + + /** + * The parent entity + */ + Entity parent; + + /** + * The particle emitter data + */ + ParticleEmitter particleEmitter; + + /** + * The last frame this emitter sent out a particle + */ + long lastEmittedFrame = 0; + + @Override + public void simulate(float deltaTime) { + Vector3d entityPos = EntityUtils.getPosition(parent); + if((float)(Globals.timekeeper.getNumberOfRenderFramesElapsed() - lastEmittedFrame) > particleEmitter.getFrequency()){ + lastEmittedFrame = Globals.timekeeper.getNumberOfRenderFramesElapsed(); + //create particle here + Globals.particleService.spawn(this.getData(), new Vector3d(entityPos)); + } + } + + /** + * Private constructor + * @param parent + * @param params + */ + private ClientParticleEmitterComponent(Entity parent, Object ... params){ + this.parent = parent; + particleEmitter = (ParticleEmitter)params[0]; + } + + /** + * Gets particle data for a new particle + * @return The data + */ + private ParticleData getData(){ + ParticleData data = new ParticleData(); + data.setAcceleration(this.particleEmitter.getAcceleration()); + data.setColor(this.particleEmitter.getColor()); + data.setLifeCurrent(this.particleEmitter.getLifeCurrent()); + data.setMaxLife(this.particleEmitter.getMaxLife()); + data.setSize(this.particleEmitter.getSize()); + data.setTexture(this.particleEmitter.getTexture()); + data.setVelocity(this.particleEmitter.getParticleVelocity()); + return data; + } + + + /** + *

+ * Attaches this tree to the entity. + *

+ * @param entity The entity to attach to + * @param tree The behavior tree to attach + * @param params Optional parameters that will be provided to the constructor + */ + public static ClientParticleEmitterComponent attachTree(Entity parent, Object ... params){ + ClientParticleEmitterComponent rVal = new ClientParticleEmitterComponent(parent,params); + //!!WARNING!! from here below should not be touched + //This was generated automatically to properly alert various systems that the btree exists and should be tracked + parent.putData(EntityDataStrings.TREE_CLIENTPARTICLEEMITTERSTATE, rVal); + Globals.clientSceneWrapper.getScene().registerBehaviorTree(rVal); + return rVal; + } + /** + *

+ * Detatches this tree from the entity. + *

+ * @param entity The entity to detach to + * @param tree The behavior tree to detach + */ + public static void detachTree(Entity entity, BehaviorTree tree){ + } + /** + *

+ * Gets the ClientEquipState of the entity + *

+ * @param entity the entity + * @return The ClientEquipState + */ + public static ClientEquipState getClientEquipState(Entity entity){ + return (ClientEquipState)entity.getData(EntityDataStrings.TREE_CLIENTPARTICLEEMITTERSTATE); + } +} diff --git a/src/main/java/electrosphere/entity/state/particle/ClientParticleTree.java b/src/main/java/electrosphere/entity/state/client/particle/ClientParticleTree.java similarity index 58% rename from src/main/java/electrosphere/entity/state/particle/ClientParticleTree.java rename to src/main/java/electrosphere/entity/state/client/particle/ClientParticleTree.java index 11d787c4..882a45e4 100644 --- a/src/main/java/electrosphere/entity/state/particle/ClientParticleTree.java +++ b/src/main/java/electrosphere/entity/state/client/particle/ClientParticleTree.java @@ -1,10 +1,15 @@ -package electrosphere.entity.state.particle; +package electrosphere.entity.state.client.particle; + +import java.util.Random; import org.joml.AxisAngle4f; import org.joml.Matrix4f; import org.joml.Quaternionf; import org.joml.Vector3d; +import org.joml.Vector3f; +import org.joml.Vector4f; +import electrosphere.client.entity.camera.CameraEntityUtils; import electrosphere.engine.Globals; import electrosphere.entity.ClientEntityUtils; import electrosphere.entity.Entity; @@ -12,6 +17,7 @@ import electrosphere.entity.EntityDataStrings; import electrosphere.entity.EntityUtils; import electrosphere.entity.btree.BehaviorTree; import electrosphere.game.data.particle.ParticleData; +import electrosphere.renderer.actor.instance.InstancedActor; /** * Particle component for a client-side particle @@ -28,15 +34,21 @@ public class ClientParticleTree implements BehaviorTree { //current life int lifeCurrent; - - //The destination of the particle - Vector3d destination; - //the velocity of the particle - float velocity; + Vector3d velocity; //the acceleration of the particle float acceleration; + + /** + * The data for this particle + */ + ParticleData particleData; + + /** + * The color of the particle + */ + Vector3d color; /** @@ -48,20 +60,16 @@ public class ClientParticleTree implements BehaviorTree { if(params.length < 1){ throw new IllegalArgumentException("No particle data was provided"); } - if(params.length < 2){ - throw new IllegalArgumentException("No destination set for the particle"); - } - ParticleData particleData = (ParticleData)params[0]; - Vector3d destination = (Vector3d)params[1]; + this.particleData = (ParticleData)params[0]; //sets data for the tree this.parent = parent; this.maxLife = particleData.getMaxLife(); - this.destination = destination; this.velocity = particleData.getVelocity(); this.acceleration = particleData.getAcceleration(); this.hasLife = particleData.getMaxLife() != null; this.lifeCurrent = maxLife; + this.color = new Vector3d(new Random().nextFloat(),new Random().nextFloat(),new Random().nextFloat()); } public int getMaxLife() { @@ -72,11 +80,7 @@ public class ClientParticleTree implements BehaviorTree { return lifeCurrent; } - public Vector3d getDestination() { - return destination; - } - - public float getVelocity() { + public Vector3d getVelocity() { return velocity; } @@ -86,17 +90,25 @@ public class ClientParticleTree implements BehaviorTree { @Override public void simulate(float deltaTime){ + InstancedActor instancedActor = InstancedActor.getInstancedActor(parent); Vector3d parentPosition = EntityUtils.getPosition(parent); - parentPosition.add(new Vector3d(destination).mul(velocity)); - velocity = velocity + acceleration; - if(velocity < 0){ - velocity = 0; + Vector3f cameraPos = CameraEntityUtils.getCameraCenter(Globals.playerCamera); + + //update position + parentPosition.set(new Vector3d(parentPosition).add(velocity)); + + //update velocity + Vector3d accelerationVec = new Vector3d(velocity).normalize().mul(acceleration); + velocity = new Vector3d(velocity).add(accelerationVec); + if(velocity.length() < 0){ + velocity = new Vector3d(0,0,0); acceleration = 0; } if(hasLife){ lifeCurrent--; if(lifeCurrent <= 0){ ClientEntityUtils.destroyEntity(parent); + Globals.clientSceneWrapper.getScene().deregisterBehaviorTree(this); } } @@ -104,6 +116,19 @@ public class ClientParticleTree implements BehaviorTree { Matrix4f rotationMatrix = new Matrix4f(Globals.viewMatrix).invert(); Quaternionf rotation = new Quaternionf(rotationMatrix.getRotation(new AxisAngle4f())); EntityUtils.getRotation(parent).set(rotation); + + Vector3f scale = EntityUtils.getScale(parent); + scale.set(this.particleData.getSize()); + + //push values to buffer that eventually gets uploaded to gpu + if(instancedActor != null){ + instancedActor.setAttribute(Globals.particleService.getModelAttrib(), new Matrix4f().translationRotateScale( + new Vector3f((float)parentPosition.x,(float)parentPosition.y,(float)parentPosition.z).sub(cameraPos), + new Quaternionf(rotation), + scale + )); + instancedActor.setAttribute(Globals.particleService.getColorAttrib(), new Vector4f((float)this.color.x,(float)this.color.y,(float)this.color.z,1.0f)); + } } /** diff --git a/src/main/java/electrosphere/entity/state/light/ClientPointLightComponent.java b/src/main/java/electrosphere/entity/state/light/ClientPointLightComponent.java index edbf3375..f3434c22 100644 --- a/src/main/java/electrosphere/entity/state/light/ClientPointLightComponent.java +++ b/src/main/java/electrosphere/entity/state/light/ClientPointLightComponent.java @@ -84,7 +84,7 @@ public class ClientPointLightComponent implements BehaviorTree { * @return The ClientEquipState */ public static ClientEquipState getClientEquipState(Entity entity){ - return (ClientEquipState)entity.getData(EntityDataStrings.TREE_CLIENTEQUIPSTATE); + return (ClientEquipState)entity.getData(EntityDataStrings.TREE_CLIENTLIGHTSTATE); } } diff --git a/src/main/java/electrosphere/entity/types/common/CommonEntityUtils.java b/src/main/java/electrosphere/entity/types/common/CommonEntityUtils.java index 7909f81c..5110a1c8 100644 --- a/src/main/java/electrosphere/entity/types/common/CommonEntityUtils.java +++ b/src/main/java/electrosphere/entity/types/common/CommonEntityUtils.java @@ -20,6 +20,7 @@ import electrosphere.entity.state.attack.ServerAttackTree; import electrosphere.entity.state.attack.ShooterTree; import electrosphere.entity.state.block.ClientBlockTree; import electrosphere.entity.state.block.ServerBlockTree; +import electrosphere.entity.state.client.particle.ClientParticleEmitterComponent; import electrosphere.entity.state.equip.ClientEquipState; import electrosphere.entity.state.equip.ServerEquipState; import electrosphere.entity.state.gravity.ClientGravityTree; @@ -257,6 +258,15 @@ public class CommonEntityUtils { ClientPointLightComponent.attachTree(entity, rawType.getPointLight()); } + // + // + // Particle emitter + // + // + if(rawType.getParticleEmitter() != null){ + ClientParticleEmitterComponent.attachTree(entity, rawType.getParticleEmitter()); + } + if(rawType.getEquipPoints() != null && rawType.getEquipPoints().size() > 0){ ClientEquipState.attachTree(entity, rawType.getEquipPoints()); entity.putData(EntityDataStrings.EQUIP_INVENTORY, RelationalInventoryState.buildRelationalInventoryStateFromEquipList(rawType.getEquipPoints())); diff --git a/src/main/java/electrosphere/game/data/common/CommonEntityType.java b/src/main/java/electrosphere/game/data/common/CommonEntityType.java index 00df8acc..60ac0235 100644 --- a/src/main/java/electrosphere/game/data/common/CommonEntityType.java +++ b/src/main/java/electrosphere/game/data/common/CommonEntityType.java @@ -21,6 +21,7 @@ import electrosphere.game.data.foliage.type.AmbientAudio; import electrosphere.game.data.foliage.type.GrowthModel; import electrosphere.game.data.foliage.type.TreeModel; import electrosphere.game.data.graphics.GraphicsTemplate; +import electrosphere.game.data.particle.ParticleEmitter; /** * Common data that all entity types use @@ -137,6 +138,11 @@ public class CommonEntityType { */ PointLightDescription pointLight; + /** + * The particle emitter assigned to the entity + */ + ParticleEmitter particleEmitter; + /** * Gets the id for this creature type * @return The id @@ -321,4 +327,12 @@ public class CommonEntityType { return pointLight; } + /** + * Gets the particle emitter data + * @return The particle emitter data + */ + public ParticleEmitter getParticleEmitter(){ + return particleEmitter; + } + } diff --git a/src/main/java/electrosphere/game/data/particle/ParticleData.java b/src/main/java/electrosphere/game/data/particle/ParticleData.java index cd2dbc7c..7c0bdf70 100644 --- a/src/main/java/electrosphere/game/data/particle/ParticleData.java +++ b/src/main/java/electrosphere/game/data/particle/ParticleData.java @@ -1,5 +1,7 @@ package electrosphere.game.data.particle; +import org.joml.Vector3d; + /** * Data on how a particle should behave */ @@ -16,7 +18,6 @@ public class ParticleData { */ Integer maxLife; - /** * The life the particle starts with */ @@ -26,7 +27,7 @@ public class ParticleData { /** * The initial velocity of the particle */ - Float velocity; + Vector3d velocity; /** * The acceleration of the particle @@ -43,6 +44,11 @@ public class ParticleData { */ Float size; + /** + * The color of the particle + */ + Vector3d color; + /** * Gets the max life of the particle * @return The max life @@ -63,7 +69,7 @@ public class ParticleData { * Gets the starting velocity of the particle * @return The starting velocity */ - public Float getVelocity(){ + public Vector3d getVelocity(){ return velocity; } @@ -99,4 +105,38 @@ public class ParticleData { return size; } + public void setName(String name) { + this.name = name; + } + + public void setMaxLife(Integer maxLife) { + this.maxLife = maxLife; + } + + public void setLifeCurrent(Integer lifeCurrent) { + this.lifeCurrent = lifeCurrent; + } + + public void setVelocity(Vector3d velocity) { + this.velocity = velocity; + } + + public void setAcceleration(Float acceleration) { + this.acceleration = acceleration; + } + + public void setTexture(String texture) { + this.texture = texture; + } + + public void setSize(Float size) { + this.size = size; + } + + public void setColor(Vector3d color) { + this.color = color; + } + + + } diff --git a/src/main/java/electrosphere/game/data/particle/ParticleEmitter.java b/src/main/java/electrosphere/game/data/particle/ParticleEmitter.java new file mode 100644 index 00000000..47cc004b --- /dev/null +++ b/src/main/java/electrosphere/game/data/particle/ParticleEmitter.java @@ -0,0 +1,166 @@ +package electrosphere.game.data.particle; + +import org.joml.Vector3d; + +/** + * Describes a particle emitter + */ +public class ParticleEmitter { + + /** + * The name of the particle type + */ + String name; + + /** + * The maximum life of the particle + */ + Integer maxLife; + + /** + * The life the particle starts with + */ + Integer lifeCurrent; + + + /** + * The initial velocity of the particle + */ + Vector3d particleVelocity; + + /** + * The acceleration of the particle + */ + Float acceleration; + + /** + * The texture of the particle + */ + String texture; + + /** + * The size of the particle + */ + Float size; + + /** + * The color of the particle + */ + Vector3d color; + + /** + * The number of particles to emit per frame + */ + Float frequency; + + /** + * Gets the max life of the particle + * @return The max life + */ + public Integer getMaxLife(){ + return maxLife; + } + + /** + * Gets the starting life of the particle + * @return The starting life + */ + public Integer getLifeCurrent(){ + return lifeCurrent; + } + + /** + * Gets the starting velocity of the particle + * @return The starting velocity + */ + public Vector3d getParticleVelocity(){ + return particleVelocity; + } + + /** + * Gets the acceleration of the particle + * @return The acceleration + */ + public Float getAcceleration(){ + return acceleration; + } + + /** + * Gets the texture of the particle + * @return The texture + */ + public String getTexture(){ + return texture; + } + + /** + * Gets the name of the particle type + * @return The name of the particle type + */ + public String getName(){ + return name; + } + + /** + * Gets the size of the particle + * @return The size of the particle + */ + public Float getSize(){ + return size; + } + + /** + * Gets the number of particles to emit per frame + * @return The number of particles to emit per frame + */ + public Float getFrequency(){ + return frequency; + } + + /** + * Gets the color of the particles generated by the emitted + * @return The color + */ + public Vector3d getColor(){ + return color; + } + + public void setName(String name) { + this.name = name; + } + + public void setMaxLife(Integer maxLife) { + this.maxLife = maxLife; + } + + public void setLifeCurrent(Integer lifeCurrent) { + this.lifeCurrent = lifeCurrent; + } + + public void setParticleVelocity(Vector3d particleVelocity) { + this.particleVelocity = particleVelocity; + } + + public void setAcceleration(Float acceleration) { + this.acceleration = acceleration; + } + + public void setTexture(String texture) { + this.texture = texture; + } + + public void setSize(Float size) { + this.size = size; + } + + public void setColor(Vector3d color) { + this.color = color; + } + + public void setFrequency(Float frequency){ + this.frequency = frequency; + } + + + +} diff --git a/src/main/java/electrosphere/renderer/actor/instance/InstanceManager.java b/src/main/java/electrosphere/renderer/actor/instance/InstanceManager.java index 0896c449..fed2b802 100644 --- a/src/main/java/electrosphere/renderer/actor/instance/InstanceManager.java +++ b/src/main/java/electrosphere/renderer/actor/instance/InstanceManager.java @@ -5,12 +5,11 @@ import java.util.LinkedList; import java.util.List; import java.util.Map; - import electrosphere.engine.Globals; import electrosphere.renderer.OpenGLState; import electrosphere.renderer.RenderPipelineState; -import electrosphere.renderer.buffer.ShaderAttribute; import electrosphere.renderer.buffer.HomogenousUniformBuffer.HomogenousBufferTypes; +import electrosphere.renderer.buffer.ShaderAttribute; import electrosphere.renderer.model.Model; import electrosphere.renderer.shader.ShaderProgram; @@ -67,6 +66,33 @@ public class InstanceManager { return rVal; } + /** + * Creates an instanced actor of a model at a given model path + * @param modelPath The path for the model + * @return The instanced actor + */ + public InstancedActor createInstancedActor( + String modelPath, + String vertexShaderPath, + String fragmentShaderPath, + StridedInstanceData instanceData, + int capacity + ){ + InstancedActor rVal = new InstancedActor(modelPath); + if(!pathToInstanceData.containsKey(modelPath)){ + //queue shader + Globals.assetManager.addShaderToQueue(vertexShaderPath, fragmentShaderPath); + //if asset manager doesn't have model, queue model + if(Globals.assetManager.fetchModel(modelPath) == null){ + Globals.assetManager.addModelPathToQueue(modelPath); + } + //register to internal-to-manager datastructures + pathToInstanceData.put(modelPath,instanceData); + modelsToDraw.add(modelPath); + } + return rVal; + } + /** * Draws all models that are queued in this instance manager */ diff --git a/src/main/java/electrosphere/renderer/actor/instance/InstancedActor.java b/src/main/java/electrosphere/renderer/actor/instance/InstancedActor.java index 1ccad3c5..0930cbb8 100644 --- a/src/main/java/electrosphere/renderer/actor/instance/InstancedActor.java +++ b/src/main/java/electrosphere/renderer/actor/instance/InstancedActor.java @@ -6,9 +6,13 @@ import java.util.Map; import org.joml.Matrix4d; import org.joml.Matrix4f; +import org.joml.Quaterniond; +import org.joml.Quaternionf; import org.joml.Sphered; import org.joml.Vector3d; import org.joml.Vector3f; +import org.joml.Vector4d; +import org.joml.Vector4f; import electrosphere.engine.Globals; import electrosphere.entity.Entity; @@ -116,8 +120,18 @@ public class InstancedActor implements Comparable { attributes.put(attribute, value); } else if(value instanceof Vector3f){ attributes.put(attribute, value); + } else if(value instanceof Vector3d){ + attributes.put(attribute, value); + } else if(value instanceof Vector4d){ + attributes.put(attribute, value); + } else if(value instanceof Vector4f){ + attributes.put(attribute, value); + } else if(value instanceof Quaterniond){ + attributes.put(attribute, value); + } else if(value instanceof Quaternionf){ + attributes.put(attribute, value); } else { - LoggerInterface.loggerRenderer.ERROR("Unsupported operation", new Exception()); + LoggerInterface.loggerRenderer.ERROR("Unsupported operation " + value, new Exception()); } // attributes.put(attribute, value); } diff --git a/src/main/java/electrosphere/renderer/actor/instance/StridedInstanceData.java b/src/main/java/electrosphere/renderer/actor/instance/StridedInstanceData.java index 75277f25..2da43ffa 100644 --- a/src/main/java/electrosphere/renderer/actor/instance/StridedInstanceData.java +++ b/src/main/java/electrosphere/renderer/actor/instance/StridedInstanceData.java @@ -13,10 +13,10 @@ import org.joml.Vector3f; import org.joml.Vector4d; import org.joml.Vector4f; -import electrosphere.renderer.buffer.HomogenousUniformBuffer.HomogenousBufferTypes; import electrosphere.renderer.buffer.HomogenousUniformBuffer; import electrosphere.renderer.buffer.ShaderAttribute; import electrosphere.renderer.buffer.ShaderStorageBuffer; +import electrosphere.logger.LoggerInterface; import electrosphere.renderer.OpenGLState; import electrosphere.renderer.RenderPipelineState; import electrosphere.renderer.buffer.BufferEnums.BufferAccess; @@ -27,7 +27,7 @@ import electrosphere.renderer.buffer.BufferEnums.BufferUsage; */ public class StridedInstanceData implements InstanceData { - //the capacity (number of instanced) of this data block. Defaults to 100 + //the capacity (number of instances) of this data block. Defaults to 1000 int capacity = 1000; //shader paths @@ -45,35 +45,60 @@ public class StridedInstanceData implements InstanceData { Map indexActorMap = new HashMap(); //list of all attribute indices in use by this instance data - List attributeIndices = new LinkedList(); + List attributes; //the SSBO ShaderStorageBuffer buffer; + /** + * The index to bind to + */ + int bindPoint; + /** * Constructor * @param capacity Capacity of the buffer (number of elements) backing this data */ - public StridedInstanceData(int capacity, List types, String vertexPath, String fragmentPath){ + public StridedInstanceData(int capacity, int bindPoint, List attributes, String vertexPath, String fragmentPath){ int entrySize = 0; - for(HomogenousBufferTypes type : types){ - entrySize = entrySize + HomogenousUniformBuffer.calculateTypeSize(type); + boolean hitMat = false; + boolean hitVec = false; + boolean hitPrimitive = false; + for(ShaderAttribute attribute : attributes){ + entrySize = entrySize + HomogenousUniformBuffer.calculateTypeSize(attribute.getType()); + switch(attribute.getType()){ + case MAT4D: + case MAT4F: { + hitMat = true; + } break; + case VEC3D: + case VEC3F: + case VEC4D: + case VEC4F: { + hitVec = true; + } break; + case INT: + case DOUBLE: + case FLOAT: { + hitPrimitive = true; + } break; + } } + if(hitPrimitive && (hitMat || hitVec)){ + String message = "Warning! You are mixing a larger alignment type (vec, mat) with primitives (int, float)!\n" + + "This can potentially cause alignment bugs. See hhttps://learnopengl.com/Advanced-OpenGL/Advanced-GLSL or \n" + + "https://www.khronos.org/opengl/wiki/Interface_Block_(GLSL)#Memory_layout for details on memory layout!" + ; + LoggerInterface.loggerRenderer.WARNING(message); + } + this.attributes = attributes; this.capacity = capacity; this.vertexShaderPath = vertexPath; this.fragmentShaderPath = fragmentPath; actorQueue = new LinkedList(); this.buffer = new ShaderStorageBuffer(capacity * entrySize, BufferUsage.STREAM, BufferAccess.DRAW); - } - - /** - * Adds a new type of attribute to this instance data block - * @param shaderAttribute The shader attribute - * @param type The type of the attribute - */ - protected void addDataType(ShaderAttribute shaderAttribute, HomogenousBufferTypes type){ - attributeIndices.add(shaderAttribute); + this.bindPoint = bindPoint; } @@ -108,90 +133,96 @@ public class StridedInstanceData implements InstanceData { public void fillBuffers(){ int i = 0; ByteBuffer byteBuff = this.buffer.getBuffer(); + byteBuff.limit(byteBuff.capacity()); //buffer data for(InstancedActor actor : actorQueue){ //push values to attribute buffers - for(ShaderAttribute attribute : attributeIndices){ - switch(attribute.getType()){ - case VEC3F: { - Vector3f vec = (Vector3f)actor.getAttributeValue(attribute); - byteBuff.putFloat(vec.x); - byteBuff.putFloat(vec.y); - byteBuff.putFloat(vec.z); - } break; - case VEC3D: { - Vector3d vec = (Vector3d)actor.getAttributeValue(attribute); - byteBuff.putDouble(vec.x); - byteBuff.putDouble(vec.y); - byteBuff.putDouble(vec.z); - } break; - case VEC4F: { - Vector4f vec = (Vector4f)actor.getAttributeValue(attribute); - byteBuff.putFloat(vec.w); - byteBuff.putFloat(vec.x); - byteBuff.putFloat(vec.y); - byteBuff.putFloat(vec.z); - } break; - case VEC4D: { - Vector4d vec = (Vector4d)actor.getAttributeValue(attribute); - byteBuff.putDouble(vec.w); - byteBuff.putDouble(vec.x); - byteBuff.putDouble(vec.y); - byteBuff.putDouble(vec.z); - } break; - case MAT4F: { - Matrix4f mat = (Matrix4f)actor.getAttributeValue(attribute); - byteBuff.putFloat(mat.m00()); - byteBuff.putFloat(mat.m01()); - byteBuff.putFloat(mat.m02()); - byteBuff.putFloat(mat.m03()); + for(ShaderAttribute attribute : attributes){ + if(actor.getAttributeValue(attribute) != null){ + switch(attribute.getType()){ + case VEC3F: { + Vector3f vec = (Vector3f)actor.getAttributeValue(attribute); + byteBuff.putFloat(vec.x); + byteBuff.putFloat(vec.y); + byteBuff.putFloat(vec.z); + } break; + case VEC3D: { + Vector3d vec = (Vector3d)actor.getAttributeValue(attribute); + byteBuff.putDouble(vec.x); + byteBuff.putDouble(vec.y); + byteBuff.putDouble(vec.z); + } break; + case VEC4F: { + Vector4f vec = (Vector4f)actor.getAttributeValue(attribute); + byteBuff.putFloat(vec.w); + byteBuff.putFloat(vec.x); + byteBuff.putFloat(vec.y); + byteBuff.putFloat(vec.z); + } break; + case VEC4D: { + Vector4d vec = (Vector4d)actor.getAttributeValue(attribute); + byteBuff.putDouble(vec.w); + byteBuff.putDouble(vec.x); + byteBuff.putDouble(vec.y); + byteBuff.putDouble(vec.z); + } break; + case MAT4F: { + Matrix4f mat = (Matrix4f)actor.getAttributeValue(attribute); + byteBuff.putFloat(mat.m00()); + byteBuff.putFloat(mat.m01()); + byteBuff.putFloat(mat.m02()); + byteBuff.putFloat(mat.m03()); - byteBuff.putFloat(mat.m10()); - byteBuff.putFloat(mat.m11()); - byteBuff.putFloat(mat.m12()); - byteBuff.putFloat(mat.m13()); - - byteBuff.putFloat(mat.m20()); - byteBuff.putFloat(mat.m21()); - byteBuff.putFloat(mat.m22()); - byteBuff.putFloat(mat.m23()); + byteBuff.putFloat(mat.m10()); + byteBuff.putFloat(mat.m11()); + byteBuff.putFloat(mat.m12()); + byteBuff.putFloat(mat.m13()); + + byteBuff.putFloat(mat.m20()); + byteBuff.putFloat(mat.m21()); + byteBuff.putFloat(mat.m22()); + byteBuff.putFloat(mat.m23()); - byteBuff.putFloat(mat.m30()); - byteBuff.putFloat(mat.m31()); - byteBuff.putFloat(mat.m32()); - byteBuff.putFloat(mat.m33()); - } break; - case MAT4D: { - Matrix4d mat = (Matrix4d)actor.getAttributeValue(attribute); - byteBuff.putDouble((float)mat.m00()); - byteBuff.putDouble((float)mat.m01()); - byteBuff.putDouble((float)mat.m02()); - byteBuff.putDouble((float)mat.m03()); + byteBuff.putFloat(mat.m30()); + byteBuff.putFloat(mat.m31()); + byteBuff.putFloat(mat.m32()); + byteBuff.putFloat(mat.m33()); + } break; + case MAT4D: { + Matrix4d mat = (Matrix4d)actor.getAttributeValue(attribute); + byteBuff.putDouble((float)mat.m00()); + byteBuff.putDouble((float)mat.m01()); + byteBuff.putDouble((float)mat.m02()); + byteBuff.putDouble((float)mat.m03()); - byteBuff.putDouble((float)mat.m10()); - byteBuff.putDouble((float)mat.m11()); - byteBuff.putDouble((float)mat.m12()); - byteBuff.putDouble((float)mat.m13()); - - byteBuff.putDouble((float)mat.m20()); - byteBuff.putDouble((float)mat.m21()); - byteBuff.putDouble((float)mat.m22()); - byteBuff.putDouble((float)mat.m23()); + byteBuff.putDouble((float)mat.m10()); + byteBuff.putDouble((float)mat.m11()); + byteBuff.putDouble((float)mat.m12()); + byteBuff.putDouble((float)mat.m13()); + + byteBuff.putDouble((float)mat.m20()); + byteBuff.putDouble((float)mat.m21()); + byteBuff.putDouble((float)mat.m22()); + byteBuff.putDouble((float)mat.m23()); - byteBuff.putDouble((float)mat.m30()); - byteBuff.putDouble((float)mat.m31()); - byteBuff.putDouble((float)mat.m32()); - byteBuff.putDouble((float)mat.m33()); - } break; - case DOUBLE: { - byteBuff.putDouble((Double)actor.getAttributeValue(attribute)); - } break; - case FLOAT: { - byteBuff.putFloat((Float)actor.getAttributeValue(attribute)); - } break; - case INT: { - byteBuff.putInt((Integer)actor.getAttributeValue(attribute)); - } break; + byteBuff.putDouble((float)mat.m30()); + byteBuff.putDouble((float)mat.m31()); + byteBuff.putDouble((float)mat.m32()); + byteBuff.putDouble((float)mat.m33()); + } break; + case DOUBLE: { + byteBuff.putDouble((Double)actor.getAttributeValue(attribute)); + } break; + case FLOAT: { + byteBuff.putFloat((Float)actor.getAttributeValue(attribute)); + } break; + case INT: { + byteBuff.putInt((Integer)actor.getAttributeValue(attribute)); + } break; + default: { + throw new Error("Unhandled attribute type!"); + } + } } } //increment @@ -224,7 +255,8 @@ public class StridedInstanceData implements InstanceData { @Override public void upload(OpenGLState openGLState, RenderPipelineState renderPipelineState){ this.buffer.upload(openGLState); - openGLState.glBindBufferBase(0, buffer); + openGLState.glBindBufferBase(this.bindPoint, buffer); + renderPipelineState.setInstanceCount(this.getDrawCount()); } @Override diff --git a/src/main/java/electrosphere/renderer/buffer/ShaderAttribute.java b/src/main/java/electrosphere/renderer/buffer/ShaderAttribute.java index 8a1313d5..6606d9e8 100644 --- a/src/main/java/electrosphere/renderer/buffer/ShaderAttribute.java +++ b/src/main/java/electrosphere/renderer/buffer/ShaderAttribute.java @@ -15,6 +15,11 @@ public class ShaderAttribute { //for multi-attribute index types (mat4f, mat4d, etc) int[] attributeIndices; + /** + * The name of the attribute + */ + String name; + /** * The type of the attribute */ @@ -39,10 +44,11 @@ public class ShaderAttribute { /** * Constructor for 1-1 attribute - * @param attributeIndex The attribute index + * @param name The name of the attribute + * @param type The type of attribute */ - public ShaderAttribute(int attributeIndex, HomogenousBufferTypes type){ - this.attributeIndex = attributeIndex; + public ShaderAttribute(String name, HomogenousBufferTypes type){ + this.name = name; this.type = type; } @@ -78,4 +84,12 @@ public class ShaderAttribute { return type; } + /** + * Gets the name of the attribute + * @return The name + */ + public String getName(){ + return name; + } + }