Files
LNXSDK/leenkx/Sources/iron/culling/CullData.hx
2026-10-15 02:41:03 -07:00

374 lines
11 KiB
Haxe

package iron.culling;
import iron.object.MeshObject;
import iron.Scene;
import kha.arrays.Float32Array;
class CullData {
static inline var STRIDE = 16;
static inline var BASIS_STRIDE = 12;
public var data: Float32Array;
public var prevData: Float32Array;
public var uidData: kha.arrays.Int32Array;
public var prevUidData: kha.arrays.Int32Array;
var basisData: Float32Array;
var prevBasisData: Float32Array;
public var count: Int = 0;
public var prevCount: Int = 0;
public var anyMoved: Bool = false;
public var anyFlagsChanged: Bool = false;
public var orderChanged: Bool = false;
public var meshes: Array<MeshObject> = [];
public var slotsDirty: Bool = true;
public var slotsRebuilt: Bool = false;
public var visibleList: Array<Int>;
public var visibleCount: Int = 0;
var staticFlagsCache: haxe.ds.IntMap<Int> = new haxe.ds.IntMap<Int>();
var matTranspCache: haxe.ds.IntMap<Int> = new haxe.ds.IntMap<Int>();
var matSnapshots: haxe.ds.IntMap<haxe.ds.Vector<iron.data.MaterialData>> = new haxe.ds.IntMap();
var prevUidMap: haxe.ds.IntMap<Int> = new haxe.ds.IntMap<Int>();
public function new() {
visibleList = [];
}
public inline function stride(): Int {
return STRIDE;
}
public function ensureCapacity(cap: Int) {
if (data == null || data.length < cap * STRIDE) {
var newCap = cap;
if (data != null) newCap = Std.int(Math.max(cap, data.length / STRIDE * 2));
var oldData = data;
var oldPrev = prevData;
var oldUid = uidData;
var oldPrevUid = prevUidData;
var oldBasis = basisData;
var oldPrevBasis = prevBasisData;
data = new Float32Array(newCap * STRIDE);
prevData = new Float32Array(newCap * STRIDE);
uidData = new kha.arrays.Int32Array(newCap);
prevUidData = new kha.arrays.Int32Array(newCap);
basisData = new Float32Array(newCap * BASIS_STRIDE);
prevBasisData = new Float32Array(newCap * BASIS_STRIDE);
if (oldData != null) {
var copyLen = oldData.length;
for (i in 0...copyLen) data[i] = oldData[i];
}
if (oldPrev != null) {
var copyLen = oldPrev.length;
for (i in 0...copyLen) prevData[i] = oldPrev[i];
}
if (oldUid != null) {
var copyLen = oldUid.length;
for (i in 0...copyLen) uidData[i] = oldUid[i];
}
if (oldPrevUid != null) {
var copyLen = oldPrevUid.length;
for (i in 0...copyLen) prevUidData[i] = oldPrevUid[i];
}
if (oldBasis != null) {
var copyLen = oldBasis.length;
for (i in 0...copyLen) basisData[i] = oldBasis[i];
}
if (oldPrevBasis != null) {
var copyLen = oldPrevBasis.length;
for (i in 0...copyLen) prevBasisData[i] = oldPrevBasis[i];
}
}
}
public function syncFromScene() {
var sceneMeshes = Scene.active.meshes;
slotsRebuilt = false;
if (slotsDirty || meshes.length != sceneMeshes.length) {
meshes = sceneMeshes.copy();
slotsDirty = false;
slotsRebuilt = true;
staticFlagsCache.clear();
matTranspCache.clear();
matSnapshots.clear();
}
count = meshes.length;
ensureCapacity(count);
var tmpD = prevData; prevData = data; data = tmpD;
var tmpU = prevUidData; prevUidData = uidData; uidData = tmpU;
var tmpB = prevBasisData; prevBasisData = basisData; basisData = tmpB;
prevUidMap.clear();
var prevUidMapBuilt = false;
anyMoved = false;
anyFlagsChanged = false;
orderChanged = false;
for (i in 0...count) {
var mesh = meshes[i];
var t = mesh.transform;
var uid = mesh.uid;
var p = mesh.parent;
while (p != null) {
p.transform.update();
p = p.parent;
}
t.update();
var off = i * STRIDE;
var r = t.radius;
var wx = t.worldx();
var wy = t.worldy();
var wz = t.worldz();
var radiusScale = mesh.data.isSkinned ? 2.0 : 1.0;
#if lnx_gpu_particles
if (mesh.particleSystems != null || mesh.particleOwner != null) radiusScale *= 1000;
#end
if (mesh.data.geom.instanced) radiusScale *= 100;
r *= radiusScale;
data[off + 3] = r;
data[off + 4] = wx;
data[off + 5] = wy;
data[off + 6] = wz;
var w = t.world;
var prevIdx: Null<Int> = null;
if (prevUidData != null && i < prevCount && prevUidData[i] == uid) {
prevIdx = i;
} else {
if (!prevUidMapBuilt) {
prevUidMapBuilt = true;
if (prevUidData != null) {
for (pi in 0...prevCount) prevUidMap.set(prevUidData[pi], pi);
}
}
prevIdx = prevUidMap.get(uid);
}
if (prevIdx != null && prevIdx != i) orderChanged = true;
var aabbCached = false;
if (prevIdx != null) {
var poff = prevIdx * STRIDE;
var pb = prevIdx * BASIS_STRIDE;
if (prevData[poff + 4] == wx && prevData[poff + 5] == wy && prevData[poff + 6] == wz &&
prevBasisData[pb + 0] == w._00 && prevBasisData[pb + 1] == w._10 && prevBasisData[pb + 2] == w._20 &&
prevBasisData[pb + 3] == w._01 && prevBasisData[pb + 4] == w._11 && prevBasisData[pb + 5] == w._21 &&
prevBasisData[pb + 6] == w._02 && prevBasisData[pb + 7] == w._12 && prevBasisData[pb + 8] == w._22 &&
prevBasisData[pb + 9] == t.dim.x && prevBasisData[pb + 10] == t.dim.y && prevBasisData[pb + 11] == t.dim.z) {
data[off + 0] = prevData[poff + 0];
data[off + 1] = prevData[poff + 1];
data[off + 2] = prevData[poff + 2];
data[off + 8] = prevData[poff + 8];
data[off + 9] = prevData[poff + 9];
data[off + 10] = prevData[poff + 10];
aabbCached = true;
}
}
if (!aabbCached) {
var r00 = w._00, r10 = w._10, r20 = w._20;
var r01 = w._01, r11 = w._11, r21 = w._21;
var r02 = w._02, r12 = w._12, r22 = w._22;
var lenX = Math.sqrt(r00 * r00 + r01 * r01 + r02 * r02);
var lenY = Math.sqrt(r10 * r10 + r11 * r11 + r12 * r12);
var lenZ = Math.sqrt(r20 * r20 + r21 * r21 + r22 * r22);
if (lenX > 1e-8) { r00 /= lenX; r01 /= lenX; r02 /= lenX; }
if (lenY > 1e-8) { r10 /= lenY; r11 /= lenY; r12 /= lenY; }
if (lenZ > 1e-8) { r20 /= lenZ; r21 /= lenZ; r22 /= lenZ; }
var hx = t.dim.x * 0.5 * radiusScale;
var hy = t.dim.y * 0.5 * radiusScale;
var hz = t.dim.z * 0.5 * radiusScale;
var aabbMinX = 1e20, aabbMinY = 1e20, aabbMinZ = 1e20;
var aabbMaxX = -1e20, aabbMaxY = -1e20, aabbMaxZ = -1e20;
for (c in 0...8) {
var cx = (c & 1) != 0 ? hx : -hx;
var cy = (c & 2) != 0 ? hy : -hy;
var cz = (c & 4) != 0 ? hz : -hz;
var px = cx * r00 + cy * r10 + cz * r20 + wx;
var py = cx * r01 + cy * r11 + cz * r21 + wy;
var pz = cx * r02 + cy * r12 + cz * r22 + wz;
if (px < aabbMinX) aabbMinX = px;
if (py < aabbMinY) aabbMinY = py;
if (pz < aabbMinZ) aabbMinZ = pz;
if (px > aabbMaxX) aabbMaxX = px;
if (py > aabbMaxY) aabbMaxY = py;
if (pz > aabbMaxZ) aabbMaxZ = pz;
}
data[off + 0] = aabbMinX;
data[off + 1] = aabbMinY;
data[off + 2] = aabbMinZ;
data[off + 8] = aabbMaxX;
data[off + 9] = aabbMaxY;
data[off + 10] = aabbMaxZ;
}
var flags: Int;
var cachedFlags = staticFlagsCache.get(uid);
if (cachedFlags != null) {
flags = cachedFlags;
} else {
flags = 0;
if (mesh.raw != null && mesh.raw.properties != null) {
for (prop in mesh.raw.properties) {
if (prop.name == "occluder" && prop.value == true) {
flags |= 1;
break;
}
}
}
if ((flags & 1) == 0 && mesh.data != null &&
mesh.data.raw != null && mesh.data.raw.occluder_proxy != null) {
flags |= 1;
}
if (mesh.raw != null && mesh.raw.visible_shadow != false) flags |= 4;
staticFlagsCache.set(uid, flags);
}
var mats = mesh.materials;
var snap = matSnapshots.get(uid);
var matsChanged = snap == null || mats == null || snap.length != mats.length;
if (!matsChanged) {
for (mi in 0...mats.length) {
if (snap[mi] != mats[mi]) { matsChanged = true; break; }
}
}
if (matsChanged) {
var tbit = 0;
if (mats != null) {
for (mi in 0...mats.length) {
var mat = mats[mi];
if (mat == null || mat.shader == null) continue;
for (sc in mat.shader.contexts) {
var sr = sc.raw;
if (sr != null && sr.name != null && sr.name.substr(0, 4) == "mesh" &&
(sr.depth_write != true || sr.blend_source != null)) {
tbit = 32;
break;
}
}
if (tbit != 0) break;
}
}
matTranspCache.set(uid, tbit);
var nsnap = new haxe.ds.Vector<iron.data.MaterialData>(mats != null ? mats.length : 0);
for (mi in 0...nsnap.length) nsnap[mi] = mats[mi];
matSnapshots.set(uid, nsnap);
}
var tbitCached = matTranspCache.get(uid);
if (tbitCached != null) flags |= tbitCached;
if (mesh.visible) flags |= 8;
if (radiusScale != 1.0) flags |= 16;
if (prevIdx != null) {
var poff = prevIdx * STRIDE;
if (prevData[poff + 0] != data[off + 0] ||
prevData[poff + 1] != data[off + 1] ||
prevData[poff + 2] != data[off + 2] ||
prevData[poff + 8] != data[off + 8] ||
prevData[poff + 9] != data[off + 9] ||
prevData[poff + 10] != data[off + 10]) {
flags |= 2;
anyMoved = true;
}
} else {
flags |= 2;
anyMoved = true;
anyFlagsChanged = true;
}
if (prevIdx != null) {
var poff = prevIdx * STRIDE;
if (((flags ^ Std.int(prevData[poff + 7])) & ~2) != 0) anyFlagsChanged = true;
}
data[off + 7] = flags;
var boff = i * BASIS_STRIDE;
basisData[boff + 0] = w._00;
basisData[boff + 1] = w._10;
basisData[boff + 2] = w._20;
basisData[boff + 3] = w._01;
basisData[boff + 4] = w._11;
basisData[boff + 5] = w._21;
basisData[boff + 6] = w._02;
basisData[boff + 7] = w._12;
basisData[boff + 8] = w._22;
basisData[boff + 9] = t.dim.x;
basisData[boff + 10] = t.dim.y;
basisData[boff + 11] = t.dim.z;
uidData[i] = uid;
}
prevCount = count;
}
public inline function getMinX(i: Int): Float {
return data[i * STRIDE + 0];
}
public inline function getMinY(i: Int): Float {
return data[i * STRIDE + 1];
}
public inline function getMinZ(i: Int): Float {
return data[i * STRIDE + 2];
}
public inline function getRadius(i: Int): Float {
return data[i * STRIDE + 3];
}
public inline function getCenterX(i: Int): Float {
return data[i * STRIDE + 4];
}
public inline function getCenterY(i: Int): Float {
return data[i * STRIDE + 5];
}
public inline function getCenterZ(i: Int): Float {
return data[i * STRIDE + 6];
}
public inline function getFlags(i: Int): Int {
return Std.int(data[i * STRIDE + 7]);
}
public inline function isOccluder(i: Int): Bool {
return (getFlags(i) & 1) != 0;
}
public inline function isDynamic(i: Int): Bool {
return (getFlags(i) & 2) != 0;
}
public inline function isShadowCaster(i: Int): Bool {
return (getFlags(i) & 4) != 0;
}
public inline function isMeshVisible(i: Int): Bool {
return (getFlags(i) & 8) != 0;
}
public inline function getObjectID(i: Int): Int {
return uidData[i];
}
public inline function getMaxX(i: Int): Float {
return data[i * STRIDE + 8];
}
public inline function getMaxY(i: Int): Float {
return data[i * STRIDE + 9];
}
public inline function getMaxZ(i: Int): Float {
return data[i * STRIDE + 10];
}
public function clearVisible() {
visibleCount = 0;
if (visibleList.length > 0) visibleList.resize(0);
}
public function addVisible(objectIndex: Int) {
visibleList.push(objectIndex);
visibleCount++;
}
}