diff --git a/assets/Data/entity/objects/game_objects.json b/assets/Data/entity/objects/game_objects.json index 94cec451..4e3ce895 100644 --- a/assets/Data/entity/objects/game_objects.json +++ b/assets/Data/entity/objects/game_objects.json @@ -19,7 +19,7 @@ "z": 0.0 }, "acceleration": -0.01, - "texture": "", + "texture": "blood1.png", "size": 0.3, "color": { "x": 0.5, diff --git a/assets/Shaders/entities/particle/particle.fs b/assets/Shaders/entities/particle/particle.fs index 9a1a6082..cbc97b17 100644 --- a/assets/Shaders/entities/particle/particle.fs +++ b/assets/Shaders/entities/particle/particle.fs @@ -50,7 +50,8 @@ struct Cluster { uint lightIndices[MAX_LIGHTS_PER_CLUSTER]; }; -out vec4 FragColor; +layout (location = 0) out vec4 accum; +layout (location = 1) out float reveal; layout(std430, binding = CLUSTER_SSBO_BIND_POINT) restrict buffer clusterGridSSBO { @@ -119,7 +120,7 @@ void main(){ //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; + vec4 textureColor = texture(material.diffuse,TexCoord); // vec3 textureColor = vec3(0.17647,0.4,0.09411);//texture(material.diffuse, TexCoord).rgb; //shadow @@ -136,19 +137,25 @@ void main(){ } //error checking on light clusters if(pointLightCount > MAX_LIGHTS_PER_CLUSTER){ - FragColor = vec4(1.0f,0.0f,0.0f,1); + accum = vec4(1.0f,0.0f,0.0f,1); + reveal = textureColor.a; return; } //calculate final color - vec3 finalColor = textureColor * lightIntensity;// * max(shadow,0.4); + vec4 finalColor = textureColor.rgba * vec4(lightIntensity,1.0);// * 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); + //calculate weight function + float weight = clamp(pow(min(1.0, finalColor.a * 10.0) + 0.01, 3.0) * 1e8 * + pow(1.0 - gl_FragCoord.z * 0.9, 3.0), 1e-2, 3e3); + + //emit colors + accum = vec4(finalColor.rgb * finalColor.a, finalColor.a) * weight; + reveal = finalColor.a; } // calculates the color when using a directional light. diff --git a/assets/Shaders/entities/particle/particle.vs b/assets/Shaders/entities/particle/particle.vs index cd469ce1..1db9db09 100644 --- a/assets/Shaders/entities/particle/particle.vs +++ b/assets/Shaders/entities/particle/particle.vs @@ -12,6 +12,7 @@ A point light struct ParticleData { mat4 model; vec4 color; + vec4 texture; }; @@ -64,7 +65,9 @@ void main() { FragPos = vec3(model * FinalVertex); ViewFragPos = vec3(view * model * FinalVertex); Normal = mat3(transpose(inverse(model))) * aNormal; - TexCoord = aTex; + + //offset based on data stored in particle data + TexCoord = (aTex * currentData.texture.xy) + currentData.texture.zw; //shadow map stuff diff --git a/buildNumber.properties b/buildNumber.properties index 106ed524..ee41aa98 100644 --- a/buildNumber.properties +++ b/buildNumber.properties @@ -1,3 +1,3 @@ #maven.buildNumber.plugin properties file -#Thu Sep 19 21:21:28 EDT 2024 -buildNumber=351 +#Thu Sep 19 23:04:32 EDT 2024 +buildNumber=355 diff --git a/docs/src/progress/renderertodo.md b/docs/src/progress/renderertodo.md index 250c8c02..95276e03 100644 --- a/docs/src/progress/renderertodo.md +++ b/docs/src/progress/renderertodo.md @@ -1018,6 +1018,8 @@ Upgrade terrain generation algorithms Documentation +Test Creation + diff --git a/src/main/java/electrosphere/client/entity/particle/ParticleService.java b/src/main/java/electrosphere/client/entity/particle/ParticleService.java index b6759aaf..5f36c293 100644 --- a/src/main/java/electrosphere/client/entity/particle/ParticleService.java +++ b/src/main/java/electrosphere/client/entity/particle/ParticleService.java @@ -10,6 +10,7 @@ import electrosphere.client.entity.instance.InstancedEntityUtils; import electrosphere.engine.assetmanager.AssetDataStrings; import electrosphere.engine.signal.Signal; import electrosphere.engine.signal.SignalServiceImpl; +import electrosphere.entity.DrawableUtils; import electrosphere.entity.Entity; import electrosphere.entity.EntityCreationUtils; import electrosphere.entity.EntityUtils; @@ -17,6 +18,7 @@ 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.texture.TextureAtlas; import electrosphere.renderer.buffer.ShaderAttribute; /** @@ -44,6 +46,11 @@ public class ParticleService extends SignalServiceImpl { */ static final String FRAGMENT_SHADER_PATH = "Shaders/entities/particle/particle.fs"; + /** + * The particle texture atlas + */ + TextureAtlas particleTextureAtlas; + /** * The instance data for the particles @@ -60,6 +67,8 @@ public class ParticleService extends SignalServiceImpl { ShaderAttribute colorAttrib; + ShaderAttribute textureAttrib; + /** * Constructor */ @@ -82,9 +91,11 @@ public class ParticleService extends SignalServiceImpl { case RENDERING_ENGINE_READY: { modelAttrib = new ShaderAttribute("model", HomogenousBufferTypes.MAT4F); colorAttrib = new ShaderAttribute("color", HomogenousBufferTypes.VEC4F); + textureAttrib = new ShaderAttribute("texture", HomogenousBufferTypes.VEC4F); List types = Arrays.asList(new ShaderAttribute[]{ modelAttrib, colorAttrib, + textureAttrib, }); 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); @@ -104,6 +115,22 @@ public class ParticleService extends SignalServiceImpl { return this.particleInstanceData; } + /** + * Sets the particle texture atlas + * @param particleTextureAtlas The particle texture atlas + */ + public void setParticleTextureAtlas(TextureAtlas particleTextureAtlas){ + this.particleTextureAtlas = particleTextureAtlas; + } + + /** + * Sets the texture atlas for the particle texture + * @return The atlas + */ + public TextureAtlas getTextureAtlas(){ + return particleTextureAtlas; + } + /** * Spawns a particle * @param data The particle data for the particle @@ -113,6 +140,7 @@ public class ParticleService extends SignalServiceImpl { public Entity spawn(ParticleData data, Vector3d position){ Entity rVal = EntityCreationUtils.createClientSpatialEntity(); InstancedEntityUtils.makeEntityInstanced(rVal, instanceTemplate); + DrawableUtils.makeEntityTransparent(rVal); EntityUtils.getPosition(rVal).set(position); ClientParticleTree.attachTree(rVal, data); return rVal; @@ -125,5 +153,9 @@ public class ParticleService extends SignalServiceImpl { public ShaderAttribute getColorAttrib(){ return colorAttrib; } + + public ShaderAttribute getTextureAttrib(){ + return textureAttrib; + } } diff --git a/src/main/java/electrosphere/engine/assetmanager/AssetDataStrings.java b/src/main/java/electrosphere/engine/assetmanager/AssetDataStrings.java index bc67d98f..c4ebf5f9 100644 --- a/src/main/java/electrosphere/engine/assetmanager/AssetDataStrings.java +++ b/src/main/java/electrosphere/engine/assetmanager/AssetDataStrings.java @@ -15,7 +15,8 @@ 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"; + public static final String MODEL_PARTICLE = "particleModel"; + public static final String TEXTURE_PARTICLE = "particleTexture"; /** * UI generic audio diff --git a/src/main/java/electrosphere/engine/assetmanager/AssetManager.java b/src/main/java/electrosphere/engine/assetmanager/AssetManager.java index c244432b..2b4db1d3 100644 --- a/src/main/java/electrosphere/engine/assetmanager/AssetManager.java +++ b/src/main/java/electrosphere/engine/assetmanager/AssetManager.java @@ -338,6 +338,15 @@ public class AssetManager { return rVal; } + /** + * Registers a texture to a path + * @param t The texture + * @param path The path + */ + public void registerTextureToPath(Texture t, String path){ + texturesLoadedIntoMemory.put(path,t); + } + public boolean hasLoadedTexture(String path){ return texturesLoadedIntoMemory.containsKey(path); } diff --git a/src/main/java/electrosphere/engine/loadingthreads/InitialAssetLoading.java b/src/main/java/electrosphere/engine/loadingthreads/InitialAssetLoading.java index dffff3ae..63d45fa0 100644 --- a/src/main/java/electrosphere/engine/loadingthreads/InitialAssetLoading.java +++ b/src/main/java/electrosphere/engine/loadingthreads/InitialAssetLoading.java @@ -2,17 +2,21 @@ package electrosphere.engine.loadingthreads; import java.awt.Graphics; import java.awt.image.BufferedImage; +import java.io.File; import java.io.IOException; +import java.nio.file.Files; import java.util.concurrent.TimeUnit; import javax.imageio.ImageIO; import electrosphere.client.terrain.cells.VoxelTextureAtlas; import electrosphere.engine.Globals; +import electrosphere.engine.assetmanager.AssetDataStrings; import electrosphere.engine.assetmanager.queue.QueuedTexture; import electrosphere.game.data.voxel.VoxelData; import electrosphere.game.data.voxel.VoxelType; import electrosphere.logger.LoggerInterface; +import electrosphere.renderer.texture.TextureAtlas; import electrosphere.util.FileUtils; /** @@ -33,7 +37,8 @@ public class InitialAssetLoading { */ protected static void loadData(){ - loadTextureAtlas(); + loadVoxelTextureAtlas(); + loadParticleAtlas(); LoggerInterface.loggerEngine.INFO("Finished loading texture atlas"); } @@ -41,7 +46,7 @@ public class InitialAssetLoading { /** * Loads the texture atlas */ - private static void loadTextureAtlas(){ + private static void loadVoxelTextureAtlas(){ //terrain texture atlas Globals.profiler.beginCpuSample("createVoxelTextureAtlas"); VoxelData data = Globals.gameConfigCurrent.getVoxelData(); @@ -89,6 +94,88 @@ public class InitialAssetLoading { Globals.voxelTextureAtlas.setNormal(atlasQueuedTexture.getTexture()); } + /** + * Loads the texture atlas + */ + private static void loadParticleAtlas(){ + //terrain texture atlas + Globals.profiler.beginCpuSample("create particle texture atlas"); + int iterator = 0; + BufferedImage image = null; + TextureAtlas textureAtlas = new TextureAtlas(); + File particleTextureDirectory = FileUtils.getAssetFile("Textures/particles"); + File cachedAtlas = FileUtils.getCacheFile("particleAtlas.png"); + if(cachedAtlas.exists()){ + try { + image = ImageIO.read(Files.newInputStream(cachedAtlas.toPath())); + for(File childFile : particleTextureDirectory.listFiles()){ + if(!childFile.isDirectory()){ + //put coords in map + textureAtlas.putPathCoord(childFile.getName(),iterator); + + //iterate + iterator++; + } + } + } catch (IOException e) { + e.printStackTrace(); + throw new Error("Failed to load texture atlas from cache!"); + } + } else { + cachedAtlas.getParentFile().mkdirs(); + image = new BufferedImage(TextureAtlas.ATLAS_DIM, TextureAtlas.ATLAS_DIM, BufferedImage.TYPE_4BYTE_ABGR); + Graphics graphics = image.getGraphics(); + for(File childFile : particleTextureDirectory.listFiles()){ + if(!childFile.isDirectory()){ + String texturePath = "Textures/particles/" + childFile.getName(); + int offX = iterator % TextureAtlas.ELEMENTS_PER_ROW; + int offY = iterator / TextureAtlas.ELEMENTS_PER_ROW; + try { + BufferedImage newType = ImageIO.read(FileUtils.getAssetFile(texturePath)); + int drawX = TextureAtlas.ATLAS_ELEMENT_DIM * offX; + int drawY = TextureAtlas.ATLAS_DIM - TextureAtlas.ATLAS_ELEMENT_DIM - TextureAtlas.ATLAS_ELEMENT_DIM * offY; + graphics.drawImage(newType, drawX, drawY, TextureAtlas.ATLAS_ELEMENT_DIM, TextureAtlas.ATLAS_ELEMENT_DIM, null); + } catch (IOException e) { + LoggerInterface.loggerRenderer.ERROR("Texture atlas failed to find texture " + texturePath, e); + } + //put coords in map + textureAtlas.putPathCoord(childFile.getName(),iterator); + + //iterate + iterator++; + } + } + try { + ImageIO.write(image, "png", Files.newOutputStream(cachedAtlas.toPath())); + } catch (IOException e) { + e.printStackTrace(); + throw new Error("Failed to save to cache!"); + } + } + Globals.profiler.endCpuSample(); + + //queue to asset manager + atlasQueuedTexture = new QueuedTexture(image); + Globals.assetManager.queuedAsset(atlasQueuedTexture); + + + //wait the texture to be loaded + while(!atlasQueuedTexture.hasLoaded()){ + try { + TimeUnit.MILLISECONDS.sleep(1); + } catch (InterruptedException e) { + LoggerInterface.loggerEngine.ERROR("failed to sleep", e); + } + } + + //construct texture atlas from buffered image + Globals.assetManager.registerTextureToPath(atlasQueuedTexture.getTexture(), AssetDataStrings.TEXTURE_PARTICLE); + textureAtlas.setSpecular(atlasQueuedTexture.getTexture()); + textureAtlas.setNormal(atlasQueuedTexture.getTexture()); + + Globals.particleService.setParticleTextureAtlas(textureAtlas); + } + /** * Gets the queued texture */ diff --git a/src/main/java/electrosphere/entity/state/client/particle/ClientParticleTree.java b/src/main/java/electrosphere/entity/state/client/particle/ClientParticleTree.java index 882a45e4..bf520b2a 100644 --- a/src/main/java/electrosphere/entity/state/client/particle/ClientParticleTree.java +++ b/src/main/java/electrosphere/entity/state/client/particle/ClientParticleTree.java @@ -18,6 +18,7 @@ import electrosphere.entity.EntityUtils; import electrosphere.entity.btree.BehaviorTree; import electrosphere.game.data.particle.ParticleData; import electrosphere.renderer.actor.instance.InstancedActor; +import electrosphere.renderer.texture.TextureAtlas; /** * Particle component for a client-side particle @@ -122,12 +123,23 @@ public class ClientParticleTree implements BehaviorTree { //push values to buffer that eventually gets uploaded to gpu if(instancedActor != null){ + TextureAtlas particleTextureAtlas = Globals.particleService.getTextureAtlas(); + int textureIndex = particleTextureAtlas.getTextureIndex(this.particleData.getTexture()); 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)); + + //when written to buffer, will be written in order w, x, y, z + //but gpu will fetch in order x, y, z, w + instancedActor.setAttribute(Globals.particleService.getTextureAttrib(), new Vector4f( + particleTextureAtlas.getNDCDimension(), + particleTextureAtlas.getNDCCoordX(textureIndex), + particleTextureAtlas.getNDCCoordY(textureIndex), + particleTextureAtlas.getNDCDimension() + )); } } diff --git a/src/main/java/electrosphere/renderer/RenderUtils.java b/src/main/java/electrosphere/renderer/RenderUtils.java index 213c9818..3823f00f 100644 --- a/src/main/java/electrosphere/renderer/RenderUtils.java +++ b/src/main/java/electrosphere/renderer/RenderUtils.java @@ -214,7 +214,7 @@ public class RenderUtils { - + particleMesh.setMaterial(new Material(AssetDataStrings.TEXTURE_PARTICLE)); diff --git a/src/main/java/electrosphere/renderer/texture/TextureAtlas.java b/src/main/java/electrosphere/renderer/texture/TextureAtlas.java new file mode 100644 index 00000000..bd620daa --- /dev/null +++ b/src/main/java/electrosphere/renderer/texture/TextureAtlas.java @@ -0,0 +1,105 @@ +package electrosphere.renderer.texture; + +import java.util.HashMap; +import java.util.Map; + +/** + * An atlas of texture path -> coordinates on provided texture + */ +public class TextureAtlas { + + + //A map of texture path -> coordinates in the atlas texture for its texture + Map pathCoordMap = new HashMap(); + + + //the actual texture + Texture specular; + + //the normal texture + Texture normal; + + //the width in pixels of a single texture in the atlas + public static final int ATLAS_ELEMENT_DIM = 64; + //the width in pixels of the whole atlas texture + public static final int ATLAS_DIM = 1024; + //number of textures per row in the atlas + public static final int ELEMENTS_PER_ROW = ATLAS_DIM / ATLAS_ELEMENT_DIM; + + /** + * Puts an entry in the path-coord map to map a texture path to a position + * @param path the texture's path + * @param coord the coordinate in the map + */ + public void putPathCoord(String path, int coord){ + pathCoordMap.put(path,coord); + } + + /** + * Sets the specular + * @param specular the specular + */ + public void setSpecular(Texture specular){ + this.specular = specular; + } + + /** + * Sets the normal + * @param normal the normal + */ + public void setNormal(Texture normal){ + this.normal = normal; + } + + /** + * Gets the atlas specular + * @return the atlas specular + */ + public Texture getSpecular(){ + return specular; + } + + /** + * Gets the atlas normal + * @return the atlas normal + */ + public Texture getNormal(){ + return normal; + } + + /** + * Gets the index in the atlas of a provided texture path + * @param texturePath The path to the texture you want the index of + * @return the index in the atlas of the texture at the provided texture path + */ + public int getTextureIndex(String texturePath){ + return pathCoordMap.containsKey(texturePath) ? pathCoordMap.get(texturePath) : -1; + } + + /** + * Gets the NDC dimensions of an image in the atlas + * @return The NDC dimension + */ + public float getNDCDimension(){ + return ATLAS_ELEMENT_DIM / (float)ATLAS_DIM; + } + + /** + * Gets the NDC x coordinate of an index in the atlas + * @param i The index + * @return The NDC x coordinate + */ + public float getNDCCoordX(int i){ + return (i % ELEMENTS_PER_ROW) * this.getNDCDimension(); + } + + /** + * Gets the NDC y coordinate of an index in the atlas + * @param i The index + * @return The NDC y coordinate + */ + public float getNDCCoordY(int i){ + return (i / ELEMENTS_PER_ROW) * this.getNDCDimension(); + } + +} diff --git a/src/main/java/electrosphere/util/FileUtils.java b/src/main/java/electrosphere/util/FileUtils.java index af21ea7b..dfbba734 100644 --- a/src/main/java/electrosphere/util/FileUtils.java +++ b/src/main/java/electrosphere/util/FileUtils.java @@ -177,6 +177,17 @@ public class FileUtils { File targetFile = new File("./assets" + sanitizedFilePath); return targetFile; } + + /** + * Gets a cache file + * @param pathName The relative path in the cache folder + * @return The file + */ + public static File getCacheFile(String pathName){ + String sanitizedFilePath = sanitizeFilePath(pathName); + File targetFile = new File("./.cache" + sanitizedFilePath); + return targetFile; + } /** * Gets a save file