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

@ -0,0 +1,350 @@
package radix;
import haxe.ds.Vector;
import haxe.io.FPHelper;
/**
In-place MSD radix sort - Haxe port of Malte Skarupke's ska_sort
(ref/ska_sort.hpp, Boost Software License 1.0). Original:
https://github.com/skarupke/ska_sort
**/
class Radix {
static inline var STD_SORT_THRESHOLD = 128;
static inline var AMERICAN_FLAG_SORT_THRESHOLD = 1024;
public static inline function intToUnsigned(i:Int):Int {
return i ^ 0x80000000;
}
public static inline function floatToUnsigned(f:Float):Int {
var u = FPHelper.floatToI32(f);
var signBit = u >> 31;
return u ^ (signBit | 0x80000000);
}
public static inline function uLess(a:Int, b:Int):Bool {
return (a ^ 0x80000000) < (b ^ 0x80000000);
}
public static function sort<T>(a:Array<T>, extractKey:T->Int, ?keys:Array<Int>):Void {
if (keys == null) keys = new Array<Int>();
for (i in 0...a.length) keys[i] = extractKey(a[i]);
sortKeyed(a, keys);
}
public static function sortFloat<T>(a:Array<T>, extractKey:T->Float, ?keys:Array<Int>):Void {
if (keys == null) keys = new Array<Int>();
for (i in 0...a.length) keys[i] = floatToUnsigned(extractKey(a[i]));
sortKeyed(a, keys);
}
public static function sortKeyed<T>(a:Array<T>, keys:Array<Int>):Void {
var n = a.length;
if (stdSortIfLessThanThreshold(a, keys, 0, n)) return;
msdSort(a, keys, 0, n, 24, new RadixScratch());
}
static function msdSort<T>(a:Array<T>, keys:Array<Int>, begin:Int, end:Int, shift:Int, scratch:RadixScratch):Void {
var numElements = end - begin;
if (numElements < AMERICAN_FLAG_SORT_THRESHOLD)
americanFlagSort(a, keys, begin, end, shift, scratch);
else
skaByteSort(a, keys, begin, end, shift, scratch);
}
static function stdSortIfLessThanThreshold<T>(a:Array<T>, keys:Array<Int>, begin:Int, end:Int):Bool {
var numElements = end - begin;
if (numElements <= 1) return true;
if (numElements >= STD_SORT_THRESHOLD) return false;
insertionSort(a, keys, begin, end);
return true;
}
static function insertionSort<T>(a:Array<T>, keys:Array<Int>, begin:Int, end:Int):Void {
var i = begin + 1;
while (i < end) {
var key = keys[i];
var v = a[i];
var j = i - 1;
while (j >= begin && uLess(key, keys[j])) {
keys[j + 1] = keys[j];
a[j + 1] = a[j];
j--;
}
keys[j + 1] = key;
a[j + 1] = v;
i++;
}
}
static function americanFlagSort<T>(a:Array<T>, keys:Array<Int>, begin:Int, end:Int, shift:Int, scratch:RadixScratch):Void {
var frame = scratch.frame(3 - (shift >> 3));
var offset = frame.offset;
var nextOffset = frame.nextOffset;
var remaining = frame.remaining;
zeroTable(offset);
for (i in begin...end) {
offset[(keys[i] >>> shift) & 0xff]++;
}
var numPartitions = 0;
var total = 0;
for (i in 0...256) {
var count = offset[i];
if (count == 0) continue;
offset[i] = total;
total += count;
nextOffset[i] = total;
remaining[numPartitions] = i;
numPartitions++;
}
if (numPartitions > 1) {
var curIdx = 0;
var cur = remaining[0];
var lastIdx = numPartitions - 1;
var it = begin;
var blockEnd = begin + nextOffset[cur];
var lastElement = end - 1;
var done = false;
while (true) {
var block = (keys[it] >>> shift) & 0xff;
if (block == cur) {
it++;
if (it == lastElement) break;
if (it == blockEnd) {
while (true) {
curIdx++;
if (curIdx == lastIdx) {
done = true;
break;
}
cur = remaining[curIdx];
if (offset[cur] != nextOffset[cur]) break;
}
if (done) break;
it = begin + offset[cur];
blockEnd = begin + nextOffset[cur];
}
}
else {
var t = offset[block];
offset[block] = t + 1;
swapAt(a, keys, it, begin + t);
}
}
}
if (shift > 0) {
var startOffset = 0;
var partitionBegin = begin;
for (i in 0...numPartitions) {
var endOffset = nextOffset[remaining[i]];
var partitionEnd = begin + endOffset;
if (!stdSortIfLessThanThreshold(a, keys, partitionBegin, partitionEnd)) {
msdSort(a, keys, partitionBegin, partitionEnd, shift - 8, scratch);
}
startOffset = endOffset;
partitionBegin = partitionEnd;
}
}
}
static function skaByteSort<T>(a:Array<T>, keys:Array<Int>, begin:Int, end:Int, shift:Int, scratch:RadixScratch):Void {
var frame = scratch.frame(3 - (shift >> 3));
var offset = frame.offset;
var nextOffset = frame.nextOffset;
var remaining = frame.remaining;
zeroTable(offset);
for (i in begin...end) {
offset[(keys[i] >>> shift) & 0xff]++;
}
var numPartitions = 0;
var total = 0;
for (i in 0...256) {
var count = offset[i];
if (count != 0) {
offset[i] = total;
total += count;
remaining[numPartitions] = i;
numPartitions++;
}
nextOffset[i] = total;
}
var lastRemaining = numPartitions;
while (lastRemaining > 1) {
var b = 0;
while (b < lastRemaining && processPending(a, keys, offset, nextOffset, remaining[b], begin, shift)) b++;
var it = b + 1;
while (it < lastRemaining) {
if (processPending(a, keys, offset, nextOffset, remaining[it], begin, shift)) {
var t = remaining[b];
remaining[b] = remaining[it];
remaining[it] = t;
b++;
}
it++;
}
lastRemaining = b;
}
if (shift > 0) {
var i = numPartitions;
while (i > 0) {
i--;
var partition = remaining[i];
var start = partition == 0 ? 0 : nextOffset[partition - 1];
var endOff = nextOffset[partition];
if (!stdSortIfLessThanThreshold(a, keys, begin + start, begin + endOff)) {
msdSort(a, keys, begin + start, begin + endOff, shift - 8, scratch);
}
}
}
}
static inline function newTable():Vector<Int> {
var v = new Vector<Int>(256);
#if !(cpp || hl || java || cs || flash)
for (i in 0...256) v[i] = 0;
#end
return v;
}
static function processPending<T>(a:Array<T>, keys:Array<Int>, offset:Vector<Int>, nextOffset:Vector<Int>, partition:Int, begin:Int, shift:Int):Bool {
var endOff = nextOffset[partition];
if (offset[partition] == endOff) return false;
var it = begin + offset[partition];
var count = endOff - offset[partition];
while (count >= 4) {
placeAt(a, keys, offset, it, begin, shift); it++;
placeAt(a, keys, offset, it, begin, shift); it++;
placeAt(a, keys, offset, it, begin, shift); it++;
placeAt(a, keys, offset, it, begin, shift); it++;
count -= 4;
}
while (count > 0) {
placeAt(a, keys, offset, it, begin, shift);
it++;
count--;
}
return offset[partition] != endOff;
}
static inline function placeAt<T>(a:Array<T>, keys:Array<Int>, offset:Vector<Int>, it:Int, begin:Int, shift:Int):Void {
var p = (keys[it] >>> shift) & 0xff;
var t = offset[p];
offset[p] = t + 1;
swapAt(a, keys, it, begin + t);
}
static inline function zeroTable(v:Vector<Int>):Void {
for (i in 0...256) v[i] = 0;
}
static inline function swapAt<T>(a:Array<T>, keys:Array<Int>, i:Int, j:Int):Void {
var ta = a[i];
a[i] = a[j];
a[j] = ta;
var tk = keys[i];
keys[i] = keys[j];
keys[j] = tk;
}
public static function sortCopy<T>(a:Array<T>, buffer:Array<T>, extractKey:T->Int):Bool {
var numElements = a.length;
if (numElements < STD_SORT_THRESHOLD) {
sort(a, extractKey);
return false;
}
var keys = new Array<Int>();
var bufKeys = new Array<Int>();
for (i in 0...numElements) keys[i] = extractKey(a[i]);
sortCopyKeyed(a, keys, buffer, bufKeys);
return false;
}
public static function sortCopyKeyed<T>(a:Array<T>, keys:Array<Int>, buffer:Array<T>, bufKeys:Array<Int>):Void {
var n = a.length;
var counts0 = newTable();
var counts1 = newTable();
var counts2 = newTable();
var counts3 = newTable();
for (i in 0...n) {
var key = keys[i];
counts0[key & 0xff]++;
counts1[(key >>> 8) & 0xff]++;
counts2[(key >>> 16) & 0xff]++;
counts3[(key >>> 24) & 0xff]++;
}
var total0 = 0, total1 = 0, total2 = 0, total3 = 0;
for (i in 0...256) {
var old0 = counts0[i];
var old1 = counts1[i];
var old2 = counts2[i];
var old3 = counts3[i];
counts0[i] = total0;
counts1[i] = total1;
counts2[i] = total2;
counts3[i] = total3;
total0 += old0;
total1 += old1;
total2 += old2;
total3 += old3;
}
for (i in 0...n) {
var b = keys[i] & 0xff;
var t = counts0[b];
counts0[b] = t + 1;
buffer[t] = a[i];
bufKeys[t] = keys[i];
}
for (i in 0...n) {
var b = (bufKeys[i] >>> 8) & 0xff;
var t = counts1[b];
counts1[b] = t + 1;
a[t] = buffer[i];
keys[t] = bufKeys[i];
}
for (i in 0...n) {
var b = (keys[i] >>> 16) & 0xff;
var t = counts2[b];
counts2[b] = t + 1;
buffer[t] = a[i];
bufKeys[t] = keys[i];
}
for (i in 0...n) {
var b = (bufKeys[i] >>> 24) & 0xff;
var t = counts3[b];
counts3[b] = t + 1;
a[t] = buffer[i];
keys[t] = bufKeys[i];
}
}
}
private class RadixFrame {
public var offset:Vector<Int>;
public var nextOffset:Vector<Int>;
public var remaining:Vector<Int>;
public function new() {
offset = new Vector<Int>(256);
nextOffset = new Vector<Int>(256);
remaining = new Vector<Int>(256);
}
}
private class RadixScratch {
public var frames:Array<RadixFrame>;
public function new() {
frames = [];
}
public inline function frame(level:Int):RadixFrame {
var f = frames[level];
if (f == null) {
f = new RadixFrame();
frames[level] = f;
}
return f;
}
}