even more fps boost from fixing particles rendering

This commit is contained in:
RandomityGuy 2024-06-07 19:42:43 +05:30
parent d3610ed338
commit 0c003189e7
2 changed files with 400 additions and 6 deletions

View file

@ -1,11 +1,9 @@
package src;
import shaders.DtsTexture;
import h3d.parts.Particles;
import h3d.Matrix;
import src.TimeState;
import h3d.prim.UV;
import h3d.parts.Data.BlendMode;
import src.MarbleWorld;
import src.Util;
import h3d.mat.Data.Wrap;
@ -33,7 +31,7 @@ class ParticleData {
@:publicFields
class Particle {
public var part:h3d.parts.Particle;
public var part:src.ParticlesMesh.ParticleElement;
var data:ParticleData;
var manager:ParticleManager;
@ -61,7 +59,7 @@ class Particle {
this.lifeTime = this.o.lifetime + this.o.lifetimeVariance * (Math.random() * 2 - 1);
this.initialSpin = Util.lerp(this.o.spinRandomMin, this.o.spinRandomMax, Math.random());
this.part = new h3d.parts.Particle();
this.part = new src.ParticlesMesh.ParticleElement();
}
public function update(time:Float, dt:Float) {
@ -298,7 +296,7 @@ class ParticleManager {
var scene:Scene;
var currentTime:Float;
var particleGroups:Map<String, Particles> = [];
var particleGroups:Map<String, src.ParticlesMesh.ParticlesMesh> = [];
var particles:Array<Particle> = [];
var emitters:Array<ParticleEmitter> = [];
@ -321,7 +319,7 @@ class ParticleManager {
if (particleGroups.exists(particleData.identifier)) {
particleGroups[particleData.identifier].add(particle.part);
} else {
var pGroup = new Particles(particle.data.texture, this.scene);
var pGroup = new src.ParticlesMesh.ParticlesMesh(particle.data.texture, this.scene);
pGroup.hasColor = true;
pGroup.material.setDefaultProps("ui");
// var pdts = new DtsTexture(pGroup.material.texture);

396
src/ParticlesMesh.hx Normal file
View file

@ -0,0 +1,396 @@
package src;
private class ParticleIterator {
var p:ParticleElement;
public inline function new(p) {
this.p = p;
}
public inline function hasNext() {
return p != null;
}
public inline function next() {
var v = p;
p = p.next;
return v;
}
}
enum SortMode {
Front;
Back;
Sort;
InvSort;
}
class ParticleElement {
public var parts:ParticlesMesh;
public var x:Float;
public var y:Float;
public var z:Float;
public var w:Float; // used for sorting
public var r:Float;
public var g:Float;
public var b:Float;
public var a:Float;
public var alpha(get, set):Float;
public var frame:Int;
public var size:Float;
public var ratio:Float;
public var rotation:Float;
public var prev:ParticleElement;
public var next:ParticleElement;
// --- Particle emitter ---
public var time:Float;
public var lifeTimeFactor:Float;
public var dx:Float;
public var dy:Float;
public var dz:Float;
public var fx:Float;
public var fy:Float;
public var fz:Float;
public var randIndex = 0;
public var randValues:Array<Float>;
// -------------------------
public function new() {
r = 1;
g = 1;
b = 1;
a = 1;
frame = 0;
}
inline function get_alpha()
return a;
inline function set_alpha(v)
return a = v;
public function setColor(color:Int, alpha = 1.) {
a = alpha;
r = ((color >> 16) & 0xFF) / 255.;
g = ((color >> 8) & 0xFF) / 255.;
b = (color & 0xFF) / 255.;
}
public function remove() {
if (parts != null) {
@:privateAccess parts.kill(this);
parts = null;
}
}
public function rand():Float {
if (randValues == null)
randValues = [];
if (randValues.length <= randIndex)
randValues.push(Math.random());
return randValues[randIndex++];
}
}
class ParticlesMesh extends h3d.scene.Mesh {
var pshader:h3d.shader.ParticleShader;
public var frames:Array<h2d.Tile>;
public var count(default, null):Int = 0;
public var hasColor(default, set):Bool;
public var sortMode:SortMode;
public var globalSize:Float = 1;
var head:ParticleElement;
var tail:ParticleElement;
var pool:ParticleElement;
var tmp:h3d.Vector;
var tmpBuf:hxd.FloatBuffer;
var buffer:h3d.Buffer;
var bufferSize:Int = 0;
public function new(?texture, ?parent) {
super(null, null, parent);
material.props = material.getDefaultProps("particles3D");
sortMode = Back;
pshader = new h3d.shader.ParticleShader();
pshader.isAbsolute = true;
material.mainPass.addShader(pshader);
material.mainPass.dynamicParameters = true;
material.texture = texture;
tmp = new h3d.Vector();
}
function set_hasColor(b) {
var c = material.mainPass.getShader(h3d.shader.VertexColorAlpha);
if (b) {
if (c == null)
material.mainPass.addShader(new h3d.shader.VertexColorAlpha());
} else {
if (c != null)
material.mainPass.removeShader(c);
}
return hasColor = b;
}
/**
Offset all existing particles by the given values.
**/
public function offsetParticles(dx:Float, dy:Float, dz = 0.) {
var p = head;
while (p != null) {
p.x += dx;
p.y += dy;
p.z += dz;
p = p.next;
}
}
public function clear() {
while (head != null)
kill(head);
}
public function alloc() {
var p = emitParticle();
if (posChanged)
syncPos();
p.parts = this;
p.x = absPos.tx;
p.y = absPos.ty;
p.z = absPos.tz;
p.rotation = 0;
p.ratio = 1;
p.size = 1;
p.r = p.g = p.b = p.a = 1;
return p;
}
public function add(p) {
emitParticle(p);
return p;
}
function emitParticle(?p) {
if (p == null) {
if (pool == null)
p = new ParticleElement();
else {
p = pool;
pool = p.next;
}
}
count++;
switch (sortMode) {
case Front, Sort, InvSort:
if (head == null) {
p.next = null;
head = tail = p;
} else {
head.prev = p;
p.next = head;
head = p;
}
case Back:
if (head == null) {
p.next = null;
head = tail = p;
} else {
tail.next = p;
p.prev = tail;
p.next = null;
tail = p;
}
}
return p;
}
function kill(p:ParticleElement) {
if (p.prev == null)
head = p.next
else
p.prev.next = p.next;
if (p.next == null)
tail = p.prev
else
p.next.prev = p.prev;
p.prev = null;
p.next = pool;
pool = p;
count--;
}
function sort(list:ParticleElement) {
return haxe.ds.ListSort.sort(list, function(p1, p2) return p1.w < p2.w ? 1 : -1);
}
function sortInv(list:ParticleElement) {
return haxe.ds.ListSort.sort(list, function(p1, p2) return p1.w < p2.w ? -1 : 1);
}
public inline function getParticles() {
return new ParticleIterator(head);
}
@:access(h2d.Tile)
@:noDebug
override function draw(ctx:h3d.scene.RenderContext) {
if (head == null)
return;
switch (sortMode) {
case Sort, InvSort:
var p = head;
var m = ctx.camera.m;
while (p != null) {
p.w = (p.x * m._13 + p.y * m._23 + p.z * m._33 + m._43) / (p.x * m._14 + p.y * m._24 + p.z * m._34 + m._44);
p = p.next;
}
head = sortMode == Sort ? sort(head) : sortInv(head);
tail = head.prev;
head.prev = null;
default:
}
if (tmpBuf == null)
tmpBuf = new hxd.FloatBuffer();
var pos = 0;
var p = head;
var tmp = tmpBuf;
var surface = 0.;
if (frames == null || frames.length == 0) {
var t = material.texture == null ? h2d.Tile.fromColor(0xFF00FF) : h2d.Tile.fromTexture(material.texture);
frames = [t];
}
material.texture = frames[0].getTexture();
while (p != null) {
var f = frames[p.frame];
if (f == null)
f = frames[0];
var ratio = p.size * p.ratio * (f.height / f.width);
if (pos >= tmp.length) {
tmp.grow(tmp.length + 40 + (hasColor ? 16 : 0));
}
tmp[pos++] = p.x;
tmp[pos++] = p.y;
tmp[pos++] = p.z;
tmp[pos++] = p.size;
tmp[pos++] = ratio;
tmp[pos++] = p.rotation;
// delta
tmp[pos++] = -0.5;
tmp[pos++] = -0.5;
// UV
tmp[pos++] = f.u;
tmp[pos++] = f.v2;
// RBGA
if (hasColor) {
tmp[pos++] = p.r;
tmp[pos++] = p.g;
tmp[pos++] = p.b;
tmp[pos++] = p.a;
}
tmp[pos++] = p.x;
tmp[pos++] = p.y;
tmp[pos++] = p.z;
tmp[pos++] = p.size;
tmp[pos++] = ratio;
tmp[pos++] = p.rotation;
tmp[pos++] = -0.5;
tmp[pos++] = 0.5;
tmp[pos++] = f.u;
tmp[pos++] = f.v;
if (hasColor) {
tmp[pos++] = p.r;
tmp[pos++] = p.g;
tmp[pos++] = p.b;
tmp[pos++] = p.a;
}
tmp[pos++] = p.x;
tmp[pos++] = p.y;
tmp[pos++] = p.z;
tmp[pos++] = p.size;
tmp[pos++] = ratio;
tmp[pos++] = p.rotation;
tmp[pos++] = 0.5;
tmp[pos++] = -0.5;
tmp[pos++] = f.u2;
tmp[pos++] = f.v2;
if (hasColor) {
tmp[pos++] = p.r;
tmp[pos++] = p.g;
tmp[pos++] = p.b;
tmp[pos++] = p.a;
}
tmp[pos++] = p.x;
tmp[pos++] = p.y;
tmp[pos++] = p.z;
tmp[pos++] = p.size;
tmp[pos++] = ratio;
tmp[pos++] = p.rotation;
tmp[pos++] = 0.5;
tmp[pos++] = 0.5;
tmp[pos++] = f.u2;
tmp[pos++] = f.v;
if (hasColor) {
tmp[pos++] = p.r;
tmp[pos++] = p.g;
tmp[pos++] = p.b;
tmp[pos++] = p.a;
}
p = p.next;
}
if (pos != 0) {
var stride = 10;
if (hasColor)
stride += 4;
if (buffer == null) {
buffer = h3d.Buffer.ofSubFloats(tmp, stride, Std.int(pos / stride), [Quads, Dynamic, RawFormat]);
bufferSize = Std.int(pos / stride);
} else {
var len = Std.int(pos / stride);
if (bufferSize < len) {
buffer.dispose();
buffer = h3d.Buffer.ofSubFloats(tmp, stride, Std.int(pos / stride), [Quads, Dynamic, RawFormat]);
bufferSize = Std.int(pos / stride);
} else {
buffer.uploadVector(tmp, 0, len);
}
}
if (pshader.is3D)
pshader.size.set(globalSize, globalSize);
else
pshader.size.set(globalSize * ctx.engine.height / ctx.engine.width * 4, globalSize * 4);
ctx.uploadParams();
var verts = Std.int(pos / stride);
var vertsPerTri = 2;
ctx.engine.renderQuadBuffer(buffer, 0, verts >> 1); // buffer, 0, Std.int(pos / stride));
}
}
override function onRemove() {
super.onRemove();
if (buffer != null) {
buffer.dispose();
buffer = null;
}
}
}