Object Occlusion Culling

This commit is contained in:
2026-10-15 02:41:03 -07:00
parent 3273054766
commit 612a3d0a09
33 changed files with 3629 additions and 84 deletions

View File

@ -18,6 +18,13 @@ import iron.object.LightObject;
import iron.object.MeshObject;
import iron.object.Uniforms;
import iron.object.Clipmap;
#if lnx_radix
import haxe.io.FPHelper;
import radix.Radix;
#end
#if lnx_occlusion_culling
import iron.culling.CullSystem;
#end
#if lnx_vr
import iron.math.Vec4;
import iron.math.Mat4;
@ -50,10 +57,6 @@ class RenderPath {
return vrSimulateMode;
}
// TODO: done remove safely
public static inline function debugLog(msg: String, once: Bool = true): Void {
return;
}
#end
public var frameScissor = false;
@ -81,6 +84,18 @@ class RenderPath {
var renderToXRFramebuffer = false;
#end
public var drawOrder = DrawOrder.Distance;
#if lnx_frustum_culling
public var frustumCulling = true;
#else
public var frustumCulling = false;
#end
#if lnx_occlusion_culling
public var occlusionCulling = true;
#else
public var occlusionCulling = false;
#end
public var paused = false;
public var ready(get, never): Bool;
function get_ready(): Bool { return loading == 0; }
@ -95,6 +110,8 @@ class RenderPath {
var lastH = 0;
var bindParams: Array<String>;
var meshesSorted: Bool;
var cullComputed: Bool;
var cullFlagsReset: Bool;
var scissorSet = false;
var viewportScaled = false;
var lastFrameTime = 0.0;
@ -304,6 +321,8 @@ class RenderPath {
currentD = 1;
currentFace = -1;
meshesSorted = false;
cullComputed = false;
cullFlagsReset = false;
sun = null;
for (l in Scene.active.lights) {
@ -470,6 +489,60 @@ class RenderPath {
rt.image.generateMipmaps(1000);
}
#if lnx_radix
static var sortDistanceKeys = new Array<Int>();
static var sortDistanceMeshesBuf = new Array<MeshObject>();
static var sortDistanceKeysBuf = new Array<Int>();
static var sortIndexKeys = new Array<Int>();
static var sortIndexMeshesBuf = new Array<MeshObject>();
static var sortIndexKeysBuf = new Array<Int>();
public static function sortMeshesDistance(meshes: Array<MeshObject>) {
var n = meshes.length;
var keys = sortDistanceKeys;
var meshesBuf = sortDistanceMeshesBuf;
var keysBuf = sortDistanceKeysBuf;
if (keys.length < n) {
keys.resize(n);
meshesBuf.resize(n);
keysBuf.resize(n);
}
for (i in 0...n) {
var m = meshes[i];
var key = FPHelper.floatToI32(m.cameraDistance) >>> 1;
#if rp_depth_texture
if (m.depthRead) key |= 0x80000000;
#end
keys[i] = key;
}
Radix.sortCopyKeyed(meshes, keys, meshesBuf, keysBuf);
if (meshesBuf.length > n) meshesBuf.resize(n);
}
public static function sortMeshesIndex(meshes: Array<MeshObject>) {
var n = meshes.length;
var keys = sortIndexKeys;
var meshesBuf = sortIndexMeshesBuf;
var keysBuf = sortIndexKeysBuf;
if (keys.length < n) {
keys.resize(n);
meshesBuf.resize(n);
keysBuf.resize(n);
}
for (i in 0...n) {
var m = meshes[i];
var si = m.data.sortingIndex;
var key = si < -0x40000000 ? 0 : si > 0x3fffffff ? 0x7fffffff : si + 0x40000000;
#if rp_depth_texture
if (m.depthRead) key |= 0x80000000;
#end
keys[i] = key;
}
Radix.sortCopyKeyed(meshes, keys, meshesBuf, keysBuf);
if (meshesBuf.length > n) meshesBuf.resize(n);
}
#else
static inline function boolToInt(b: Bool): Int {
return b ? 1 : 0;
}
@ -501,6 +574,7 @@ class RenderPath {
return a.data.name > b.data.name ? 1 : -1;
});
}
#end
public function drawMeshes(context: String) {
var isShadows = context == "shadowmap" || context == "shadowmap_transparent";
@ -519,6 +593,11 @@ class RenderPath {
for (i in 0...LightObject.cascadeCount) {
light.setCascade(Scene.active.camera, i);
currentG.viewport(i * step, 0, step, step);
#if lnx_occlusion_culling
if (occlusionCulling && CullSystem.active != null) {
CullSystem.active.cullShadow(light);
}
#end
submitDraw(context);
}
drawn = true;
@ -529,7 +608,14 @@ class RenderPath {
if (context == "mesh") LightObject.updateClusters(Scene.active.camera);
#end
if (!drawn) submitDraw(context);
if (!drawn) {
#if lnx_occlusion_culling
if (isShadows && occlusionCulling && CullSystem.active != null) {
CullSystem.active.cullShadow(light);
}
#end
submitDraw(context);
}
// Callbacks to specific context
if (contextEvents != null) {
@ -546,7 +632,34 @@ class RenderPath {
var meshes = Scene.active.meshes;
MeshObject.lastPipeline = null;
if (!meshesSorted && camera != null) { // Order max once per frame for now
#if lnx_occlusion_culling
if (!cullFlagsReset && !occlusionCulling) {
cullFlagsReset = true;
for (m in meshes) {
m.occlusionVisible = true;
m.shadowOcclusionVisible = true;
}
}
#end
if (!cullComputed && (context == "mesh" || context == "depth")) {
#if lnx_occlusion_culling
if (occlusionCulling) {
if (CullSystem.active == null) CullSystem.init();
if (CullSystem.active != null && camera != null) {
if (CullSystem.active.proxiesDirty) {
CullSystem.active.loadOccluderProxies();
CullSystem.active.proxiesDirty = false;
}
CullSystem.active.cull(camera);
}
}
#end
cullComputed = true;
}
var isShadowCtx = context == "shadowmap" || context == "shadowmap_transparent";
if (!meshesSorted && camera != null && !isShadowCtx) {
var camX = camera.transform.worldx();
var camY = camera.transform.worldy();
var camZ = camera.transform.worldz();
@ -571,8 +684,20 @@ class RenderPath {
static inline function meshRenderLoop(g: Graphics, context: String, _bindParams: Array<String>, _meshes: Array<MeshObject>) {
var isReadingDepth = false;
#if lnx_occlusion_culling
var cullSys = CullSystem.active;
#end
for (m in _meshes) {
#if lnx_occlusion_culling
if (cullSys != null && !m.occlusionVisible && (context == "mesh" || context == "depth" || context == "translucent")) {
continue;
}
if (cullSys != null && !m.shadowOcclusionVisible && (context == "shadowmap" || context == "shadowmap_transparent")) {
continue;
}
#end
#if rp_depth_texture
// First mesh that reads depth
if (!isReadingDepth && m.depthRead) {