package iron.culling; class HiZPyramid { public var width: Int; public var height: Int; public var mipLevels: Int; public var mipBuffers: Array; public var mipWidths: Array; public var mipHeights: Array; public var depthEps: Float; var mipShifts: Array; public function new(width: Int, height: Int) { this.width = width; this.height = height; mipBuffers = []; mipWidths = []; mipHeights = []; depthEps = CullSystem.depthEps; var w = width; var h = height; mipLevels = 0; while (w > 1 || h > 1) { mipWidths.push(w); mipHeights.push(h); mipBuffers.push(new kha.arrays.Float32Array(w * h)); mipLevels++; w = (w + 1) >> 1; h = (h + 1) >> 1; } mipWidths.push(1); mipHeights.push(1); mipBuffers.push(new kha.arrays.Float32Array(1)); mipLevels++; mipShifts = [0, 1, 2, 3, 4, 5, 6]; while (mipShifts.length < mipLevels) mipShifts.push(mipShifts.length); } public function clear(farDepth: Float = 1e20) { var buf = mipBuffers[0]; for (i in 0...buf.length) buf[i] = farDepth; } public inline function writeDepth(x: Int, y: Int, depth: Float) { var idx = y * width + x; if (x >= 0 && x < width && y >= 0 && y < height) { if (depth < mipBuffers[0][idx]) { mipBuffers[0][idx] = depth; } } } public function buildPyramid() { for (mip in 1...mipLevels) { var prev = mipBuffers[mip - 1]; var cur = mipBuffers[mip]; var pw = mipWidths[mip - 1]; var ph = mipHeights[mip - 1]; var cw = mipWidths[mip]; var ch = mipHeights[mip]; for (y in 0...ch) { for (x in 0...cw) { var sx = x * 2; var sy = y * 2; var d00 = prev[sy * pw + sx]; var d10 = (sx + 1 < pw) ? prev[sy * pw + sx + 1] : d00; var d01 = (sy + 1 < ph) ? prev[(sy + 1) * pw + sx] : d00; var d11 = (sx + 1 < pw && sy + 1 < ph) ? prev[(sy + 1) * pw + sx + 1] : d00; var maxD = d00; if (d10 > maxD) maxD = d10; if (d01 > maxD) maxD = d01; if (d11 > maxD) maxD = d11; cur[y * cw + x] = maxD; } } } } public inline function sample(x: Int, y: Int, mip: Int): Float { var mw = mipWidths[mip]; var mh = mipHeights[mip]; if (x < 0) x = 0; if (y < 0) y = 0; if (x >= mw) x = mw - 1; if (y >= mh) y = mh - 1; return mipBuffers[mip][y * mw + x]; } public static function mipForSize(rectW: Float, rectH: Float, maxMip: Int): Int { var maxDim = Math.max(rectW, rectH); if (maxDim <= 2.0) return 0; var mip = Std.int(Math.floor(Math.log(maxDim) / Math.log(2))) - 1; if (mip < 0) mip = 0; if (mip > maxMip) mip = maxMip; return mip; } public function isOccluded( minX: Float, minY: Float, minZ: Float, maxX: Float, maxY: Float, maxZ: Float, vp: iron.math.Mat4, _screenW: Int, _screenH: Int ): Bool { var minSx = 1e20, minSy = 1e20; var maxSx = -1e20, maxSy = -1e20; var minDepth = 1e20; var m00 = vp._00, m10 = vp._10, m20 = vp._20, m30 = vp._30; var m01 = vp._01, m11 = vp._11, m21 = vp._21, m31 = vp._31; var m02 = vp._02, m12 = vp._12, m22 = vp._22, m32 = vp._32; var m03 = vp._03, m13 = vp._13, m23 = vp._23, m33 = vp._33; for (c in 0...8) { var x = (c & 1) != 0 ? maxX : minX; var y = (c & 2) != 0 ? maxY : minY; var z = (c & 4) != 0 ? maxZ : minZ; var w = m03 * x + m13 * y + m23 * z + m33; if (w <= 0) return false; var invW = SimdOps.reciprocal(w); var ndcZ = (m02 * x + m12 * y + m22 * z + m32) * invW; if (ndcZ < -1.0) ndcZ = -1.0; else if (ndcZ > 1.0) ndcZ = 1.0; var sx = ((m00 * x + m10 * y + m20 * z + m30) * invW * 0.5 + 0.5) * width; var sy = ((m01 * x + m11 * y + m21 * z + m31) * invW * 0.5 + 0.5) * height; if (sx < minSx) minSx = sx; if (sy < minSy) minSy = sy; if (sx > maxSx) maxSx = sx; if (sy > maxSy) maxSy = sy; if (ndcZ < minDepth) minDepth = ndcZ; } var ix0 = Std.int(Math.max(0, minSx)); var iy0 = Std.int(Math.max(0, minSy)); var ix1 = Std.int(Math.min(width - 1, maxSx)); var iy1 = Std.int(Math.min(height - 1, maxSy)); if (ix0 > ix1 || iy0 > iy1) return false; if (ix0 > 0) ix0--; if (iy0 > 0) iy0--; if (ix1 < width - 1) ix1++; if (iy1 < height - 1) iy1++; var rectW = ix1 - ix0 + 1; var rectH = iy1 - iy0 + 1; var mip = mipForSize(rectW, rectH, mipLevels - 1); var mw = mipWidths[mip]; var mh = mipHeights[mip]; var shift = mipShifts[mip]; var tx0 = Std.int(Math.min(mw - 1, ix0 >> shift)); var ty0 = Std.int(Math.min(mh - 1, iy0 >> shift)); var tx1 = Std.int(Math.min(mw - 1, ix1 >> shift)); var ty1 = Std.int(Math.min(mh - 1, iy1 >> shift)); var buf = mipBuffers[mip]; var limit = minDepth - depthEps; for (ty in ty0...ty1 + 1) { var rowOff = ty * mw; for (tx in tx0...tx1 + 1) { if (buf[rowOff + tx] >= limit) return false; } } return true; } }