139 lines
4.5 KiB
Java
139 lines
4.5 KiB
Java
package io.github.hydos.ginger.engine.particle;
|
|
|
|
import java.lang.Math;
|
|
import java.util.Random;
|
|
|
|
import org.joml.*;
|
|
|
|
import io.github.hydos.ginger.engine.io.Window;
|
|
import io.github.hydos.ginger.engine.math.Maths;
|
|
|
|
public class ParticleSystem
|
|
{
|
|
private float pps, averageSpeed, gravityComplient, averageLifeLength, averageScale;
|
|
private float speedError, lifeError, scaleError = 0;
|
|
private boolean randomRotation = false;
|
|
private Vector3f direction;
|
|
private float directionDeviation = 0;
|
|
private ParticleTexture texture;
|
|
private Random random = new Random();
|
|
|
|
public ParticleSystem(ParticleTexture texture, float pps, float speed, float gravityComplient, float lifeLength, float scale)
|
|
{
|
|
this.pps = pps / 100000;
|
|
this.averageSpeed = speed;
|
|
this.gravityComplient = gravityComplient;
|
|
this.averageLifeLength = lifeLength;
|
|
this.averageScale = scale;
|
|
this.texture = texture;
|
|
}
|
|
|
|
/** @param direction - The average direction in which particles are emitted.
|
|
* @param deviation - A value between 0 and 1 indicating how far from the chosen direction particles can deviate. */
|
|
public void setDirection(Vector3f direction, float deviation)
|
|
{
|
|
this.direction = new Vector3f(direction);
|
|
this.directionDeviation = (float) (deviation * Math.PI);
|
|
}
|
|
|
|
public void randomizeRotation()
|
|
{ randomRotation = true; }
|
|
|
|
/** @param error
|
|
* - A number between 0 and 1, where 0 means no error margin. */
|
|
public void setSpeedError(float error)
|
|
{ this.speedError = error * averageSpeed; }
|
|
|
|
/** @param error
|
|
* - A number between 0 and 1, where 0 means no error margin. */
|
|
public void setLifeError(float error)
|
|
{ this.lifeError = error * averageLifeLength; }
|
|
|
|
/** @param error
|
|
* - A number between 0 and 1, where 0 means no error margin. */
|
|
public void setScaleError(float error)
|
|
{ this.scaleError = error * averageScale; }
|
|
|
|
public void generateParticles(Vector3f systemCenter)
|
|
{
|
|
float delta = (float) Window.getTime();
|
|
float particlesToCreate = pps * delta;
|
|
int count = (int) Math.floor(particlesToCreate);
|
|
float partialParticle = particlesToCreate % 1;
|
|
for (int i = 0; i < count; i++)
|
|
{ emitParticle(systemCenter); }
|
|
if (Math.random() < partialParticle)
|
|
{ emitParticle(systemCenter); }
|
|
}
|
|
|
|
private void emitParticle(Vector3f center)
|
|
{
|
|
Vector3f velocity = null;
|
|
if (direction != null)
|
|
{
|
|
velocity = generateRandomUnitVectorWithinCone(direction, directionDeviation);
|
|
}
|
|
else
|
|
{
|
|
velocity = generateRandomUnitVector();
|
|
}
|
|
velocity.normalize();
|
|
Maths.scale(velocity, generateValue(averageSpeed, speedError));
|
|
float scale = generateValue(averageScale, scaleError);
|
|
float lifeLength = generateValue(averageLifeLength, lifeError);
|
|
new Particle(texture, new Vector3f(center), velocity, gravityComplient, lifeLength, generateRotation(), new Vector3f(scale, scale, scale));
|
|
}
|
|
|
|
private float generateValue(float average, float errorMargin)
|
|
{
|
|
float offset = (random.nextFloat() - 0.5f) * 2f * errorMargin;
|
|
return average + offset;
|
|
}
|
|
|
|
private float generateRotation()
|
|
{
|
|
if (randomRotation)
|
|
{
|
|
return random.nextFloat() * 360f;
|
|
}
|
|
else
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
private static Vector3f generateRandomUnitVectorWithinCone(Vector3f coneDirection, float angle)
|
|
{
|
|
float cosAngle = (float) Math.cos(angle);
|
|
Random random = new Random();
|
|
float theta = (float) (random.nextFloat() * 2f * Math.PI);
|
|
float z = cosAngle + (random.nextFloat() * (1 - cosAngle));
|
|
float rootOneMinusZSquared = (float) Math.sqrt(1 - z * z);
|
|
float x = (float) (rootOneMinusZSquared * Math.cos(theta));
|
|
float y = (float) (rootOneMinusZSquared * Math.sin(theta));
|
|
Vector4f direction = new Vector4f(x, y, z, 1);
|
|
if (coneDirection.x != 0 || coneDirection.y != 0 || (coneDirection.z != 1 && coneDirection.z != -1))
|
|
{
|
|
Vector3f rotateAxis = coneDirection.cross(coneDirection, new Vector3f(0, 0, 1));
|
|
rotateAxis.normalize();
|
|
float rotateAngle = (float) Math.acos(coneDirection.dot(new Vector3f(0, 0, 1)));
|
|
Matrix4f rotationMatrix = new Matrix4f();
|
|
rotationMatrix.rotate(-rotateAngle, rotateAxis);
|
|
rotationMatrix.transform(direction, direction);
|
|
}
|
|
else if (coneDirection.z == -1)
|
|
{ direction.z *= -1; }
|
|
return Maths.Vec4ToVec3(direction);
|
|
}
|
|
|
|
private Vector3f generateRandomUnitVector()
|
|
{
|
|
float theta = (float) (random.nextFloat() * 2f * Math.PI);
|
|
float z = (random.nextFloat() * 2) - 1;
|
|
float rootOneMinusZSquared = (float) Math.sqrt(1 - z * z);
|
|
float x = (float) (rootOneMinusZSquared * Math.cos(theta));
|
|
float y = (float) (rootOneMinusZSquared * Math.sin(theta));
|
|
return new Vector3f(x, y, z);
|
|
}
|
|
}
|