texture atlasing for particle system
Some checks are pending
studiorailgun/Renderer/pipeline/head Build queued...
Some checks are pending
studiorailgun/Renderer/pipeline/head Build queued...
This commit is contained in:
parent
2ef4b13786
commit
4b41ac823c
@ -19,7 +19,7 @@
|
||||
"z": 0.0
|
||||
},
|
||||
"acceleration": -0.01,
|
||||
"texture": "",
|
||||
"texture": "blood1.png",
|
||||
"size": 0.3,
|
||||
"color": {
|
||||
"x": 0.5,
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -1018,6 +1018,8 @@ Upgrade terrain generation algorithms
|
||||
|
||||
Documentation
|
||||
|
||||
Test Creation
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@ -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<ShaderAttribute> 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;
|
||||
@ -126,4 +154,8 @@ public class ParticleService extends SignalServiceImpl {
|
||||
return colorAttrib;
|
||||
}
|
||||
|
||||
public ShaderAttribute getTextureAttrib(){
|
||||
return textureAttrib;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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
|
||||
*/
|
||||
|
||||
@ -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()
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -214,7 +214,7 @@ public class RenderUtils {
|
||||
|
||||
|
||||
|
||||
|
||||
particleMesh.setMaterial(new Material(AssetDataStrings.TEXTURE_PARTICLE));
|
||||
|
||||
|
||||
|
||||
|
||||
105
src/main/java/electrosphere/renderer/texture/TextureAtlas.java
Normal file
105
src/main/java/electrosphere/renderer/texture/TextureAtlas.java
Normal file
@ -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<String,Integer> pathCoordMap = new HashMap<String,Integer>();
|
||||
|
||||
|
||||
//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();
|
||||
}
|
||||
|
||||
}
|
||||
@ -178,6 +178,17 @@ public class FileUtils {
|
||||
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
|
||||
* @param saveName The name of the save
|
||||
|
||||
Loading…
Reference in New Issue
Block a user