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/debug_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": {
"maxLife": 20,
"lifeCurrent": 0,
"particleVelocity": {
"x": 0.0,
"y": 0.1,
"y": 0.02,
"z": 0.0
},
"acceleration": -0.01,
"acceleration": -0.0001,
"texture": "flame_05.png",
"size": 0.3,
"color": {
"x": 0.5,
"y": 0.5,
"z": 0.5
"x": 0.83,
"y": 0.6,
"z": 0.09
},
"offset": {
"x": 0,
"y": 0,
"z": 0
},
"radialAcceleration": 0.0001,
"transparency": {
"constant": 1,
"linear": 0,
"quadratic": -1
},
"spread": 15,
"frequency": 2
},
"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;
import electrosphere.client.entity.particle.ParticleUtils;
import electrosphere.engine.Globals;
import electrosphere.entity.ClientEntityUtils;
import electrosphere.entity.Entity;
import electrosphere.entity.EntityUtils;
import electrosphere.game.data.particle.ParticleData;
// import electrosphere.client.entity.particle.ParticleUtils;
// import electrosphere.engine.Globals;
// import electrosphere.entity.ClientEntityUtils;
// import electrosphere.entity.Entity;
// import electrosphere.entity.EntityUtils;
// 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;
/**
@ -22,26 +22,26 @@ public class ParticleEffects {
* @param position The position of the collision
*/
public static void spawnBloodsplats(Vector3d position){
int max = 30;
int min = 10;
ParticleData bloodsplatData = null;
for(ParticleData data : Globals.gameConfigCurrent.getParticleDefinition().getData()){
if(data.getName().equals("blood")){
bloodsplatData = data;
}
}
Random rand = new Random();
int num = (int)(rand.nextFloat() * (max - min)) + min;
for(int i = 0; i < num; i++){
Vector3d destination = new Vector3d(rand.nextFloat() - 0.5f,rand.nextFloat() - 0.5f,rand.nextFloat() - 0.5f).normalize();
Entity particleEntity = ParticleUtils.clientSpawnBillboardParticle(bloodsplatData, destination);
ClientEntityUtils.initiallyPositionEntity(
particleEntity,
position,
new Quaterniond()
);
EntityUtils.getScale(particleEntity).mul(bloodsplatData.getSize());
}
// int max = 30;
// int min = 10;
// ParticleData bloodsplatData = null;
// for(ParticleData data : Globals.gameConfigCurrent.getParticleDefinition().getData()){
// if(data.getName().equals("blood")){
// bloodsplatData = data;
// }
// }
// Random rand = new Random();
// int num = (int)(rand.nextFloat() * (max - min)) + min;
// for(int i = 0; i < num; i++){
// Vector3d destination = new Vector3d(rand.nextFloat() - 0.5f,rand.nextFloat() - 0.5f,rand.nextFloat() - 0.5f).normalize();
// Entity particleEntity = ParticleUtils.clientSpawnBillboardParticle(bloodsplatData, destination);
// ClientEntityUtils.initiallyPositionEntity(
// particleEntity,
// position,
// new Quaterniond()
// );
// EntityUtils.getScale(particleEntity).mul(bloodsplatData.getSize());
// }
}
}

View File

@ -1,5 +1,7 @@
package electrosphere.entity.state.client.particle;
import org.joml.Quaterniond;
import org.joml.Random;
import org.joml.Vector3d;
import electrosphere.engine.Globals;
@ -10,6 +12,7 @@ import electrosphere.entity.btree.BehaviorTree;
import electrosphere.entity.state.equip.ClientEquipState;
import electrosphere.game.data.particle.ParticleData;
import electrosphere.game.data.particle.ParticleEmitter;
import electrosphere.util.math.MathUtils;
/**
* A component that causes the entity to emit particles
@ -32,13 +35,22 @@ public class ClientParticleEmitterComponent implements BehaviorTree {
*/
long lastEmittedFrame = 0;
/**
* The random for particle generation
*/
static Random particleRand = new Random(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));
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.setSize(this.particleEmitter.getSize());
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;
}

View File

