forked from LeenkxTeam/LNXSDK
Object Occlusion Culling
This commit is contained in:
@ -62,7 +62,20 @@ class CameraObject extends Object {
|
||||
|
||||
public function buildProjection(screenAspect: Null<Float> = null) {
|
||||
if (data.raw.ortho != null) {
|
||||
P = Mat4.ortho(data.raw.ortho[0], data.raw.ortho[1], data.raw.ortho[2], data.raw.ortho[3], data.raw.near_plane, data.raw.far_plane);
|
||||
var left = data.raw.ortho[0];
|
||||
var right = data.raw.ortho[1];
|
||||
var bottom = data.raw.ortho[2];
|
||||
var top = data.raw.ortho[3];
|
||||
if (data.raw.aspect != null) {
|
||||
var aspect: kha.FastFloat = data.raw.aspect;
|
||||
if (screenAspect == null) screenAspect = iron.App.w() / iron.App.h();
|
||||
var scale = aspect / screenAspect;
|
||||
var cx = (left + right) * 0.5;
|
||||
var hw = (right - left) * 0.5 * scale;
|
||||
left = cx - hw;
|
||||
right = cx + hw;
|
||||
}
|
||||
P = Mat4.ortho(left, right, bottom, top, data.raw.near_plane, data.raw.far_plane);
|
||||
}
|
||||
else {
|
||||
if (screenAspect == null) screenAspect = iron.App.w() / iron.App.h();
|
||||
@ -247,25 +260,3 @@ class CameraObject extends Object {
|
||||
return new Vec4(-transform.world._20, -transform.world._21, -transform.world._22);
|
||||
}
|
||||
}
|
||||
|
||||
class FrustumPlane {
|
||||
public var normal = new Vec4(1.0, 0.0, 0.0);
|
||||
public var constant = 0.0;
|
||||
|
||||
public function new() {}
|
||||
|
||||
public function normalize() {
|
||||
var inverseNormalLength = 1.0 / normal.length();
|
||||
normal.mult(inverseNormalLength);
|
||||
constant *= inverseNormalLength;
|
||||
}
|
||||
|
||||
public function distanceToSphere(sphereCenter: Vec4, sphereRadius: Float): Float {
|
||||
return (normal.dot(sphereCenter) + constant) - sphereRadius;
|
||||
}
|
||||
|
||||
public inline function setComponents(x: Float, y: Float, z: Float, w: Float) {
|
||||
normal.set(x, y, z);
|
||||
constant = w;
|
||||
}
|
||||
}
|
||||
|
||||
25
leenkx/Sources/iron/object/FrustumPlane.hx
Normal file
25
leenkx/Sources/iron/object/FrustumPlane.hx
Normal file
@ -0,0 +1,25 @@
|
||||
package iron.object;
|
||||
|
||||
import iron.math.Vec4;
|
||||
|
||||
class FrustumPlane {
|
||||
public var normal = new Vec4(1.0, 0.0, 0.0);
|
||||
public var constant = 0.0;
|
||||
|
||||
public function new() {}
|
||||
|
||||
public function normalize() {
|
||||
var inverseNormalLength = 1.0 / normal.length();
|
||||
normal.mult(inverseNormalLength);
|
||||
constant *= inverseNormalLength;
|
||||
}
|
||||
|
||||
public function distanceToSphere(sphereCenter: Vec4, sphereRadius: Float): Float {
|
||||
return (normal.dot(sphereCenter) + constant) - sphereRadius;
|
||||
}
|
||||
|
||||
public inline function setComponents(x: Float, y: Float, z: Float, w: Float) {
|
||||
normal.set(x, y, z);
|
||||
constant = w;
|
||||
}
|
||||
}
|
||||
@ -34,7 +34,7 @@ class LightObject extends Object {
|
||||
// Cascades
|
||||
public static var cascadeCount = 1;
|
||||
public static var cascadeSplitFactor = 0.8;
|
||||
public static var cascadeBounds = 1.0;
|
||||
public static var cascadeBounds = 2.0; // occlusion culling shadows pop with 1.0
|
||||
#if lnx_csm
|
||||
var cascadeData: Float32Array = null;
|
||||
var cascadeVP: Array<Mat4>;
|
||||
@ -140,6 +140,9 @@ class LightObject extends Object {
|
||||
}
|
||||
#end
|
||||
#end
|
||||
#if lnx_occlusion_culling
|
||||
if (iron.culling.CullSystem.active != null) iron.culling.CullSystem.active.removeLight(this);
|
||||
#end
|
||||
super.remove();
|
||||
}
|
||||
|
||||
@ -278,12 +281,13 @@ class LightObject extends Object {
|
||||
var worldPerTexelX = (maxx - minx) / smsize;
|
||||
var worldPerTexelY = (maxy - miny) / smsize;
|
||||
var worldPerTexelZ = (maxz - minz) / smsize;
|
||||
var ex = maxx - minx, ey = maxy - miny, ez = maxz - minz;
|
||||
minx = Math.floor(minx / worldPerTexelX) * worldPerTexelX;
|
||||
miny = Math.floor(miny / worldPerTexelY) * worldPerTexelY;
|
||||
minz = Math.floor(minz / worldPerTexelZ) * worldPerTexelZ;
|
||||
maxx = Math.floor(maxx / worldPerTexelX) * worldPerTexelX;
|
||||
maxy = Math.floor(maxy / worldPerTexelY) * worldPerTexelY;
|
||||
maxz = Math.floor(maxz / worldPerTexelZ) * worldPerTexelZ;
|
||||
maxx = minx + ex;
|
||||
maxy = miny + ey;
|
||||
maxz = minz + ez;
|
||||
|
||||
var hx = (maxx - minx) / 2;
|
||||
var hy = (maxy - miny) / 2;
|
||||
|
||||
@ -30,9 +30,21 @@ class MeshObject extends Object {
|
||||
public var frustumCulling = true;
|
||||
public var activeTilesheet: Tilesheet = null;
|
||||
public var tilesheets: Array<Tilesheet> = null;
|
||||
#if lnx_occlusion_culling
|
||||
public var occlusionVisible = true;
|
||||
public var shadowOcclusionVisible = true;
|
||||
#end
|
||||
var shadowCameraCulled: Bool = false;
|
||||
var shadowCameraCullFrame: Int = -1;
|
||||
var shadowCameraCullCamera: CameraObject = null;
|
||||
var shadowCameraCullLight: LightObject = null;
|
||||
public var skip_context: String = null; // Do not draw this context
|
||||
public var force_context: String = null; // Draw only this context
|
||||
static var lastPipeline: PipelineState = null;
|
||||
|
||||
var cachedMats: Map<String, Vector<MaterialData>> = null;
|
||||
var cachedMaterialContexts: Map<String, Array<MaterialContext>> = null;
|
||||
var cachedShaderContexts: Map<String, Array<ShaderContext>> = null;
|
||||
#if lnx_morph_target
|
||||
public var morphTarget: MorphTarget = null;
|
||||
#end
|
||||
@ -48,6 +60,9 @@ class MeshObject extends Object {
|
||||
this.materials = materials;
|
||||
setData(data);
|
||||
Scene.active.meshes.push(this);
|
||||
#if lnx_occlusion_culling
|
||||
if (iron.culling.CullSystem.active != null) iron.culling.CullSystem.active.markDirty();
|
||||
#end
|
||||
}
|
||||
|
||||
public function setData(data: MeshData) {
|
||||
@ -96,6 +111,9 @@ class MeshObject extends Object {
|
||||
if (activeTilesheet != null) activeTilesheet.remove();
|
||||
if (tilesheets != null) { for (ts in tilesheets) { ts.remove(); } tilesheets = null; }
|
||||
if (Scene.active != null) Scene.active.meshes.remove(this);
|
||||
#if lnx_occlusion_culling
|
||||
if (iron.culling.CullSystem.active != null) iron.culling.CullSystem.active.markDirty();
|
||||
#end
|
||||
#if (rp_renderer == "Deferred")
|
||||
if (Scene.active != null) Scene.active.markMaterialParamsDirty();
|
||||
#end
|
||||
@ -196,7 +214,7 @@ class MeshObject extends Object {
|
||||
var isShadow = context == "shadowmap";
|
||||
if (camera == null) return false;
|
||||
|
||||
if (camera.data.raw.frustum_culling && frustumCulling) {
|
||||
if (camera.data.raw.frustum_culling && frustumCulling && RenderPath.active != null && RenderPath.active.frustumCulling) {
|
||||
// Scale radius for skinned mesh and particle system
|
||||
// TODO: define skin & particle bounds
|
||||
var radiusScale = data.isSkinned ? 2.0 : 1.0;
|
||||
@ -211,7 +229,8 @@ class MeshObject extends Object {
|
||||
var frustumPlanes = isShadow ? light.frustumPlanes : camera.frustumPlanes;
|
||||
|
||||
if (isShadow && light.data.raw.type != "sun") { // Non-sun light bounds intersect camera frustum
|
||||
light.transform.radius = light.data.raw.far_plane;
|
||||
var farPlane: kha.FastFloat = light.data.raw.far_plane;
|
||||
light.transform.radius = farPlane * 1.25;
|
||||
if (!CameraObject.sphereInFrustum(camera.frustumPlanes, light.transform)) {
|
||||
return setCulled(isShadow, true);
|
||||
}
|
||||
@ -220,6 +239,37 @@ class MeshObject extends Object {
|
||||
if (!CameraObject.sphereInFrustum(frustumPlanes, transform, radiusScale)) {
|
||||
return setCulled(isShadow, true);
|
||||
}
|
||||
|
||||
if (isShadow && light.data.raw.type == "sun") {
|
||||
var frame = RenderPath.active.frame;
|
||||
if (shadowCameraCullFrame == frame && shadowCameraCullCamera == camera &&
|
||||
shadowCameraCullLight == light) {
|
||||
if (shadowCameraCulled) return setCulled(isShadow, true);
|
||||
} else {
|
||||
shadowCameraCullFrame = frame;
|
||||
shadowCameraCullCamera = camera;
|
||||
shadowCameraCullLight = light;
|
||||
shadowCameraCulled = false;
|
||||
var ldx = -light.V._02;
|
||||
var ldy = -light.V._12;
|
||||
var ldz = -light.V._22;
|
||||
var radius = transform.radius * radiusScale;
|
||||
var cx = transform.worldx();
|
||||
var cy = transform.worldy();
|
||||
var cz = transform.worldz();
|
||||
var range = camera.data.raw.far_plane * (LightObject.cascadeBounds * 4.0);
|
||||
var margin = radius * 0.1;
|
||||
for (plane in camera.frustumPlanes) {
|
||||
var reach = plane.normal.x * cx + plane.normal.y * cy + plane.normal.z * cz + plane.constant + radius + margin;
|
||||
var dot = plane.normal.x * ldx + plane.normal.y * ldy + plane.normal.z * ldz;
|
||||
if (dot > 0) reach += dot * range;
|
||||
if (reach < 0) {
|
||||
shadowCameraCulled = true;
|
||||
return setCulled(isShadow, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//culled = false;
|
||||
@ -253,6 +303,12 @@ class MeshObject extends Object {
|
||||
}
|
||||
}
|
||||
|
||||
static function matsEqual(a: Vector<MaterialData>, b: Vector<MaterialData>): Bool {
|
||||
if (a == null || b == null || a.length != b.length) return false;
|
||||
for (i in 0...a.length) if (a[i] != b[i]) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
public function render(g: Graphics, context: String, bindParams: Array<String>) {
|
||||
if (data == null || !data.geom.ready) return; // Data not yet streamed
|
||||
if (!visible) return; // Skip render if object is hidden
|
||||
@ -293,6 +349,7 @@ class MeshObject extends Object {
|
||||
// Get lod
|
||||
var mats = materials;
|
||||
var lod = this;
|
||||
var isShadowCtx = context == "shadowmap" || context == "shadowmap_transparent";
|
||||
if (raw != null && raw.lods != null && raw.lods.length > 0) {
|
||||
computeScreenSize(Scene.active.camera);
|
||||
initLods();
|
||||
@ -317,13 +374,29 @@ class MeshObject extends Object {
|
||||
if (isLodMaterial() && !validContext(mats, context)) return;
|
||||
|
||||
// Get context
|
||||
var materialContexts: Array<MaterialContext> = [];
|
||||
var shaderContexts: Array<ShaderContext> = [];
|
||||
getContexts(context, mats, materialContexts, shaderContexts);
|
||||
var materialContexts: Array<MaterialContext> = null;
|
||||
var shaderContexts: Array<ShaderContext> = null;
|
||||
if (cachedMaterialContexts != null && matsEqual(cachedMats.get(context), mats)) {
|
||||
materialContexts = cachedMaterialContexts.get(context);
|
||||
shaderContexts = cachedShaderContexts.get(context);
|
||||
}
|
||||
if (materialContexts == null) {
|
||||
materialContexts = [];
|
||||
shaderContexts = [];
|
||||
getContexts(context, mats, materialContexts, shaderContexts);
|
||||
if (cachedMaterialContexts == null) {
|
||||
cachedMats = new Map();
|
||||
cachedMaterialContexts = new Map();
|
||||
cachedShaderContexts = new Map();
|
||||
}
|
||||
cachedMats.set(context, mats);
|
||||
cachedMaterialContexts.set(context, materialContexts);
|
||||
cachedShaderContexts.set(context, shaderContexts);
|
||||
}
|
||||
|
||||
Uniforms.posUnpack = data.scalePos;
|
||||
Uniforms.texUnpack = data.scaleTex;
|
||||
transform.update();
|
||||
if (!isShadowCtx) transform.update();
|
||||
|
||||
// Render mesh
|
||||
var ldata = lod.data;
|
||||
|
||||
Reference in New Issue
Block a user