particle emitter work + torches
All checks were successful
studiorailgun/Renderer/pipeline/head This commit looks good

This commit is contained in:
austin 2024-09-20 18:38:16 -04:00
parent 112cf26bad
commit e0a73fc057
15 changed files with 465 additions and 67 deletions

View File

@ -6,6 +6,7 @@
"Data/entity/objects/testscene1objects.json", "Data/entity/objects/testscene1objects.json",
"Data/entity/objects/debug_objects.json", "Data/entity/objects/debug_objects.json",
"Data/entity/objects/game_objects.json", "Data/entity/objects/game_objects.json",
"Data/entity/objects/containers.json" "Data/entity/objects/containers.json",
"Data/entity/objects/furniture.json"
] ]
} }

View File

@ -0,0 +1,58 @@
{
"objects" : [
{
"id" : "Torch",
"particleEmitter": {
"maxLife": 20,
"lifeCurrent": 0,
"particleVelocity": {
"x": 0.0,
"y": 0.02,
"z": 0.0
},
"acceleration": -0.0001,
"texture": "flame_05.png",
"size": 0.3,
"color": {
"x": 0.83,
"y": 0.6,
"z": 0.09
},
"offset": {
"x": 0,
"y": 1.2,
"z": 0
},
"radialAcceleration": 0.0001,
"transparency": {
"constant": 1,
"linear": 0,
"quadratic": -1
},
"spread": 15,
"frequency": 2
},
"pointLight" : {
"const": 1.0,
"linear": 0.9,
"quadratic": 0.9,
"radius": 3.0,
"color": {
"x": 0.83,
"y": 0.6,
"z": 0.09
},
"offset": {
"x": 0.0,
"y": 1.1,
"z": 0.0
}
},
"modelPath" : "Models/objects/furniture/torch1.glb",
"tokens": [
]
}
],
"files" : []
}

View File