@ -1,7 +1,5 @@
package electrosphere.entity.state.client.particle;
import java.util.Random;
import org.joml.AxisAngle4f;
import org.joml.Matrix4f;
import org.joml.Quaternionf;
@ -17,6 +15,7 @@ import electrosphere.entity.EntityDataStrings;
import electrosphere.entity.EntityUtils;
import electrosphere.entity.btree.BehaviorTree;
import electrosphere.game.data.particle.ParticleData;
import electrosphere.game.data.particle.ParticleEmitter;
import electrosphere.renderer.actor.instance.InstancedActor;
import electrosphere.renderer.texture.TextureAtlas;
@ -50,6 +49,11 @@ public class ClientParticleTree implements BehaviorTree {
* The color of the particle
*/
Vector3d color;
/**
* The data about the emitter
*/
ParticleEmitter emitterData;
/**
@ -70,7 +74,8 @@ public class ClientParticleTree implements BehaviorTree {
this.acceleration = particleData.getAcceleration();
this.hasLife = particleData.getMaxLife() != null;
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() {
@ -92,11 +97,12 @@ public class ClientParticleTree implements BehaviorTree {
@Override
public void simulate(float deltaTime){
InstancedActor instancedActor = InstancedActor.getInstancedActor(parent);
Vector3d parentPosition = EntityUtils.getPosition(parent);
Vector3d position = EntityUtils.getPosition(parent);
Vector3f cameraPos = CameraEntityUtils.getCameraCenter(Globals.playerCamera);
//update position
parentPosition.set(new Vector3d(parentPosition).add(velocity));
position = position.set(new Vector3d(position).add(velocity));
EntityUtils.getPosition(parent).set(position);
//update velocity
Vector3d accelerationVec = new Vector3d(velocity).normalize().mul(acceleration);
@ -105,6 +111,37 @@ public class ClientParticleTree implements BehaviorTree {
velocity = new Vector3d(0,0,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){
lifeCurrent--;
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
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 Vector3f((float)position.x,(float)position.y,(float)position.z).sub(cameraPos),
new Quaternionf(rotation),
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
//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.foliage.type.model.FoliageTypeMap;
import electrosphere.game.data.item.type.model.ItemTypeMap;
import electrosphere.game.data.particle.ParticleDefinition;
import electrosphere.game.data.projectile.ProjectileTypeHolder;
import electrosphere.game.data.tutorial.HintDefinition;
import electrosphere.game.data.units.UnitDefinitionFile;
@ -47,11 +46,6 @@ public class Config {
*/
SurfaceAudioCollection surfaceAudioCollection;
/**
* The particle definition
*/
ParticleDefinition particleDefinition;
/**
* The unit loader
*/
@ -73,7 +67,6 @@ public class Config {
config.projectileTypeHolder = FileUtils.loadObjectFromAssetPath("Data/entity/projectile.json", ProjectileTypeHolder.class);
config.hintData = FileUtils.loadObjectFromAssetPath("Data/tutorial/hints.json", HintDefinition.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.unitLoader = UnitLoader.create(FileUtils.loadObjectFromAssetPath("Data/game/units/units.json", UnitDefinitionFile.class));
@ -245,14 +238,6 @@ public class Config {
return this.surfaceAudioCollection;
}
/**
* Gets the particle definition
* @return The particle definition
*/
public ParticleDefinition getParticleDefinition(){
return particleDefinition;
}
/**
* Gets 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 electrosphere.entity.Entity;
/**
* Data on how a particle should behave
*/
@ -49,6 +51,16 @@ public class ParticleData {
*/
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
* @return The max life
@ -137,6 +149,25 @@ public class ParticleData {
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 electrosphere.game.data.color.ColorRange;
import electrosphere.game.data.math.ScalarGenerator;
/**
* Describes a particle emitter
*/
@ -53,6 +56,41 @@ public class ParticleEmitter {
*/
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
* @return The max life
@ -161,6 +199,64 @@ public class ParticleEmitter {
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
*/
protected PointLight(PointLightDescription description){
this.position = new Vector3f();
this.radius = description.getRadius();
this.constant = description.getConstant();
this.linear = description.getLinear();