Object Occlusion Culling
This commit is contained in:
23
lib/radix/LICENSE_1_0.txt
Normal file
23
lib/radix/LICENSE_1_0.txt
Normal file
@ -0,0 +1,23 @@
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
23
lib/radix/README.md
Normal file
23
lib/radix/README.md
Normal file
@ -0,0 +1,23 @@
|
||||
# Radix
|
||||
Haxe port: in-place MSD radix sort (American flag sort) with an insertion sort fallback for small sub-ranges implemented in the Leenkx SDK by Riccardo Mazzucco
|
||||
|
||||
Original reference source Malte Skarupke's [ska_sort](https://github.com/skarupke/ska_sort) - https://github.com/skarupke/ska_sort
|
||||
|
||||
## Usage
|
||||
|
||||
```haxe
|
||||
import radix.Radix;
|
||||
|
||||
// Stable sort: counting-sort scatter between `a` and `buffer`.
|
||||
// Equal keys keep input order - matches haxe Array.sort semantics.
|
||||
Radix.sortCopyKeyed(meshes, keys, meshesBuf, keysBuf);
|
||||
|
||||
// In-place MSD sort on the same (element, keys) arrays.
|
||||
// No scratch buffer needed, but NOT stable - equal keys may reorder.
|
||||
Radix.sortKeyed(meshes, keys);
|
||||
|
||||
// Or let the sorter build keys from an extractor
|
||||
Radix.sort(meshes, function(m) {
|
||||
return (m.depthRead ? 0x80000000 : 0) | (FPHelper.floatToI32(m.cameraDistance) >>> 1);
|
||||
});
|
||||
```
|
||||
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;
|
||||
}
|
||||
}
|
||||
11
lib/radix/haxelib.json
Normal file
11
lib/radix/haxelib.json
Normal file
@ -0,0 +1,11 @@
|
||||
{
|
||||
"name": "radix",
|
||||
"url": "https://github.com/skarupke/ska_sort",
|
||||
"license": "BSL-1.0",
|
||||
"tags": ["sort", "radix", "msd", "in-place"],
|
||||
"description": "Haxe port : in-place MSD radix sort (American flag sort) for Leenkx SDK from Malte Skarupke's ska_sort with insertion sort fallback and stable LSD copy path",
|
||||
"version": "1.0.0",
|
||||
"classPath": "Sources/",
|
||||
"releasenote": "Leenkx HAXE port",
|
||||
"contributors": ["Riccardo Mazzucco"]
|
||||
}
|
||||
Reference in New Issue
Block a user