Skip to content

Commit d1ab1c6

Browse files
committed
Switch face-sorting to merge-sort
1 parent 63a003b commit d1ab1c6

File tree

3 files changed

+95
-27
lines changed

3 files changed

+95
-27
lines changed

BlueMapCore/src/main/java/de/bluecolored/bluemap/core/map/hires/HiresTileModel.java

Lines changed: 31 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -29,14 +29,12 @@
2929
import com.google.gson.GsonBuilder;
3030
import com.google.gson.stream.JsonWriter;
3131
import de.bluecolored.bluemap.core.util.InstancePool;
32+
import de.bluecolored.bluemap.core.util.MergeSort;
3233
import de.bluecolored.bluemap.core.util.math.MatrixM3f;
3334
import de.bluecolored.bluemap.core.util.math.MatrixM4f;
3435
import de.bluecolored.bluemap.core.util.math.VectorM3f;
3536

36-
import java.io.BufferedWriter;
37-
import java.io.IOException;
38-
import java.io.OutputStream;
39-
import java.io.OutputStreamWriter;
37+
import java.io.*;
4038
import java.nio.charset.StandardCharsets;
4139
import java.util.UUID;
4240

@@ -64,7 +62,7 @@ public class HiresTileModel {
6462
private double[] position;
6563
private float[] color, uv, ao;
6664
private byte[] sunlight, blocklight;
67-
private int[] materialIndex;
65+
private int[] materialIndex, materialIndexSort, materialIndexSortSupport;
6866

6967
public HiresTileModel(int initialCapacity) {
7068
if (initialCapacity < 0) throw new IllegalArgumentException("initialCapacity is negative");
@@ -388,6 +386,9 @@ private void setCapacity(int capacity) {
388386
sunlight = new byte [capacity * FI_SUNLIGHT];
389387
blocklight = new byte [capacity * FI_BLOCKLIGHT];
390388
materialIndex = new int [capacity * FI_MATERIAL_INDEX];
389+
390+
materialIndexSort = new int[materialIndex.length];
391+
materialIndexSortSupport = new int [materialIndex.length];
391392
}
392393

393394
public void writeBufferGeometryJson(OutputStream out) throws IOException {
@@ -396,6 +397,8 @@ public void writeBufferGeometryJson(OutputStream out) throws IOException {
396397
Gson gson = new GsonBuilder().create();
397398
JsonWriter json = gson.newJsonWriter(new BufferedWriter(new OutputStreamWriter(out, StandardCharsets.UTF_8), 81920));
398399

400+
Writer w = null;
401+
399402
json.beginObject(); // main-object
400403
json.name("tileGeometry").beginObject(); // tile-geometry-object
401404

@@ -583,20 +586,20 @@ private void writeMaterialGroups(JsonWriter json) throws IOException {
583586
material = materialIndex[i];
584587

585588
if (material != lastMaterial) {
586-
json.name("count").value((i - groupStart) * 3);
589+
json.name("count").value((i - groupStart) * 3L);
587590
json.endObject();
588591

589592
groupStart = i;
590593

591594
json.beginObject();
592595
json.name("materialIndex").value(material);
593-
json.name("start").value(groupStart * 3);
596+
json.name("start").value(groupStart * 3L);
594597
}
595598

596599
lastMaterial = material;
597600
}
598601

599-
json.name("count").value((miSize - groupStart) * 3);
602+
json.name("count").value((miSize - groupStart) * 3L);
600603
json.endObject();
601604

602605
}
@@ -611,34 +614,30 @@ private void writeRounded(JsonWriter json, double value) throws IOException {
611614
else json.value(d);
612615
}
613616

614-
/**
615-
* Does an optimized selection sort to sort all faces based on their material-index.
616-
* A selection sort is chosen, because it requires the least amount of swaps, which seem (untested) to be the most expensive operation here
617-
*/
618617
private void sort() {
619618
if (size <= 1) return; // nothing to sort
620619

621-
int prev = Integer.MIN_VALUE, min, minIndex, i, j;
622-
for (i = 0; i < size - 1; i++){
623-
minIndex = i;
624-
min = materialIndex[minIndex];
625-
if (min <= prev) continue; // shortcut
620+
// initialize material-index-sort
621+
for (int i = 0; i < size; i++) {
622+
materialIndexSort[i] = i;
623+
materialIndexSortSupport[i] = i;
624+
}
626625

627-
for (j = i + 1; j < size; j++){
628-
if (materialIndex[j] < min){
629-
minIndex = j;
630-
min = materialIndex[minIndex];
631-
}
632-
}
626+
// sort
627+
MergeSort.mergeSortInt(materialIndexSort, 0, size, this::compareMaterialIndex, materialIndexSortSupport);
633628

634-
if (minIndex != i) {
635-
swap(minIndex, i);
629+
// move
630+
for (int i = 0; i < size; i++) {
631+
while (materialIndexSort[i] != i) {
632+
swap(i, materialIndexSort[i]);
636633
}
637-
638-
prev = min;
639634
}
640635
}
641636

637+
private int compareMaterialIndex(int i1, int i2) {
638+
return Integer.compare(materialIndex[i1], materialIndex[i2]);
639+
}
640+
642641
private void swap(int face1, int face2) {
643642
int i, if1, if2, vi;
644643
double vd;
@@ -695,6 +694,11 @@ private void swap(int face1, int face2) {
695694
vi = materialIndex[face1];
696695
materialIndex[face1] = materialIndex[face2];
697696
materialIndex[face2] = vi;
697+
698+
//swap material-index-sort (assuming FI_MATERIAL_INDEX = 1)
699+
vi = materialIndexSort[face1];
700+
materialIndexSort[face1] = materialIndexSort[face2];
701+
materialIndexSort[face2] = vi;
698702
}
699703

700704
private static void calculateSurfaceNormal(
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package de.bluecolored.bluemap.core.util;
2+
3+
import java.util.Comparator;
4+
5+
@FunctionalInterface
6+
public interface IntComparator extends Comparator<Integer> {
7+
8+
int compare(int o1, int o2);
9+
10+
@Override
11+
default int compare(Integer o1, Integer o2) {
12+
return compare(o1.intValue(), o2.intValue());
13+
}
14+
15+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package de.bluecolored.bluemap.core.util;
2+
3+
public class MergeSort {
4+
5+
/*
6+
* Adapted from: https://github.com/vigna/fastutil
7+
*/
8+
public static void mergeSortInt(final int[] a, final int from, final int to, IntComparator comp, int[] supp) {
9+
int len = to - from;
10+
11+
if (len < 16) {
12+
insertionSortInt(a, from, to, comp);
13+
return;
14+
}
15+
if (supp == null) supp = java.util.Arrays.copyOf(a, to);
16+
17+
final int mid = (from + to) >>> 1;
18+
mergeSortInt(supp, from, mid, comp, a);
19+
mergeSortInt(supp, mid, to, comp, a);
20+
21+
if (comp.compare(supp[mid - 1], supp[mid]) <= 0) {
22+
System.arraycopy(supp, from, a, from, len);
23+
return;
24+
}
25+
26+
for (int i = from, p = from, q = mid; i < to; i++) {
27+
if (q >= to || p < mid && comp.compare(supp[p], supp[q]) <= 0) a[i] = supp[p++];
28+
else a[i] = supp[q++];
29+
}
30+
}
31+
32+
/*
33+
* Adapted from: https://github.com/vigna/fastutil
34+
*/
35+
private static void insertionSortInt(final int[] a, final int from, final int to, final IntComparator comp) {
36+
for (int i = from; ++i < to; ) {
37+
int t = a[i], j = i;
38+
for (int u = a[j - 1]; comp.compare(t, u) < 0; u = a[--j - 1]) {
39+
a[j] = u;
40+
if (from == j - 1) {
41+
--j;
42+
break;
43+
}
44+
}
45+
a[j] = t;
46+
}
47+
}
48+
49+
}

0 commit comments

Comments
 (0)