Object Occlusion Culling
This commit is contained in:
350
lib/radix/Sources/radix/Radix.hx
Normal file
350
lib/radix/Sources/radix/Radix.hx
Normal 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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user