374 lines
11 KiB
Haxe
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++;
|
|
}
|
|
}
|