|
@@ -33,15 +33,13 @@
|
|
private _renderEffect: Effect;
|
|
private _renderEffect: Effect;
|
|
private _updateEffect: Effect;
|
|
private _updateEffect: Effect;
|
|
|
|
|
|
- private _updateBuffer: Buffer;
|
|
|
|
- private _updateVAO: WebGLVertexArrayObject;
|
|
|
|
- private _updateVertexBuffers: {[key: string]: VertexBuffer} = {};
|
|
|
|
- private _renderBuffer: Buffer;
|
|
|
|
- private _renderVAO: WebGLVertexArrayObject;
|
|
|
|
- private _renderVertexBuffers: {[key: string]: VertexBuffer} = {};
|
|
|
|
-
|
|
|
|
- private _sourceVAO: WebGLVertexArrayObject;
|
|
|
|
- private _targetVAO: WebGLVertexArrayObject;
|
|
|
|
|
|
+ private _buffer0: Buffer;
|
|
|
|
+ private _buffer1: Buffer;
|
|
|
|
+ private _spriteBuffer: Buffer;
|
|
|
|
+ private _updateVAO = new Array<WebGLVertexArrayObject>();
|
|
|
|
+ private _renderVAO = new Array<WebGLVertexArrayObject>()
|
|
|
|
+
|
|
|
|
+ private _targetIndex = 0;
|
|
private _sourceBuffer: Buffer;
|
|
private _sourceBuffer: Buffer;
|
|
private _targetBuffer: Buffer;
|
|
private _targetBuffer: Buffer;
|
|
|
|
|
|
@@ -49,7 +47,11 @@
|
|
private _engine: Engine;
|
|
private _engine: Engine;
|
|
|
|
|
|
private _currentRenderId = -1;
|
|
private _currentRenderId = -1;
|
|
- private _started = true;
|
|
|
|
|
|
+ private _started = false;
|
|
|
|
+
|
|
|
|
+ private _timeDelta = 0;
|
|
|
|
+
|
|
|
|
+ private _randomTexture: RawTexture;
|
|
|
|
|
|
/**
|
|
/**
|
|
* An event triggered when the system is disposed.
|
|
* An event triggered when the system is disposed.
|
|
@@ -57,6 +59,21 @@
|
|
public onDisposeObservable = new Observable<GPUParticleSystem>();
|
|
public onDisposeObservable = new Observable<GPUParticleSystem>();
|
|
|
|
|
|
/**
|
|
/**
|
|
|
|
+ * The overall motion speed (0.01 is default update speed, faster updates = faster animation)
|
|
|
|
+ */
|
|
|
|
+ public updateSpeed = 0.01;
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * The texture used to render each particle. (this can be a spritesheet)
|
|
|
|
+ */
|
|
|
|
+ public particleTexture: Nullable<Texture>;
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Blend mode use to render the particle, it can be either ParticleSystem.BLENDMODE_ONEONE or ParticleSystem.BLENDMODE_STANDARD.
|
|
|
|
+ */
|
|
|
|
+ public blendMode = ParticleSystem.BLENDMODE_ONEONE;
|
|
|
|
+
|
|
|
|
+ /**
|
|
* Gets Wether the system has been started.
|
|
* Gets Wether the system has been started.
|
|
* @returns True if it has been started, otherwise false.
|
|
* @returns True if it has been started, otherwise false.
|
|
*/
|
|
*/
|
|
@@ -94,82 +111,124 @@
|
|
|
|
|
|
this._scene.particleSystems.push(this);
|
|
this._scene.particleSystems.push(this);
|
|
|
|
|
|
- this._renderEffect = new Effect("gpuRenderParticles", ["position", "age", "life", "velocity"], [], [], this._scene.getEngine());
|
|
|
|
-
|
|
|
|
let updateEffectOptions: EffectCreationOptions = {
|
|
let updateEffectOptions: EffectCreationOptions = {
|
|
- attributes: ["position", "age", "life", "velocity"],
|
|
|
|
- uniformsNames: [],
|
|
|
|
|
|
+ attributes: ["position", "age", "life", "seed", "direction"],
|
|
|
|
+ uniformsNames: ["timeDelta", "generalRandom", "emitterWM"],
|
|
uniformBuffersNames: [],
|
|
uniformBuffersNames: [],
|
|
- samplers:[],
|
|
|
|
|
|
+ samplers:["randomSampler"],
|
|
defines: "",
|
|
defines: "",
|
|
fallbacks: null,
|
|
fallbacks: null,
|
|
onCompiled: null,
|
|
onCompiled: null,
|
|
onError: null,
|
|
onError: null,
|
|
indexParameters: null,
|
|
indexParameters: null,
|
|
maxSimultaneousLights: 0,
|
|
maxSimultaneousLights: 0,
|
|
- transformFeedbackVaryings: ["outPosition", "outAge", "outLife", "outVelocity"]
|
|
|
|
|
|
+ transformFeedbackVaryings: ["outPosition", "outAge", "outLife", "outSeed", "outDirection"]
|
|
};
|
|
};
|
|
|
|
|
|
this._updateEffect = new Effect("gpuUpdateParticles", updateEffectOptions, this._scene.getEngine());
|
|
this._updateEffect = new Effect("gpuUpdateParticles", updateEffectOptions, this._scene.getEngine());
|
|
|
|
+
|
|
|
|
+ this._renderEffect = new Effect("gpuRenderParticles", ["position", "offset", "uv"], ["view", "projection"], ["textureSampler"], this._scene.getEngine());
|
|
|
|
+
|
|
|
|
+ // Random data
|
|
|
|
+ var d = [];
|
|
|
|
+ for (var i = 0; i < 4096; ++i) {
|
|
|
|
+ d.push(Math.random());
|
|
|
|
+ d.push(Math.random());
|
|
|
|
+ d.push(Math.random());
|
|
|
|
+ }
|
|
|
|
+ this._randomTexture = new RawTexture(new Float32Array(d), 4096, 1, Engine.TEXTUREFORMAT_RGB32F, this._scene, false, false, Texture.NEAREST_SAMPLINGMODE, Engine.TEXTURETYPE_FLOAT)
|
|
|
|
+ this._randomTexture.wrapU = Texture.WRAP_ADDRESSMODE;
|
|
|
|
+ this._randomTexture.wrapV = Texture.WRAP_ADDRESSMODE;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
* Animates the particle system for the current frame by emitting new particles and or animating the living ones.
|
|
* Animates the particle system for the current frame by emitting new particles and or animating the living ones.
|
|
*/
|
|
*/
|
|
public animate(): void {
|
|
public animate(): void {
|
|
- // Do nothing
|
|
|
|
|
|
+ if (this._currentRenderId === this._scene.getRenderId()) {
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ this._currentRenderId = this._scene.getRenderId();
|
|
|
|
+ this._timeDelta = this.updateSpeed * this._scene.getAnimationRatio();
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ private _createUpdateVAO(source: Buffer): WebGLVertexArrayObject {
|
|
|
|
+ let updateVertexBuffers: {[key: string]: VertexBuffer} = {};
|
|
|
|
+ updateVertexBuffers["position"] = source.createVertexBuffer("position", 0, 3);
|
|
|
|
+ updateVertexBuffers["age"] = source.createVertexBuffer("age", 3, 1);
|
|
|
|
+ updateVertexBuffers["life"] = source.createVertexBuffer("life", 4, 1);
|
|
|
|
+ updateVertexBuffers["seed"] = source.createVertexBuffer("seed", 5, 1);
|
|
|
|
+ updateVertexBuffers["direction"] = source.createVertexBuffer("direction", 6, 3);
|
|
|
|
+
|
|
|
|
+ let vao = this._engine.recordVertexArrayObject(updateVertexBuffers, null, this._updateEffect);
|
|
|
|
+ this._engine.bindArrayBuffer(null);
|
|
|
|
+
|
|
|
|
+ return vao;
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+ private _createRenderVAO(source: Buffer, spriteSource: Buffer): WebGLVertexArrayObject {
|
|
|
|
+ let renderVertexBuffers: {[key: string]: VertexBuffer} = {};
|
|
|
|
+ renderVertexBuffers["position"] = source.createVertexBuffer("position", 0, 3, 9, true);
|
|
|
|
+ renderVertexBuffers["offset"] = spriteSource.createVertexBuffer("offset", 0, 2);
|
|
|
|
+ renderVertexBuffers["uv"] = spriteSource.createVertexBuffer("uv", 2, 2);
|
|
|
|
+
|
|
|
|
+ let vao = this._engine.recordVertexArrayObject(renderVertexBuffers, null, this._renderEffect);
|
|
|
|
+ this._engine.bindArrayBuffer(null);
|
|
|
|
+
|
|
|
|
+ return vao;
|
|
|
|
+ }
|
|
|
|
|
|
private _initialize(): void {
|
|
private _initialize(): void {
|
|
- if (this._renderVAO) {
|
|
|
|
|
|
+ if (this._buffer0) {
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ let engine = this._scene.getEngine();
|
|
var data = new Array<float>();
|
|
var data = new Array<float>();
|
|
for (var particleIndex = 0; particleIndex < this._capacity; particleIndex++) {
|
|
for (var particleIndex = 0; particleIndex < this._capacity; particleIndex++) {
|
|
// position
|
|
// position
|
|
data.push(0.0);
|
|
data.push(0.0);
|
|
data.push(0.0);
|
|
data.push(0.0);
|
|
data.push(0.0);
|
|
data.push(0.0);
|
|
-
|
|
|
|
- var life = 1 + Math.random() * 10; // TODO: var
|
|
|
|
|
|
+
|
|
|
|
+ // Age and life
|
|
|
|
+ var life = 1 + Math.random();// * 10;
|
|
data.push(life + 1); // create the particle as a dead one to create a new one at start
|
|
data.push(life + 1); // create the particle as a dead one to create a new one at start
|
|
data.push(life);
|
|
data.push(life);
|
|
-
|
|
|
|
- // velocity
|
|
|
|
- data.push(0.0);
|
|
|
|
|
|
+
|
|
|
|
+ // Seed
|
|
|
|
+ data.push(Math.random());
|
|
|
|
+
|
|
|
|
+ // direction
|
|
data.push(0.0);
|
|
data.push(0.0);
|
|
data.push(0.0);
|
|
data.push(0.0);
|
|
|
|
+ data.push(0.0);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ // Sprite data
|
|
|
|
+ var spriteData = new Float32Array([1, 1, 1, 1, -1, 1, 0, 1,
|
|
|
|
+ -1, -1, 0, 0, 1, 1, 1, 1,
|
|
|
|
+ -1, -1, 0, 0, 1, -1, 1, 0]);
|
|
|
|
+
|
|
|
|
+ // Buffers
|
|
|
|
+ this._buffer0 = new Buffer(engine, data, false, 9);
|
|
|
|
+ this._buffer1 = new Buffer(engine, data, false, 9);
|
|
|
|
+ this._spriteBuffer = new Buffer(engine, spriteData, false, 4);
|
|
|
|
+
|
|
// Update VAO
|
|
// Update VAO
|
|
- this._updateBuffer = new Buffer(this._scene.getEngine(), data, false, 0);
|
|
|
|
- this._updateVertexBuffers["position"] = this._updateBuffer.createVertexBuffer("position", 0, 3, 3);
|
|
|
|
- this._updateVertexBuffers["age"] = this._updateBuffer.createVertexBuffer("age", 3, 1, 1);
|
|
|
|
- this._updateVertexBuffers["life"] = this._updateBuffer.createVertexBuffer("life", 4, 1, 1);
|
|
|
|
- this._updateVertexBuffers["velocity"] = this._updateBuffer.createVertexBuffer("velocity", 5, 3, 3);
|
|
|
|
-
|
|
|
|
- this._updateVAO = this._engine.recordVertexArrayObject(this._updateVertexBuffers, null, this._updateEffect);
|
|
|
|
- this._engine.bindArrayBuffer(null);
|
|
|
|
|
|
+ this._updateVAO.push(this._createUpdateVAO(this._buffer0));
|
|
|
|
+ this._updateVAO.push(this._createUpdateVAO(this._buffer1));
|
|
|
|
|
|
// Render VAO
|
|
// Render VAO
|
|
- this._renderBuffer = new Buffer(this._scene.getEngine(), data, false, 0);
|
|
|
|
- this._renderVertexBuffers["position"] = this._renderBuffer.createVertexBuffer("position", 0, 3, 3);
|
|
|
|
- this._renderVertexBuffers["age"] = this._renderBuffer.createVertexBuffer("age", 3, 1, 1);
|
|
|
|
- this._renderVertexBuffers["life"] = this._renderBuffer.createVertexBuffer("life", 4, 1, 1);
|
|
|
|
- this._renderVertexBuffers["velocity"] = this._renderBuffer.createVertexBuffer("velocity", 5, 3, 3);
|
|
|
|
-
|
|
|
|
- this._renderVAO = this._engine.recordVertexArrayObject(this._renderVertexBuffers, null, this._renderEffect);
|
|
|
|
- this._engine.bindArrayBuffer(null);
|
|
|
|
|
|
+ this._renderVAO.push(this._createRenderVAO(this._buffer1, this._spriteBuffer));
|
|
|
|
+ this._renderVAO.push(this._createRenderVAO(this._buffer0, this._spriteBuffer));
|
|
|
|
|
|
// Links
|
|
// Links
|
|
- this._sourceVAO = this._updateVAO;
|
|
|
|
- this._targetVAO = this._renderVAO;
|
|
|
|
|
|
+ this._sourceBuffer = this._buffer0;
|
|
|
|
+ this._targetBuffer = this._buffer1;
|
|
|
|
|
|
- this._sourceBuffer = this._updateBuffer;
|
|
|
|
- this._targetBuffer = this._renderBuffer;
|
|
|
|
}
|
|
}
|
|
-
|
|
|
|
/**
|
|
/**
|
|
* Renders the particle system in its current state.
|
|
* Renders the particle system in its current state.
|
|
* @returns the current number of particles.
|
|
* @returns the current number of particles.
|
|
@@ -182,18 +241,26 @@
|
|
// Get everything ready to render
|
|
// Get everything ready to render
|
|
this. _initialize();
|
|
this. _initialize();
|
|
|
|
|
|
- if (this._currentRenderId === this._scene.getRenderId()) {
|
|
|
|
- return 0;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- this._currentRenderId = this._scene.getRenderId();
|
|
|
|
-
|
|
|
|
// Enable update effect
|
|
// Enable update effect
|
|
this._engine.enableEffect(this._updateEffect);
|
|
this._engine.enableEffect(this._updateEffect);
|
|
- this._engine.setState(false);
|
|
|
|
|
|
+ this._engine.setState(false);
|
|
|
|
+
|
|
|
|
+ this._updateEffect.setFloat("timeDelta", this._timeDelta);
|
|
|
|
+ this._updateEffect.setFloat("generalRandom", Math.random());
|
|
|
|
+ this._updateEffect.setTexture("randomSampler", this._randomTexture);
|
|
|
|
+
|
|
|
|
+ let emitterWM: Matrix;
|
|
|
|
+ if ((<AbstractMesh>this.emitter).position) {
|
|
|
|
+ var emitterMesh = (<AbstractMesh>this.emitter);
|
|
|
|
+ emitterWM = emitterMesh.getWorldMatrix();
|
|
|
|
+ } else {
|
|
|
|
+ var emitterPosition = (<Vector3>this.emitter);
|
|
|
|
+ emitterWM = Matrix.Translation(emitterPosition.x, emitterPosition.y, emitterPosition.z);
|
|
|
|
+ }
|
|
|
|
+ this._updateEffect.setMatrix("emitterWM", emitterWM);
|
|
|
|
|
|
// Bind source VAO
|
|
// Bind source VAO
|
|
- this._engine.bindVertexArrayObject(this._sourceVAO, null);
|
|
|
|
|
|
+ this._engine.bindVertexArrayObject(this._updateVAO[this._targetIndex], null);
|
|
|
|
|
|
// Update
|
|
// Update
|
|
this._engine.bindTransformFeedbackBuffer(this._targetBuffer.getBuffer());
|
|
this._engine.bindTransformFeedbackBuffer(this._targetBuffer.getBuffer());
|
|
@@ -206,17 +273,29 @@
|
|
|
|
|
|
// Enable render effect
|
|
// Enable render effect
|
|
this._engine.enableEffect(this._renderEffect);
|
|
this._engine.enableEffect(this._renderEffect);
|
|
|
|
+ this._renderEffect.setMatrix("view", this._scene.getViewMatrix());
|
|
|
|
+ this._renderEffect.setMatrix("projection", this._scene.getProjectionMatrix());
|
|
|
|
+ this._renderEffect.setTexture("textureSampler", this.particleTexture);
|
|
|
|
+
|
|
|
|
+ // Draw order
|
|
|
|
+ if (this.blendMode === ParticleSystem.BLENDMODE_ONEONE) {
|
|
|
|
+ this._engine.setAlphaMode(Engine.ALPHA_ONEONE);
|
|
|
|
+ } else {
|
|
|
|
+ this._engine.setAlphaMode(Engine.ALPHA_COMBINE);
|
|
|
|
+ }
|
|
|
|
|
|
// Bind source VAO
|
|
// Bind source VAO
|
|
- this._engine.bindVertexArrayObject(this._targetVAO, null);
|
|
|
|
|
|
+ this._engine.bindVertexArrayObject(this._renderVAO[this._targetIndex], null);
|
|
|
|
|
|
// Render
|
|
// Render
|
|
- this._engine.drawArraysType(Material.PointListDrawMode, 0, this._capacity);
|
|
|
|
|
|
+ this._engine.drawArraysType(Material.TriangleFillMode, 0, 6, this._capacity);
|
|
|
|
+ this._engine.setAlphaMode(Engine.ALPHA_DISABLE);
|
|
|
|
|
|
// Switch VAOs
|
|
// Switch VAOs
|
|
- let tmpVAO = this._sourceVAO;
|
|
|
|
- this._sourceVAO = this._targetVAO;
|
|
|
|
- this._targetVAO = tmpVAO;
|
|
|
|
|
|
+ this._targetIndex++;
|
|
|
|
+ if (this._targetIndex === 2) {
|
|
|
|
+ this._targetIndex = 0;
|
|
|
|
+ }
|
|
|
|
|
|
// Switch buffers
|
|
// Switch buffers
|
|
let tmpBuffer = this._sourceBuffer;
|
|
let tmpBuffer = this._sourceBuffer;
|
|
@@ -242,7 +321,29 @@
|
|
this._scene.particleSystems.splice(index, 1);
|
|
this._scene.particleSystems.splice(index, 1);
|
|
}
|
|
}
|
|
|
|
|
|
- //TODO: this._dataBuffer.dispose();
|
|
|
|
|
|
+ if (this._buffer0) {
|
|
|
|
+ this._buffer0.dispose();
|
|
|
|
+ (<any>this._buffer0) = null;
|
|
|
|
+ }
|
|
|
|
+ if (this._buffer1) {
|
|
|
|
+ this._buffer1.dispose();
|
|
|
|
+ (<any>this._buffer1) = null;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ for (var index = 0; index < this._updateVAO.length; index++) {
|
|
|
|
+ this._engine.releaseVertexArrayObject(this._updateVAO[index]);
|
|
|
|
+ }
|
|
|
|
+ this._updateVAO = [];
|
|
|
|
+
|
|
|
|
+ for (var index = 0; index < this._renderVAO.length; index++) {
|
|
|
|
+ this._engine.releaseVertexArrayObject(this._renderVAO[index]);
|
|
|
|
+ }
|
|
|
|
+ this._renderVAO = [];
|
|
|
|
+
|
|
|
|
+ if (this._randomTexture) {
|
|
|
|
+ this._randomTexture.dispose();
|
|
|
|
+ (<any>this._randomTexture) = null;
|
|
|
|
+ }
|
|
|
|
|
|
// Callback
|
|
// Callback
|
|
this.onDisposeObservable.notifyObservers(this);
|
|
this.onDisposeObservable.notifyObservers(this);
|