@ -9,23 +9,35 @@
] ]
}, },
{ {
"id" : "particleEmitterTest", "id" : "flameEmitterTest",
"particleEmitter": { "particleEmitter": {
"maxLife": 20, "maxLife": 20,
"lifeCurrent": 0, "lifeCurrent": 0,
"particleVelocity": { "particleVelocity": {
"x": 0.0, "x": 0.0,
"y": 0.1, "y": 0.02,
"z": 0.0 "z": 0.0
}, },
"acceleration": -0.01, "acceleration": -0.0001,
"texture": "flame_05.png", "texture": "flame_05.png",
"size": 0.3, "size": 0.3,
"color": { "color": {
"x": 0.5, "x": 0.83,
"y": 0.5, "y": 0.6,
"z": 0.5 "z": 0.09
}, },
"offset": {
"x": 0,
"y": 0,
"z": 0
},
"radialAcceleration": 0.0001,
"transparency": {
"constant": 1,
"linear": 0,
"quadratic": -1
},
"spread": 15,
"frequency": 2 "frequency": 2
}, },
"tokens": [ "tokens": [

View File

@ -0,0 +1,11 @@
{
"textureMap": {
"Models/objects/furniture/torch1.glb": [
{
"meshName" : "Cube",
"diffuse" : "/Textures/wooden.png",
"isDefault" : true
}
]
}
}

Binary file not shown.

BIN
assets/Textures/wooden.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 380 KiB

View File

@ -1,15 +1,15 @@
package electrosphere.client.effects; package electrosphere.client.effects;
import electrosphere.client.entity.particle.ParticleUtils; // import electrosphere.client.entity.particle.ParticleUtils;
import electrosphere.engine.Globals; // import electrosphere.engine.Globals;
import electrosphere.entity.ClientEntityUtils; // import electrosphere.entity.ClientEntityUtils;
import electrosphere.entity.Entity; // import electrosphere.entity.Entity;
import electrosphere.entity.EntityUtils; // import electrosphere.entity.EntityUtils;
import electrosphere.game.data.particle.ParticleData; // import electrosphere.game.data.particle.ParticleData;
import java.util.Random; // import java.util.Random;
import org.joml.Quaterniond; // import org.joml.Quaterniond;
import org.joml.Vector3d; import org.joml.Vector3d;
/** /**
@ -22,26 +22,26 @@ public class ParticleEffects {
* @param position The position of the collision * @param position The position of the collision
*/ */
public static void spawnBloodsplats(Vector3d position){ public static void spawnBloodsplats(Vector3d position){
int max = 30; // int max = 30;
int min = 10; // int min = 10;
ParticleData bloodsplatData = null; // ParticleData bloodsplatData = null;
for(ParticleData data : Globals.gameConfigCurrent.getParticleDefinition().getData()){ // for(ParticleData data : Globals.gameConfigCurrent.getParticleDefinition().getData()){
if(data.getName().equals("blood")){ // if(data.getName().equals("blood")){
bloodsplatData = data; // bloodsplatData = data;
} // }
} // }
Random rand = new Random(); // Random rand = new Random();
int num = (int)(rand.nextFloat() * (max - min)) + min; // int num = (int)(rand.nextFloat() * (max - min)) + min;
for(int i = 0; i < num; i++){ // for(int i = 0; i < num; i++){
Vector3d destination = new Vector3d(rand.nextFloat() - 0.5f,rand.nextFloat() - 0.5f,rand.nextFloat() - 0.5f).normalize(); // Vector3d destination = new Vector3d(rand.nextFloat() - 0.5f,rand.nextFloat() - 0.5f,rand.nextFloat() - 0.5f).normalize();
Entity particleEntity = ParticleUtils.clientSpawnBillboardParticle(bloodsplatData, destination); // Entity particleEntity = ParticleUtils.clientSpawnBillboardParticle(bloodsplatData, destination);
ClientEntityUtils.initiallyPositionEntity( // ClientEntityUtils.initiallyPositionEntity(
particleEntity, // particleEntity,
position, // position,
new Quaterniond() // new Quaterniond()
); // );
EntityUtils.getScale(particleEntity).mul(bloodsplatData.getSize()); // EntityUtils.getScale(particleEntity).mul(bloodsplatData.getSize());
} // }
} }
} }

View File

@ -1,5 +1,7 @@
package electrosphere.entity.state.client.particle; package electrosphere.entity.state.client.particle;
import org.joml.Quaterniond;
import org.joml.Random;
import org.joml.Vector3d; import org.joml.Vector3d;
import electrosphere.engine.Globals; import electrosphere.engine.Globals;
@ -10,6 +12,7 @@ import electrosphere.entity.btree.BehaviorTree;
import electrosphere.entity.state.equip.ClientEquipState; import electrosphere.entity.state.equip.ClientEquipState;
import electrosphere.game.data.particle.ParticleData; import electrosphere.game.data.particle.ParticleData;
import electrosphere.game.data.particle.ParticleEmitter; import electrosphere.game.data.particle.ParticleEmitter;
import electrosphere.util.math.MathUtils;
/** /**
* A component that causes the entity to emit particles * A component that causes the entity to emit particles
@ -32,13 +35,22 @@ public class ClientParticleEmitterComponent implements BehaviorTree {
*/ */
long lastEmittedFrame = 0; long lastEmittedFrame = 0;
/**
* The random for particle generation
*/
static Random particleRand = new Random(0);
@Override @Override
public void simulate(float deltaTime) { public void simulate(float deltaTime) {
Vector3d entityPos = EntityUtils.getPosition(parent); Vector3d entityPos = EntityUtils.getPosition(parent);
if((float)(Globals.timekeeper.getNumberOfRenderFramesElapsed() - lastEmittedFrame) > particleEmitter.getFrequency()){ if((float)(Globals.timekeeper.getNumberOfRenderFramesElapsed() - lastEmittedFrame) > particleEmitter.getFrequency()){
lastEmittedFrame = Globals.timekeeper.getNumberOfRenderFramesElapsed(); lastEmittedFrame = Globals.timekeeper.getNumberOfRenderFramesElapsed();
//create particle here //create particle here
Globals.particleService.spawn(this.getData(), new Vector3d(entityPos)); Vector3d spawnPos = new Vector3d(entityPos);
if(this.particleEmitter.getOffset() != null){
spawnPos = spawnPos.add(new Vector3d(this.particleEmitter.getOffset()).rotate(new Quaterniond(EntityUtils.getRotation(parent))));
}
Globals.particleService.spawn(this.getData(), spawnPos);
} }
} }
@ -64,7 +76,23 @@ public class ClientParticleEmitterComponent implements BehaviorTree {
data.setMaxLife(this.particleEmitter.getMaxLife()); data.setMaxLife(this.particleEmitter.getMaxLife());
data.setSize(this.particleEmitter.getSize()); data.setSize(this.particleEmitter.getSize());
data.setTexture(this.particleEmitter.getTexture()); data.setTexture(this.particleEmitter.getTexture());
data.setVelocity(this.particleEmitter.getParticleVelocity()); Vector3d initialVelocity = new Vector3d(this.particleEmitter.getParticleVelocity());
if(this.particleEmitter.getSpread() != null){
Quaterniond rot = new Quaterniond();
Vector3d cross = null;
Vector3d normalizedVelocity = new Vector3d(initialVelocity).normalize();
if(Math.abs(normalizedVelocity.dot(MathUtils.getOriginVector())) < 0.8){
cross = MathUtils.getOriginVector().cross(normalizedVelocity);
} else {
cross = MathUtils.getUpVector().cross(normalizedVelocity);
}
rot.rotateAxis(Math.PI * 2 * particleRand.nextFloat(), normalizedVelocity);
rot.rotateAxis((particleRand.nextFloat() * this.particleEmitter.getSpread()) / 180.0 * Math.PI, cross);
initialVelocity = rot.transform(initialVelocity);
}
data.setVelocity(initialVelocity);
data.setEmitter(this.particleEmitter);
data.setParentEmitter(parent);
return data; return data;
} }

View File

@ -1,7 +1,5 @@
package electrosphere.entity.state.client.particle; package electrosphere.entity.state.client.particle;
import java.util.Random;
import org.joml.AxisAngle4f; import org.joml.AxisAngle4f;
import org.joml.Matrix4f; import org.joml.Matrix4f;
import org.joml.Quaternionf; import org.joml.Quaternionf;
@ -17,6 +15,7 @@ import electrosphere.entity.EntityDataStrings;
import electrosphere.entity.EntityUtils; import electrosphere.entity.EntityUtils;
import electrosphere.entity.btree.BehaviorTree; import electrosphere.entity.btree.BehaviorTree;
import electrosphere.game.data.particle.ParticleData; import electrosphere.game.data.particle.ParticleData;
import electrosphere.game.data.particle.ParticleEmitter;
import electrosphere.renderer.actor.instance.InstancedActor; import electrosphere.renderer.actor.instance.InstancedActor;
import electrosphere.renderer.texture.TextureAtlas; import electrosphere.renderer.texture.TextureAtlas;
@ -50,6 +49,11 @@ public class ClientParticleTree implements BehaviorTree {
* The color of the particle * The color of the particle
*/ */
Vector3d color; Vector3d color;
/**
* The data about the emitter
*/
ParticleEmitter emitterData;
/** /**
@ -70,7 +74,8 @@ public class ClientParticleTree implements BehaviorTree {
this.acceleration = particleData.getAcceleration(); this.acceleration = particleData.getAcceleration();
this.hasLife = particleData.getMaxLife() != null; this.hasLife = particleData.getMaxLife() != null;
this.lifeCurrent = maxLife; this.lifeCurrent = maxLife;
this.color = new Vector3d(new Random().nextFloat(),new Random().nextFloat(),0); this.emitterData = particleData.getEmitter();
this.color = new Vector3d(particleData.getColor());
} }
public int getMaxLife() { public int getMaxLife() {
@ -92,11 +97,12 @@ public class ClientParticleTree implements BehaviorTree {
@Override @Override
public void simulate(float deltaTime){ public void simulate(float deltaTime){
InstancedActor instancedActor = InstancedActor.getInstancedActor(parent); InstancedActor instancedActor = InstancedActor.getInstancedActor(parent);
Vector3d parentPosition = EntityUtils.getPosition(parent); Vector3d position = EntityUtils.getPosition(parent);
Vector3f cameraPos = CameraEntityUtils.getCameraCenter(Globals.playerCamera); Vector3f cameraPos = CameraEntityUtils.getCameraCenter(Globals.playerCamera);
//update position //update position
parentPosition.set(new Vector3d(parentPosition).add(velocity)); position = position.set(new Vector3d(position).add(velocity));
EntityUtils.getPosition(parent).set(position);
//update velocity //update velocity
Vector3d accelerationVec = new Vector3d(velocity).normalize().mul(acceleration); Vector3d accelerationVec = new Vector3d(velocity).normalize().mul(acceleration);
@ -105,6 +111,37 @@ public class ClientParticleTree implements BehaviorTree {
velocity = new Vector3d(0,0,0); velocity = new Vector3d(0,0,0);
acceleration = 0; acceleration = 0;
} }
//add radial acceleration
if(this.emitterData.getRadialAcceleration() != null){
Vector3d towardsParent = new Vector3d(EntityUtils.getPosition(this.particleData.getParentEmitter())).sub(position).normalize().mul(this.emitterData.getRadialAcceleration());
velocity = velocity.add(towardsParent);
}
//rotate the model to face the camera
Matrix4f rotationMatrix = new Matrix4f(Globals.viewMatrix).invert();
Quaternionf rotation = new Quaternionf(rotationMatrix.getRotation(new AxisAngle4f()));
EntityUtils.getRotation(parent).set(rotation);
//scale the particle
Vector3f scale = EntityUtils.getScale(parent);
scale.set(this.particleData.getSize());
//calculate alpha
float alpha = 1.0f;
if(this.emitterData.getTransparency() != null){
float t = 1.0f - (lifeCurrent / (float)maxLife);
alpha = (float)this.emitterData.getTransparency().calculate(t);
}
//store color
Vector4f currentColor = new Vector4f(
(float)this.color.y,
(float)this.color.z,
alpha,
(float)this.color.x
);
//calculate life left
if(hasLife){ if(hasLife){
lifeCurrent--; lifeCurrent--;
if(lifeCurrent <= 0){ if(lifeCurrent <= 0){
@ -113,24 +150,16 @@ public class ClientParticleTree implements BehaviorTree {
} }
} }
//rotate the model to face the camera
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 //push values to buffer that eventually gets uploaded to gpu
if(instancedActor != null){ if(instancedActor != null){
TextureAtlas particleTextureAtlas = Globals.particleService.getTextureAtlas(); TextureAtlas particleTextureAtlas = Globals.particleService.getTextureAtlas();
int textureIndex = particleTextureAtlas.getTextureIndex(this.particleData.getTexture()); int textureIndex = particleTextureAtlas.getTextureIndex(this.particleData.getTexture());
instancedActor.setAttribute(Globals.particleService.getModelAttrib(), new Matrix4f().translationRotateScale( instancedActor.setAttribute(Globals.particleService.getModelAttrib(), new Matrix4f().translationRotateScale(
new Vector3f((float)parentPosition.x,(float)parentPosition.y,(float)parentPosition.z).sub(cameraPos), new Vector3f((float)position.x,(float)position.y,(float)position.z).sub(cameraPos),
new Quaternionf(rotation), new Quaternionf(rotation),
scale scale
)); ));
instancedActor.setAttribute(Globals.particleService.getColorAttrib(), new Vector4f((float)this.color.y,(float)this.color.z,1.0f,(float)this.color.x)); instancedActor.setAttribute(Globals.particleService.getColorAttrib(), currentColor);
//when written to buffer, will be written in order w, x, y, z //when written to buffer, will be written in order w, x, y, z
//but gpu will fetch in order x, y, z, w //but gpu will fetch in order x, y, z, w

View File

@ -13,7 +13,6 @@ import electrosphere.game.data.creature.type.attack.AttackMoveResolver;
import electrosphere.game.data.creature.type.model.CreatureTypeMap; import electrosphere.game.data.creature.type.model.CreatureTypeMap;
import electrosphere.game.data.foliage.type.model.FoliageTypeMap; import electrosphere.game.data.foliage.type.model.FoliageTypeMap;
import electrosphere.game.data.item.type.model.ItemTypeMap; import electrosphere.game.data.item.type.model.ItemTypeMap;
import electrosphere.game.data.particle.ParticleDefinition;
import electrosphere.game.data.projectile.ProjectileTypeHolder; import electrosphere.game.data.projectile.ProjectileTypeHolder;
import electrosphere.game.data.tutorial.HintDefinition; import electrosphere.game.data.tutorial.HintDefinition;
import electrosphere.game.data.units.UnitDefinitionFile; import electrosphere.game.data.units.UnitDefinitionFile;
@ -47,11 +46,6 @@ public class Config {
*/ */
SurfaceAudioCollection surfaceAudioCollection; SurfaceAudioCollection surfaceAudioCollection;
/**
* The particle definition
*/
ParticleDefinition particleDefinition;
/** /**
* The unit loader * The unit loader
*/ */
@ -73,7 +67,6 @@ public class Config {
config.projectileTypeHolder = FileUtils.loadObjectFromAssetPath("Data/entity/projectile.json", ProjectileTypeHolder.class); config.projectileTypeHolder = FileUtils.loadObjectFromAssetPath("Data/entity/projectile.json", ProjectileTypeHolder.class);
config.hintData = FileUtils.loadObjectFromAssetPath("Data/tutorial/hints.json", HintDefinition.class); config.hintData = FileUtils.loadObjectFromAssetPath("Data/tutorial/hints.json", HintDefinition.class);
config.surfaceAudioCollection = FileUtils.loadObjectFromAssetPath("Data/audio/surface.json", SurfaceAudioCollection.class); config.surfaceAudioCollection = FileUtils.loadObjectFromAssetPath("Data/audio/surface.json", SurfaceAudioCollection.class);
config.particleDefinition = FileUtils.loadObjectFromAssetPath("Data/entity/particles.json", ParticleDefinition.class);
config.projectileTypeHolder.init(); config.projectileTypeHolder.init();
config.unitLoader = UnitLoader.create(FileUtils.loadObjectFromAssetPath("Data/game/units/units.json", UnitDefinitionFile.class)); config.unitLoader = UnitLoader.create(FileUtils.loadObjectFromAssetPath("Data/game/units/units.json", UnitDefinitionFile.class));
@ -245,14 +238,6 @@ public class Config {
return this.surfaceAudioCollection; return this.surfaceAudioCollection;
} }
/**
* Gets the particle definition
* @return The particle definition
*/
public ParticleDefinition getParticleDefinition(){
return particleDefinition;
}
/** /**
* Gets the unit loader * Gets the unit loader
* @return The unit loader * @return The unit loader

View File

@ -0,0 +1,88 @@
package electrosphere.game.data.color;
/**
* Defines a range of colors
*/
public class ColorRange {
/**
* The minimum chroma value
*/
float chromaMin;
/**
* The range of allowed chroma values
*/
float chromaRange;
/**
* The minimum hue value
*/
float hueMin;
/**
* The range of allowed hue values
*/
float hueRange;
/**
* The minimum lightness
*/
float lightMin;
/**
* The range of allowed lightness values
*/
float lightRange;
public float getChromaMin() {
return chromaMin;
}
public void setChromaMin(float chromaMin) {
this.chromaMin = chromaMin;
}
public float getChromaRange() {
return chromaRange;
}
public void setChromaRange(float chromaRange) {
this.chromaRange = chromaRange;
}
public float getHueMin() {
return hueMin;
}
public void setHueMin(float hueMin) {
this.hueMin = hueMin;
}
public float getHueRange() {
return hueRange;
}
public void setHueRange(float hueRange) {
this.hueRange = hueRange;
}
public float getLightMin() {
return lightMin;
}
public void setLightMin(float lightMin) {
this.lightMin = lightMin;
}
public float getLightRange() {
return lightRange;
}
public void setLightRange(float lightRange) {
this.lightRange = lightRange;
}
}

View File

@ -0,0 +1,58 @@
package electrosphere.game.data.math;
/**
* A defintion of a function that generates a scalar
*/
public class ScalarGenerator {
/**
* The constant value
*/
float constant;
/**
* The linear value
*/
float linear;
/**
* The quadratic value
*/
float quadratic;
public float getConstant() {
return constant;
}
public void setConstant(float constant) {
this.constant = constant;
}
public float getLinear() {
return linear;
}
public void setLinear(float linear) {
this.linear = linear;
}
public float getQuadratic() {
return quadratic;
}
public void setQuadratic(float quadratic) {
this.quadratic = quadratic;
}
/**
* Calculates the scalar's value given an input t
* @param t The input
* @return The scalar's value at t
*/
public double calculate(double t){
return constant + t * linear + t * t * quadratic;
}
}

View File

@ -2,6 +2,8 @@ package electrosphere.game.data.particle;
import org.joml.Vector3d; import org.joml.Vector3d;
import electrosphere.entity.Entity;
/** /**
* Data on how a particle should behave * Data on how a particle should behave
*/ */
@ -49,6 +51,16 @@ public class ParticleData {
*/ */
Vector3d color; Vector3d color;
/**
* The particle emitter DO NOT SET THIS IN JSON
*/
ParticleEmitter emitter;
/**
* The parent emitter
*/
Entity parentEmitter;
/** /**
* Gets the max life of the particle * Gets the max life of the particle
* @return The max life * @return The max life
@ -137,6 +149,25 @@ public class ParticleData {
this.color = color; this.color = color;
} }
public Vector3d getColor() {
return color;
}
public ParticleEmitter getEmitter() {
return emitter;
}
public void setEmitter(ParticleEmitter emitter) {
this.emitter = emitter;
}
public Entity getParentEmitter() {
return parentEmitter;
}
public void setParentEmitter(Entity parentEmitter) {
this.parentEmitter = parentEmitter;
}
} }

View File

@ -2,6 +2,9 @@ package electrosphere.game.data.particle;
import org.joml.Vector3d; import org.joml.Vector3d;
import electrosphere.game.data.color.ColorRange;
import electrosphere.game.data.math.ScalarGenerator;
/** /**
* Describes a particle emitter * Describes a particle emitter
*/ */
@ -53,6 +56,41 @@ public class ParticleEmitter {
*/ */
Float frequency; Float frequency;
/**
* The gravity applied to the particle
*/
Vector3d gravity;
/**
* The range of allowed colors
*/
ColorRange colorRange;
/**
* The value of transparency given time
*/
ScalarGenerator transparency;
/**
* The value of scale given time
*/
ScalarGenerator scale;
/**
* The amount the emitter can vary the initial velocity
*/
Float spread;
/**
* The acceleration to/from the center of the emitter
*/
Float radialAcceleration;
/**
* The offset from the base of the entity to emit from
*/
Vector3d offset;
/** /**
* Gets the max life of the particle * Gets the max life of the particle
* @return The max life * @return The max life
@ -161,6 +199,64 @@ public class ParticleEmitter {
this.frequency = frequency; this.frequency = frequency;
} }
public Vector3d getGravity() {
return gravity;
}
public void setGravity(Vector3d gravity) {
this.gravity = gravity;
}
public ColorRange getColorRange() {
return colorRange;
}
public void setColorRange(ColorRange colorRange) {
this.colorRange = colorRange;
}
public ScalarGenerator getTransparency() {
return transparency;
}
public void setTransparency(ScalarGenerator transparency) {
this.transparency = transparency;
}
public ScalarGenerator getScale() {
return scale;
}
public void setScale(ScalarGenerator scale) {
this.scale = scale;
}
public Float getSpread() {
return spread;
}
public void setSpread(Float spread) {
this.spread = spread;
}
public Float getRadialAcceleration() {
return radialAcceleration;
}
public void setRadialAcceleration(Float radialAcceleration) {
this.radialAcceleration = radialAcceleration;
}
public Vector3d getOffset() {
return offset;
}
public void setOffset(Vector3d offset) {
this.offset = offset;
}
} }

View File

@ -86,6 +86,7 @@ public class PointLight {
* @param description The description * @param description The description
*/ */
protected PointLight(PointLightDescription description){ protected PointLight(PointLightDescription description){
this.position = new Vector3f();
this.radius = description.getRadius(); this.radius = description.getRadius();
this.constant = description.getConstant(); this.constant = description.getConstant();
this.linear = description.getLinear(); this.linear = description.getLinear();