mirror of
https://github.com/RandomityGuy/MBHaxe.git
synced 2026-04-27 21:21:41 +00:00
Add particlesystem stuff
This commit is contained in:
parent
cc706f8cbb
commit
f88433ea26
11 changed files with 553 additions and 2 deletions
|
|
@ -3,5 +3,6 @@
|
||||||
-lib hlsdl
|
-lib hlsdl
|
||||||
-lib polygonal-ds
|
-lib polygonal-ds
|
||||||
-hl marblegame.hl
|
-hl marblegame.hl
|
||||||
|
-D windowSize=1280x720
|
||||||
--main Main
|
--main Main
|
||||||
-debug
|
-debug
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
package src;
|
package src;
|
||||||
|
|
||||||
|
import shaders.Billboard;
|
||||||
import collision.BoxCollisionEntity;
|
import collision.BoxCollisionEntity;
|
||||||
import shaders.DtsTexture;
|
import shaders.DtsTexture;
|
||||||
import h3d.shader.AlphaMult;
|
import h3d.shader.AlphaMult;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
package src;
|
package src;
|
||||||
|
|
||||||
|
import shaders.Billboard;
|
||||||
import shaders.DtsTexture;
|
import shaders.DtsTexture;
|
||||||
import h3d.mat.Pass;
|
import h3d.mat.Pass;
|
||||||
import h3d.shader.AlphaMult;
|
import h3d.shader.AlphaMult;
|
||||||
|
|
|
||||||
45
src/Main.hx
45
src/Main.hx
|
|
@ -1,5 +1,12 @@
|
||||||
package;
|
package;
|
||||||
|
|
||||||
|
import h3d.mat.Data.Blend;
|
||||||
|
import src.ParticleSystem.ParticleEmitterOptions;
|
||||||
|
import src.ParticleSystem.ParticleEmitter;
|
||||||
|
import src.ParticleSystem.Particle;
|
||||||
|
import src.ParticleSystem.ParticleManager;
|
||||||
|
import src.ParticleSystem.ParticleData;
|
||||||
|
import src.ParticleSystem.ParticleData;
|
||||||
import shapes.Helicopter;
|
import shapes.Helicopter;
|
||||||
import shapes.ShockAbsorber;
|
import shapes.ShockAbsorber;
|
||||||
import shapes.SuperBounce;
|
import shapes.SuperBounce;
|
||||||
|
|
@ -116,7 +123,45 @@ class Main extends hxd.App {
|
||||||
ag.y = 6;
|
ag.y = 6;
|
||||||
world.addDtsObject(ag);
|
world.addDtsObject(ag);
|
||||||
|
|
||||||
|
// var le:ParticleEmitterOptions = {
|
||||||
|
// ejectionPeriod: 0.01,
|
||||||
|
// ambientVelocity: new Vector(0, 0, 0),
|
||||||
|
// ejectionVelocity: 0.5,
|
||||||
|
// velocityVariance: 0.25,
|
||||||
|
// emitterLifetime: 1e8,
|
||||||
|
// inheritedVelFactor: 0.2,
|
||||||
|
// particleOptions: {
|
||||||
|
// texture: 'particles/smoke.png',
|
||||||
|
// blending: Add,
|
||||||
|
// spinSpeed: 40,
|
||||||
|
// spinRandomMin: -90,
|
||||||
|
// spinRandomMax: 90,
|
||||||
|
// lifetime: 1,
|
||||||
|
// lifetimeVariance: 0.15,
|
||||||
|
// dragCoefficient: 0.8,
|
||||||
|
// acceleration: 0,
|
||||||
|
// colors: [new Vector(0.56, 0.36, 0.26, 1), new Vector(0.56, 0.36, 0.26, 0)],
|
||||||
|
// sizes: [0.5, 1],
|
||||||
|
// times: [0, 1]
|
||||||
|
// }
|
||||||
|
// };
|
||||||
|
|
||||||
|
// var p1 = new ParticleData();
|
||||||
|
// p1.identifier = "testparticle";
|
||||||
|
// p1.texture = ResourceLoader.getTexture("data/particles/smoke.png");
|
||||||
|
|
||||||
|
// // var emitter = new ParticleEmitter(le, p1, world.particleManager); // var p = new Particle();
|
||||||
|
// world.particleManager.createEmitter(le, p1, new Vector());
|
||||||
|
|
||||||
|
// p.position = new Vector();
|
||||||
|
// p.color = new Vector(255, 255, 255);
|
||||||
|
// p.rotation = Math.PI;
|
||||||
|
// p.scale = 5;
|
||||||
|
|
||||||
|
// world.particleManager.addParticle(p1, p);
|
||||||
|
|
||||||
// for (i in 0...10) {
|
// for (i in 0...10) {
|
||||||
|
|
||||||
// for (j in 0...10) {
|
// for (j in 0...10) {
|
||||||
// var trapdoor = new Tornado();
|
// var trapdoor = new Tornado();
|
||||||
// trapdoor.x = i * 2;
|
// trapdoor.x = i * 2;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
package src;
|
package src;
|
||||||
|
|
||||||
|
import src.ParticleSystem.ParticleData;
|
||||||
|
import src.ParticleSystem.ParticleEmitterOptions;
|
||||||
import src.DtsObject;
|
import src.DtsObject;
|
||||||
import sdl.Cursor;
|
import sdl.Cursor;
|
||||||
import hxd.Cursor;
|
import hxd.Cursor;
|
||||||
|
|
@ -36,6 +38,29 @@ class Move {
|
||||||
public function new() {}
|
public function new() {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final bounceParticleOptions:ParticleEmitterOptions = {
|
||||||
|
ejectionPeriod: 1,
|
||||||
|
ambientVelocity: new Vector(0, 0, 0.0),
|
||||||
|
ejectionVelocity: 2.6,
|
||||||
|
velocityVariance: 0.25 * 0.5,
|
||||||
|
emitterLifetime: 4,
|
||||||
|
inheritedVelFactor: 0,
|
||||||
|
particleOptions: {
|
||||||
|
texture: 'particles/star.png',
|
||||||
|
blending: Alpha,
|
||||||
|
spinSpeed: 90,
|
||||||
|
spinRandomMin: -90,
|
||||||
|
spinRandomMax: 90,
|
||||||
|
lifetime: 500,
|
||||||
|
lifetimeVariance: 100,
|
||||||
|
dragCoefficient: 0.5,
|
||||||
|
acceleration: -2,
|
||||||
|
colors: [new Vector(0.9, 0, 0, 1), new Vector(0.9, 0.9, 0, 1), new Vector(0.9, 0.9, 0, 0)],
|
||||||
|
sizes: [0.25, 0.25, 0.25],
|
||||||
|
times: [0, 0.75, 1]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
class Marble extends GameObject {
|
class Marble extends GameObject {
|
||||||
public var camera:CameraController;
|
public var camera:CameraController;
|
||||||
public var cameraObject:Object;
|
public var cameraObject:Object;
|
||||||
|
|
@ -86,6 +111,8 @@ class Marble extends GameObject {
|
||||||
var shockAbsorberEnableTime:Float = -1e8;
|
var shockAbsorberEnableTime:Float = -1e8;
|
||||||
var helicopterEnableTime:Float = -1e8;
|
var helicopterEnableTime:Float = -1e8;
|
||||||
|
|
||||||
|
var bounceEmitterData:ParticleData;
|
||||||
|
|
||||||
public function new() {
|
public function new() {
|
||||||
super();
|
super();
|
||||||
var geom = Sphere.defaultUnitSphere();
|
var geom = Sphere.defaultUnitSphere();
|
||||||
|
|
@ -100,6 +127,10 @@ class Marble extends GameObject {
|
||||||
this.camera = new CameraController(cast this);
|
this.camera = new CameraController(cast this);
|
||||||
|
|
||||||
this.collider = new SphereCollisionEntity(cast this);
|
this.collider = new SphereCollisionEntity(cast this);
|
||||||
|
|
||||||
|
this.bounceEmitterData = new ParticleData();
|
||||||
|
this.bounceEmitterData.identifier = "MarbleBounceParticle";
|
||||||
|
this.bounceEmitterData.texture = ResourceLoader.getTexture("data/particles/star.png");
|
||||||
}
|
}
|
||||||
|
|
||||||
public function init(level:MarbleWorld) {
|
public function init(level:MarbleWorld) {
|
||||||
|
|
@ -307,6 +338,7 @@ class Marble extends GameObject {
|
||||||
var velocityAdd = surfaceVel.multiply(-(1 + restitution));
|
var velocityAdd = surfaceVel.multiply(-(1 + restitution));
|
||||||
var vAtC = sVel.add(this.omega.cross(contacts[i].normal.multiply(-this._radius)));
|
var vAtC = sVel.add(this.omega.cross(contacts[i].normal.multiply(-this._radius)));
|
||||||
var normalVel = -contacts[i].normal.dot(sVel);
|
var normalVel = -contacts[i].normal.dot(sVel);
|
||||||
|
bounceEmitter(sVel.length() * restitution, contacts[i].normal);
|
||||||
vAtC = vAtC.sub(contacts[i].normal.multiply(contacts[i].normal.dot(sVel)));
|
vAtC = vAtC.sub(contacts[i].normal.multiply(contacts[i].normal.dot(sVel)));
|
||||||
var vAtCMag = vAtC.length();
|
var vAtCMag = vAtC.length();
|
||||||
if (vAtCMag != 0) {
|
if (vAtCMag != 0) {
|
||||||
|
|
@ -473,6 +505,10 @@ class Marble extends GameObject {
|
||||||
return [A, a];
|
return [A, a];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function bounceEmitter(speed:Float, normal:Vector) {
|
||||||
|
this.level.particleManager.createEmitter(bounceParticleOptions, this.bounceEmitterData, this.getAbsPos().getPosition());
|
||||||
|
}
|
||||||
|
|
||||||
function ReportBounce(pos:Vector, normal:Vector, speed:Float) {
|
function ReportBounce(pos:Vector, normal:Vector, speed:Float) {
|
||||||
if (this._bounceYet && speed < this._bounceSpeed) {
|
if (this._bounceYet && speed < this._bounceSpeed) {
|
||||||
return;
|
return;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
package src;
|
package src;
|
||||||
|
|
||||||
|
import src.ParticleSystem.ParticleManager;
|
||||||
import src.Util;
|
import src.Util;
|
||||||
import h3d.Quat;
|
import h3d.Quat;
|
||||||
import shapes.PowerUp;
|
import shapes.PowerUp;
|
||||||
|
|
@ -21,6 +22,7 @@ import src.Marble;
|
||||||
class MarbleWorld {
|
class MarbleWorld {
|
||||||
public var collisionWorld:CollisionWorld;
|
public var collisionWorld:CollisionWorld;
|
||||||
public var instanceManager:InstanceManager;
|
public var instanceManager:InstanceManager;
|
||||||
|
public var particleManager:ParticleManager;
|
||||||
|
|
||||||
public var interiors:Array<InteriorObject> = [];
|
public var interiors:Array<InteriorObject> = [];
|
||||||
public var pathedInteriors:Array<PathedInterior> = [];
|
public var pathedInteriors:Array<PathedInterior> = [];
|
||||||
|
|
@ -49,6 +51,7 @@ class MarbleWorld {
|
||||||
this.collisionWorld = new CollisionWorld();
|
this.collisionWorld = new CollisionWorld();
|
||||||
this.scene = scene;
|
this.scene = scene;
|
||||||
this.instanceManager = new InstanceManager(scene);
|
this.instanceManager = new InstanceManager(scene);
|
||||||
|
this.particleManager = new ParticleManager(cast this);
|
||||||
this.sky = new Sky();
|
this.sky = new Sky();
|
||||||
sky.dmlPath = "data/skies/sky_day.dml";
|
sky.dmlPath = "data/skies/sky_day.dml";
|
||||||
sky.init(cast this);
|
sky.init(cast this);
|
||||||
|
|
@ -112,6 +115,7 @@ class MarbleWorld {
|
||||||
marble.update(currentTime, dt, collisionWorld, this.pathedInteriors);
|
marble.update(currentTime, dt, collisionWorld, this.pathedInteriors);
|
||||||
}
|
}
|
||||||
this.instanceManager.update(dt);
|
this.instanceManager.update(dt);
|
||||||
|
this.particleManager.update(1000 * currentTime, dt);
|
||||||
currentTime += dt;
|
currentTime += dt;
|
||||||
if (this.marble != null) {
|
if (this.marble != null) {
|
||||||
callCollisionHandlers(marble);
|
callCollisionHandlers(marble);
|
||||||
|
|
|
||||||
363
src/ParticleSystem.hx
Normal file
363
src/ParticleSystem.hx
Normal file
|
|
@ -0,0 +1,363 @@
|
||||||
|
package src;
|
||||||
|
|
||||||
|
import h3d.prim.UV;
|
||||||
|
import h3d.parts.Data.BlendMode;
|
||||||
|
import src.MarbleWorld;
|
||||||
|
import src.Util;
|
||||||
|
import h3d.mat.Data.Wrap;
|
||||||
|
import shaders.Billboard;
|
||||||
|
import hxd.IndexBuffer;
|
||||||
|
import h3d.col.Point;
|
||||||
|
import h3d.prim.Polygon;
|
||||||
|
import h3d.prim.MeshPrimitive;
|
||||||
|
import h3d.mat.Texture;
|
||||||
|
import h3d.scene.Scene;
|
||||||
|
import h3d.mat.Material;
|
||||||
|
import h3d.Vector;
|
||||||
|
import h3d.scene.MeshBatch;
|
||||||
|
import h3d.scene.Mesh;
|
||||||
|
|
||||||
|
@:publicFields
|
||||||
|
class ParticleData {
|
||||||
|
var texture:Texture;
|
||||||
|
var identifier:String;
|
||||||
|
|
||||||
|
public function new() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
@:publicFields
|
||||||
|
class Particle {
|
||||||
|
var data:ParticleData;
|
||||||
|
var manager:ParticleManager;
|
||||||
|
var o:ParticleOptions;
|
||||||
|
var position:Vector;
|
||||||
|
var vel:Vector;
|
||||||
|
var rotation:Float;
|
||||||
|
var color:Vector;
|
||||||
|
var scale:Float;
|
||||||
|
var lifeTime:Float;
|
||||||
|
var initialSpin:Float;
|
||||||
|
var spawnTime:Float;
|
||||||
|
var currentAge:Float = 0;
|
||||||
|
var acc:Vector = new Vector();
|
||||||
|
|
||||||
|
public function new(options:ParticleOptions, manager:ParticleManager, data:ParticleData, spawnTime:Float, pos:Vector, vel:Vector) {
|
||||||
|
this.o = options;
|
||||||
|
this.manager = manager;
|
||||||
|
this.data = data;
|
||||||
|
this.spawnTime = spawnTime;
|
||||||
|
this.position = pos;
|
||||||
|
this.vel = vel;
|
||||||
|
this.acc = this.vel.multiply(options.acceleration);
|
||||||
|
|
||||||
|
this.lifeTime = this.o.lifetime + this.o.lifetimeVariance * (Math.random() * 2 - 1);
|
||||||
|
this.initialSpin = Util.lerp(this.o.spinRandomMin, this.o.spinRandomMax, Math.random());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function update(time:Float, dt:Float) {
|
||||||
|
var t = dt;
|
||||||
|
var a = this.acc;
|
||||||
|
a = a.sub(this.vel.multiply(this.o.dragCoefficient));
|
||||||
|
this.vel = this.vel.add(a.multiply(dt));
|
||||||
|
this.position = this.position.add(this.vel.multiply(dt));
|
||||||
|
|
||||||
|
this.currentAge += dt;
|
||||||
|
|
||||||
|
var elapsed = time - this.spawnTime;
|
||||||
|
var completion = Util.clamp(elapsed / this.lifeTime, 0, 1);
|
||||||
|
|
||||||
|
if (currentAge > this.lifeTime) // Again, rewind needs this
|
||||||
|
{
|
||||||
|
this.manager.removeParticle(this.data, this);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (completion == 1) {
|
||||||
|
// The particle can die
|
||||||
|
this.manager.removeParticle(this.data, this);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// var velElapsed = elapsed / 1000;
|
||||||
|
// velElapsed *= 0.001;
|
||||||
|
// velElapsed = Math.pow(velElapsed, (1 - this.o.dragCoefficient)); // Somehow slow down velocity over time based on the drag coefficient
|
||||||
|
|
||||||
|
// // Compute the position
|
||||||
|
// var pos = this.position.add(this.vel.multiply(velElapsed + this.o.acceleration * (velElapsed * velElapsed) / 2));
|
||||||
|
// this.position = pos;
|
||||||
|
|
||||||
|
this.rotation = (this.initialSpin + this.o.spinSpeed * elapsed / 1000) * Math.PI / 180;
|
||||||
|
|
||||||
|
// Check where we are in the times array
|
||||||
|
var indexLow = 0;
|
||||||
|
var indexHigh = 1;
|
||||||
|
for (i in 2...this.o.times.length) {
|
||||||
|
if (this.o.times[indexHigh] >= completion)
|
||||||
|
break;
|
||||||
|
|
||||||
|
indexLow = indexHigh;
|
||||||
|
indexHigh = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.o.times.length == 1)
|
||||||
|
indexHigh = indexLow;
|
||||||
|
var t = (completion - this.o.times[indexLow]) / (this.o.times[indexHigh] - this.o.times[indexLow]);
|
||||||
|
|
||||||
|
// Adjust color
|
||||||
|
var color = Util.lerpThreeVectors(this.o.colors[indexLow], this.o.colors[indexHigh], t);
|
||||||
|
this.color = color;
|
||||||
|
// this.material.opacity = color.a * * 1.5; // Adjusted because additive mixing can be kind of extreme
|
||||||
|
|
||||||
|
// Adjust sizing
|
||||||
|
this.scale = Util.lerp(this.o.sizes[indexLow], this.o.sizes[indexHigh], t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef ParticleBatch = {
|
||||||
|
var instances:Array<Particle>;
|
||||||
|
var meshBatch:MeshBatch;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** The options for a single particle. */
|
||||||
|
typedef ParticleOptions = {
|
||||||
|
var texture:String;
|
||||||
|
|
||||||
|
/** Which blending mode to use. */
|
||||||
|
var blending:h3d.mat.BlendMode;
|
||||||
|
|
||||||
|
/** The spinning speed in degrees per second. */
|
||||||
|
var spinSpeed:Float;
|
||||||
|
|
||||||
|
var spinRandomMin:Float;
|
||||||
|
var spinRandomMax:Float;
|
||||||
|
var lifetime:Float;
|
||||||
|
var lifetimeVariance:Float;
|
||||||
|
var dragCoefficient:Float;
|
||||||
|
|
||||||
|
/** Acceleration along the velocity vector. */
|
||||||
|
var acceleration:Float;
|
||||||
|
|
||||||
|
var colors:Array<Vector>;
|
||||||
|
var sizes:Array<Float>;
|
||||||
|
|
||||||
|
/** Determines at what percentage of lifetime the corresponding colors and sizes are in effect. */
|
||||||
|
var times:Array<Float>;
|
||||||
|
};
|
||||||
|
|
||||||
|
/** The options for a particle emitter. */
|
||||||
|
typedef ParticleEmitterOptions = {
|
||||||
|
/** The time between particle ejections. */
|
||||||
|
var ejectionPeriod:Float; /** A fixed velocity to add to each particle. */
|
||||||
|
|
||||||
|
var ambientVelocity:Vector; /** The particle is ejected in a random direction with this velocity. */
|
||||||
|
|
||||||
|
var ejectionVelocity:Float;
|
||||||
|
|
||||||
|
var velocityVariance:Float;
|
||||||
|
var emitterLifetime:Float;
|
||||||
|
|
||||||
|
/** How much of the emitter's own velocity the particle should inherit. */
|
||||||
|
var inheritedVelFactor:Float; /** Computes a spawn offset for each particle. */
|
||||||
|
|
||||||
|
var ?spawnOffset:Void->Vector;
|
||||||
|
|
||||||
|
var particleOptions:ParticleOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
@:publicFields
|
||||||
|
class ParticleEmitter {
|
||||||
|
var o:ParticleEmitterOptions;
|
||||||
|
var data:ParticleData;
|
||||||
|
var manager:ParticleManager;
|
||||||
|
var spawnTime:Float;
|
||||||
|
var lastEmitTime:Float;
|
||||||
|
var currentWaitPeriod:Float;
|
||||||
|
var lastPos:Vector;
|
||||||
|
var lastPosTime:Float;
|
||||||
|
var currPos:Vector;
|
||||||
|
var currPosTime:Float;
|
||||||
|
var creationTime:Float;
|
||||||
|
var vel = new Vector();
|
||||||
|
var getPos:Void->Vector;
|
||||||
|
|
||||||
|
public function new(options:ParticleEmitterOptions, data:ParticleData, manager:ParticleManager, ?getPos:Void->Vector) {
|
||||||
|
this.o = options;
|
||||||
|
this.manager = manager;
|
||||||
|
this.getPos = getPos;
|
||||||
|
this.data = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function spawn(time:Float) {
|
||||||
|
this.spawnTime = time;
|
||||||
|
this.emit(time);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function tick(time:Float) {
|
||||||
|
// Cap the amount of particles emitted in such a case to prevent lag
|
||||||
|
if (time - this.lastEmitTime >= 1000)
|
||||||
|
this.lastEmitTime = time - 1000;
|
||||||
|
// Spawn as many particles as needed
|
||||||
|
while (this.lastEmitTime + this.currentWaitPeriod <= time) {
|
||||||
|
this.emit(this.lastEmitTime + this.currentWaitPeriod);
|
||||||
|
var completion = Util.clamp((this.lastEmitTime - this.spawnTime) / this.o.emitterLifetime, 0, 1);
|
||||||
|
if (completion == 1) {
|
||||||
|
this.manager.removeEmitter(this);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Emit a single particle. */
|
||||||
|
public function emit(time:Float) {
|
||||||
|
this.lastEmitTime = time;
|
||||||
|
this.currentWaitPeriod = this.o.ejectionPeriod;
|
||||||
|
var pos = this.getPosAtTime(time).clone();
|
||||||
|
if (this.o.spawnOffset != null)
|
||||||
|
pos.add(this.o.spawnOffset()); // Call the spawnOffset function if it's there
|
||||||
|
// This isn't necessarily uniform but it's fine for the purpose.
|
||||||
|
var randomPointOnSphere = new Vector(Math.random() * 2 - 1, Math.random() * 2 - 1, Math.random() * 2 - 1).normalized();
|
||||||
|
// Compute the total velocity
|
||||||
|
var vel = this.vel.multiply(this.o.inheritedVelFactor)
|
||||||
|
.add(randomPointOnSphere.multiply(this.o.ejectionVelocity + this.o.velocityVariance * (Math.random() * 2 - 1)))
|
||||||
|
.add(this.o.ambientVelocity);
|
||||||
|
var particle = new Particle(this.o.particleOptions, this.manager, this.data, time, pos, vel);
|
||||||
|
this.manager.addParticle(data, particle);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Computes the interpolated emitter position at a point in time. */
|
||||||
|
public function getPosAtTime(time:Float) {
|
||||||
|
if (this.lastPos == null)
|
||||||
|
return this.currPos;
|
||||||
|
var completion = Util.clamp((time - this.lastPosTime) / (this.currPosTime - this.lastPosTime), 0, 1);
|
||||||
|
return Util.lerpThreeVectors(this.lastPos, this.currPos, completion);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setPos(pos:Vector, time:Float) {
|
||||||
|
this.lastPos = this.currPos;
|
||||||
|
this.lastPosTime = this.currPosTime;
|
||||||
|
this.currPos = pos.clone();
|
||||||
|
this.currPosTime = time;
|
||||||
|
this.vel = this.currPos.sub(this.lastPos).multiply(1000 / (this.currPosTime - this.lastPosTime));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ParticleManager {
|
||||||
|
var particlebatches:Map<String, ParticleBatch> = new Map();
|
||||||
|
var level:MarbleWorld;
|
||||||
|
var scene:Scene;
|
||||||
|
var currentTime:Float;
|
||||||
|
|
||||||
|
var emitters:Array<ParticleEmitter> = [];
|
||||||
|
|
||||||
|
public function new(level:MarbleWorld) {
|
||||||
|
this.level = level;
|
||||||
|
this.scene = level.scene;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function update(currentTime:Float, dt:Float) {
|
||||||
|
this.tick();
|
||||||
|
this.currentTime = currentTime;
|
||||||
|
for (obj => batch in particlebatches) {
|
||||||
|
for (instance in batch.instances)
|
||||||
|
instance.update(currentTime, dt);
|
||||||
|
}
|
||||||
|
for (obj => batch in particlebatches) {
|
||||||
|
batch.meshBatch.begin(batch.instances.length);
|
||||||
|
for (instance in batch.instances) {
|
||||||
|
if (instance.currentAge != 0) {
|
||||||
|
batch.meshBatch.setPosition(instance.position.x, instance.position.y, instance.position.z);
|
||||||
|
var particleShader = batch.meshBatch.material.mainPass.getShader(Billboard);
|
||||||
|
particleShader.scale = instance.scale;
|
||||||
|
particleShader.rotation = instance.rotation;
|
||||||
|
batch.meshBatch.material.blendMode = instance.o.blending;
|
||||||
|
batch.meshBatch.material.color.load(instance.color);
|
||||||
|
batch.meshBatch.shadersChanged = true;
|
||||||
|
batch.meshBatch.setScale(instance.scale);
|
||||||
|
batch.meshBatch.emitInstance();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function addParticle(particleData:ParticleData, particle:Particle) {
|
||||||
|
if (particlebatches.exists(particleData.identifier)) {
|
||||||
|
particlebatches.get(particleData.identifier).instances.push(particle);
|
||||||
|
} else {
|
||||||
|
var pts = [
|
||||||
|
new Point(-0.5, -0.5, 0),
|
||||||
|
new Point(-0.5, 0.5, 0),
|
||||||
|
new Point(0.5, -0.5, 0),
|
||||||
|
new Point(0.5, 0.5)
|
||||||
|
];
|
||||||
|
var prim = new Polygon(pts);
|
||||||
|
prim.idx = new IndexBuffer();
|
||||||
|
prim.idx.push(0);
|
||||||
|
prim.idx.push(1);
|
||||||
|
prim.idx.push(2);
|
||||||
|
prim.idx.push(1);
|
||||||
|
prim.idx.push(3);
|
||||||
|
prim.idx.push(2);
|
||||||
|
prim.uvs = [new UV(0, 0), new UV(0, 1), new UV(1, 0), new UV(1, 1)];
|
||||||
|
prim.addNormals();
|
||||||
|
var mat = Material.create(particleData.texture);
|
||||||
|
mat.mainPass.addShader(new h3d.shader.pbr.PropsValues(1, 0, 0, 1));
|
||||||
|
mat.texture.wrap = Wrap.Repeat;
|
||||||
|
mat.blendMode = Alpha;
|
||||||
|
var billboardShader = new Billboard();
|
||||||
|
mat.mainPass.addShader(billboardShader);
|
||||||
|
var mb = new MeshBatch(prim, mat, this.scene);
|
||||||
|
var batch:ParticleBatch = {
|
||||||
|
instances: [particle],
|
||||||
|
meshBatch: mb
|
||||||
|
};
|
||||||
|
particlebatches.set(particleData.identifier, batch);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function removeParticle(particleData:ParticleData, particle:Particle) {
|
||||||
|
if (particlebatches.exists(particleData.identifier)) {
|
||||||
|
particlebatches.get(particleData.identifier).instances.remove(particle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTime() {
|
||||||
|
return this.currentTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function createEmitter(options:ParticleEmitterOptions, data:ParticleData, initialPos:Vector, ?getPos:Void->Vector) {
|
||||||
|
var emitter = new ParticleEmitter(options, data, cast this, getPos);
|
||||||
|
emitter.currPos = (getPos != null) ? getPos() : initialPos.clone();
|
||||||
|
if (emitter.currPos == null)
|
||||||
|
emitter.currPos = initialPos.clone();
|
||||||
|
emitter.currPosTime = this.getTime();
|
||||||
|
emitter.creationTime = this.getTime();
|
||||||
|
emitter.spawn(this.getTime());
|
||||||
|
this.emitters.push(emitter);
|
||||||
|
return emitter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function removeEmitter(emitter:ParticleEmitter) {
|
||||||
|
this.emitters.remove(emitter);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function removeEverything() {
|
||||||
|
for (particle in this.particlebatches) {
|
||||||
|
particle.instances = [];
|
||||||
|
}
|
||||||
|
for (emitter in this.emitters)
|
||||||
|
this.removeEmitter(emitter);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function tick() {
|
||||||
|
var time = this.getTime();
|
||||||
|
for (emitter in this.emitters) {
|
||||||
|
if (emitter.getPos != null)
|
||||||
|
emitter.setPos(emitter.getPos(), time);
|
||||||
|
emitter.tick(time);
|
||||||
|
// Remove the artifact that was created in a different future cause we rewinded and now we shifted timelines
|
||||||
|
if (emitter.creationTime > time) {
|
||||||
|
this.removeEmitter(emitter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -33,7 +33,7 @@ class Util {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function lerpThreeVectors(v1:Vector, v2:Vector, t:Float) {
|
public static function lerpThreeVectors(v1:Vector, v2:Vector, t:Float) {
|
||||||
return new Vector(lerp(v1.x, v2.x, t), lerp(v1.y, v2.y, t), lerp(v1.z, v2.z, t));
|
return new Vector(lerp(v1.x, v2.x, t), lerp(v1.y, v2.y, t), lerp(v1.z, v2.z, t), lerp(v1.w, v2.w, t));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function rotateImage(bitmap:BitmapData, angle:Float) {
|
public static function rotateImage(bitmap:BitmapData, angle:Float) {
|
||||||
|
|
|
||||||
33
src/shaders/Billboard.hx
Normal file
33
src/shaders/Billboard.hx
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
package shaders;
|
||||||
|
|
||||||
|
class Billboard extends hxsl.Shader {
|
||||||
|
static var SRC = {
|
||||||
|
@input var input:{
|
||||||
|
var uv:Vec2;
|
||||||
|
};
|
||||||
|
@global var camera:{
|
||||||
|
var view:Mat4;
|
||||||
|
var proj:Mat4;
|
||||||
|
};
|
||||||
|
@global var global:{
|
||||||
|
@perObject var modelView:Mat4;
|
||||||
|
};
|
||||||
|
@param var scale:Float;
|
||||||
|
@param var rotation:Float;
|
||||||
|
var relativePosition:Vec3;
|
||||||
|
var projectedPosition:Vec4;
|
||||||
|
var calculatedUV:Vec2;
|
||||||
|
function vertex() {
|
||||||
|
var mid = 0.5;
|
||||||
|
var uv = input.uv;
|
||||||
|
calculatedUV.x = cos(rotation) * (uv.x - mid) + sin(rotation) * (uv.y - mid) + mid;
|
||||||
|
calculatedUV.y = cos(rotation) * (uv.y - mid) - sin(rotation) * (uv.x - mid) + mid;
|
||||||
|
}
|
||||||
|
function billboard(pos:Vec2, scale:Vec2):Vec4 {
|
||||||
|
return (vec4(0, 0, 0, 1) * (global.modelView * camera.view) + vec4(pos * scale, 0, 0));
|
||||||
|
}
|
||||||
|
function __init__() {
|
||||||
|
projectedPosition = billboard(relativePosition.xy, vec2(scale, scale)) * camera.proj;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,14 +1,45 @@
|
||||||
package shapes;
|
package shapes;
|
||||||
|
|
||||||
|
import src.ResourceLoader;
|
||||||
|
import src.ParticleSystem.ParticleData;
|
||||||
|
import h3d.Vector;
|
||||||
import src.DtsObject;
|
import src.DtsObject;
|
||||||
|
|
||||||
|
final superJumpParticleOptions:src.ParticleSystem.ParticleEmitterOptions = {
|
||||||
|
ejectionPeriod: 10,
|
||||||
|
ambientVelocity: new Vector(0, 0, 0.05),
|
||||||
|
ejectionVelocity: 1 * 0.5,
|
||||||
|
velocityVariance: 0.25 * 0.5,
|
||||||
|
emitterLifetime: 1000,
|
||||||
|
inheritedVelFactor: 0.1,
|
||||||
|
particleOptions: {
|
||||||
|
texture: 'particles/twirl.png',
|
||||||
|
blending: Add,
|
||||||
|
spinSpeed: 90,
|
||||||
|
spinRandomMin: -90,
|
||||||
|
spinRandomMax: 90,
|
||||||
|
lifetime: 1000,
|
||||||
|
lifetimeVariance: 150,
|
||||||
|
dragCoefficient: 0.25,
|
||||||
|
acceleration: 0,
|
||||||
|
colors: [new Vector(0, 0.5, 1, 0), new Vector(0, 0.6, 1, 1), new Vector(0, 0.5, 1, 0)],
|
||||||
|
sizes: [0.25, 0.25, 0.5],
|
||||||
|
times: [0, 0.75, 1]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
class SuperJump extends PowerUp {
|
class SuperJump extends PowerUp {
|
||||||
|
var sjEmitterParticleData:ParticleData;
|
||||||
|
|
||||||
public function new() {
|
public function new() {
|
||||||
super();
|
super();
|
||||||
this.dtsPath = "data/shapes/items/superjump.dts";
|
this.dtsPath = "data/shapes/items/superjump.dts";
|
||||||
this.isCollideable = false;
|
this.isCollideable = false;
|
||||||
this.isTSStatic = false;
|
this.isTSStatic = false;
|
||||||
this.identifier = "SuperJump";
|
this.identifier = "SuperJump";
|
||||||
|
sjEmitterParticleData = new ParticleData();
|
||||||
|
sjEmitterParticleData.identifier = "superJumpParticle";
|
||||||
|
sjEmitterParticleData.texture = ResourceLoader.getTexture("data/particles/twirl.png");
|
||||||
}
|
}
|
||||||
|
|
||||||
public function pickUp():Bool {
|
public function pickUp():Bool {
|
||||||
|
|
@ -18,6 +49,7 @@ class SuperJump extends PowerUp {
|
||||||
public function use(time:Float) {
|
public function use(time:Float) {
|
||||||
var marble = this.level.marble;
|
var marble = this.level.marble;
|
||||||
marble.velocity = marble.velocity.add(this.level.currentUp.multiply(20));
|
marble.velocity = marble.velocity.add(this.level.currentUp.multiply(20));
|
||||||
|
this.level.particleManager.createEmitter(superJumpParticleOptions, this.sjEmitterParticleData, null, () -> marble.getAbsPos().getPosition());
|
||||||
// marble.body.addLinearVelocity(this.level.currentUp.scale(20)); // Simply add to vertical velocity
|
// marble.body.addLinearVelocity(this.level.currentUp.scale(20)); // Simply add to vertical velocity
|
||||||
// if (!this.level.rewinding)
|
// if (!this.level.rewinding)
|
||||||
// AudioManager.play(this.sounds[1]);
|
// AudioManager.play(this.sounds[1]);
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,42 @@
|
||||||
package shapes;
|
package shapes;
|
||||||
|
|
||||||
|
import src.ResourceLoader;
|
||||||
|
import src.ParticleSystem.ParticleData;
|
||||||
|
import src.ParticleSystem.ParticleEmitterOptions;
|
||||||
import h3d.Quat;
|
import h3d.Quat;
|
||||||
import h3d.Vector;
|
import h3d.Vector;
|
||||||
import src.DtsObject;
|
import src.DtsObject;
|
||||||
|
|
||||||
|
final superSpeedParticleOptions:ParticleEmitterOptions = {
|
||||||
|
ejectionPeriod: 5,
|
||||||
|
ambientVelocity: new Vector(0, 0, 0.2),
|
||||||
|
ejectionVelocity: 1 * 0.5,
|
||||||
|
velocityVariance: 0.25 * 0.5,
|
||||||
|
emitterLifetime: 1100,
|
||||||
|
inheritedVelFactor: 0.25,
|
||||||
|
particleOptions: {
|
||||||
|
texture: 'particles/spark.png',
|
||||||
|
blending: Add,
|
||||||
|
spinSpeed: 0,
|
||||||
|
spinRandomMin: 0,
|
||||||
|
spinRandomMax: 0,
|
||||||
|
lifetime: 1500,
|
||||||
|
lifetimeVariance: 150,
|
||||||
|
dragCoefficient: 0.25,
|
||||||
|
acceleration: 0,
|
||||||
|
colors: [
|
||||||
|
new Vector(0.8, 0.8, 0, 0),
|
||||||
|
new Vector(0.8, 0.8, 0, 1),
|
||||||
|
new Vector(0.8, 0.8, 0, 0)
|
||||||
|
],
|
||||||
|
sizes: [0.25, 0.25, 1],
|
||||||
|
times: [0, 0.25, 1]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
class SuperSpeed extends PowerUp {
|
class SuperSpeed extends PowerUp {
|
||||||
|
var ssEmitterParticleData:ParticleData;
|
||||||
|
|
||||||
public function new() {
|
public function new() {
|
||||||
super();
|
super();
|
||||||
this.dtsPath = "data/shapes/items/superspeed.dts";
|
this.dtsPath = "data/shapes/items/superspeed.dts";
|
||||||
|
|
@ -12,6 +44,9 @@ class SuperSpeed extends PowerUp {
|
||||||
this.isTSStatic = false;
|
this.isTSStatic = false;
|
||||||
this.identifier = "SuperSpeed";
|
this.identifier = "SuperSpeed";
|
||||||
this.useInstancing = true;
|
this.useInstancing = true;
|
||||||
|
ssEmitterParticleData = new ParticleData();
|
||||||
|
ssEmitterParticleData.identifier = "superSpeedParticle";
|
||||||
|
ssEmitterParticleData.texture = ResourceLoader.getTexture("data/particles/spark.png");
|
||||||
}
|
}
|
||||||
|
|
||||||
public function pickUp():Bool {
|
public function pickUp():Bool {
|
||||||
|
|
@ -37,7 +72,7 @@ class SuperSpeed extends PowerUp {
|
||||||
// marble.body.addLinearVelocity(this.level.currentUp.scale(20)); // Simply add to vertical velocity
|
// marble.body.addLinearVelocity(this.level.currentUp.scale(20)); // Simply add to vertical velocity
|
||||||
// if (!this.level.rewinding)
|
// if (!this.level.rewinding)
|
||||||
// AudioManager.play(this.sounds[1]);
|
// AudioManager.play(this.sounds[1]);
|
||||||
// this.level.particles.createEmitter(superJumpParticleOptions, null, () => Util.vecOimoToThree(marble.body.getPosition()));
|
this.level.particleManager.createEmitter(superSpeedParticleOptions, this.ssEmitterParticleData, null, () -> marble.getAbsPos().getPosition());
|
||||||
// this.level.deselectPowerUp();
|
// this.level.deselectPowerUp();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue