Shiver me timbers

- Use arrrays in the trees
- MeshTree tracks parallel arrays of children and keys
- InstanceTree tracks which mesh tree it came from for lookup purposes
- Remove walker object
- Make RecyclingPoseStack use add/removeLast instead of push/pop
This commit is contained in:
Jozufozu 2024-09-14 19:38:55 -07:00
parent 5ffddeb221
commit 6f2b8fd3fb
3 changed files with 67 additions and 64 deletions

View File

@ -1,10 +1,6 @@
package dev.engine_room.flywheel.lib.model.part;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.ObjIntConsumer;
@ -32,11 +28,12 @@ import net.minecraft.client.model.geom.PartPose;
public final class InstanceTree {
private static final ModelCache<Model.ConfiguredMesh> MODEL_CACHE = new ModelCache<>(entry -> new SingleMeshModel(entry.mesh(), entry.material()));
private final MeshTree source;
@Nullable
private final TransformedInstance instance;
private final PartPose initialPose;
@Unmodifiable
private final Map<String, InstanceTree> children;
private final InstanceTree[] children;
private final Quaternionf rotation = new Quaternionf();
@ -52,7 +49,8 @@ public final class InstanceTree {
public boolean visible = true;
public boolean skipDraw;
private InstanceTree(@Nullable TransformedInstance instance, PartPose initialPose, @Unmodifiable Map<String, InstanceTree> children) {
private InstanceTree(MeshTree source, @Nullable TransformedInstance instance, PartPose initialPose, InstanceTree[] children) {
this.source = source;
this.instance = instance;
this.initialPose = initialPose;
this.children = children;
@ -60,12 +58,15 @@ public final class InstanceTree {
}
private static InstanceTree create(InstancerProvider provider, MeshTree meshTree, BiFunction<String, Mesh, Model.ConfiguredMesh> meshFinalizerFunc, String path) {
Map<String, InstanceTree> children = new HashMap<>();
InstanceTree[] children = new InstanceTree[meshTree.childCount()];
String pathSlash = path + "/";
meshTree.children().forEach((name, meshTreeChild) -> {
children.put(name, InstanceTree.create(provider, meshTreeChild, meshFinalizerFunc, pathSlash + name));
});
for (int i = 0; i < meshTree.childCount(); i++) {
var meshTreeChild = meshTree.child(i);
String name = meshTree.childName(i);
children[i] = InstanceTree.create(provider, meshTreeChild, meshFinalizerFunc, pathSlash + name);
}
Mesh mesh = meshTree.mesh();
TransformedInstance instance;
@ -77,7 +78,7 @@ public final class InstanceTree {
instance = null;
}
return new InstanceTree(instance, meshTree.initialPose(), Collections.unmodifiableMap(children));
return new InstanceTree(meshTree, instance, meshTree.initialPose(), children);
}
public static InstanceTree create(InstancerProvider provider, MeshTree meshTree, BiFunction<String, Mesh, Model.ConfiguredMesh> meshFinalizerFunc) {
@ -105,18 +106,17 @@ public final class InstanceTree {
return initialPose;
}
@Unmodifiable
public Map<String, InstanceTree> children() {
return children;
}
public boolean hasChild(String name) {
return children.containsKey(name);
@Nullable
public InstanceTree child(int index) {
if (index < 0 || index >= children.length) {
return null;
}
return children[index];
}
@Nullable
public InstanceTree child(String name) {
return children.get(name);
return child(source.childIndex(name));
}
public InstanceTree childOrThrow(String name) {
@ -133,7 +133,7 @@ public final class InstanceTree {
if (instance != null) {
consumer.accept(instance);
}
for (InstanceTree child : children.values()) {
for (InstanceTree child : children) {
child.traverse(consumer);
}
}
@ -143,7 +143,7 @@ public final class InstanceTree {
if (instance != null) {
consumer.accept(instance, i);
}
for (InstanceTree child : children.values()) {
for (InstanceTree child : children) {
child.traverse(i, consumer);
}
}
@ -153,7 +153,7 @@ public final class InstanceTree {
if (instance != null) {
consumer.accept(instance, i, j);
}
for (InstanceTree child : children.values()) {
for (InstanceTree child : children) {
child.traverse(i, j, consumer);
}
}
@ -171,16 +171,6 @@ public final class InstanceTree {
}
public void updateInstances(PoseStack poseStack) {
// Need to use an anonymous class so it can reference this.
updateInstancesInner(poseStack, new Walker() {
@Override
public void accept(InstanceTree child) {
child.updateInstancesInner(poseStack, this);
}
});
}
private void updateInstancesInner(PoseStack poseStack, Walker walker) {
if (visible) {
poseStack.pushPose();
translateAndRotate(poseStack);
@ -192,7 +182,9 @@ public final class InstanceTree {
// Use the bare HashMap.forEach because .values() always allocates a new collection.
// We also don't want to store an array of children because that would statically use a lot more memory.
children.forEach(walker);
for (InstanceTree child : children) {
child.updateInstances(poseStack);
}
poseStack.popPose();
}
@ -294,8 +286,9 @@ public final class InstanceTree {
if (instance != null) {
instance.delete();
}
children.values()
.forEach(InstanceTree::delete);
for (InstanceTree child : children) {
child.delete();
}
}
@ApiStatus.Experimental
@ -303,14 +296,4 @@ public final class InstanceTree {
public interface ObjIntIntConsumer<T> {
void accept(T t, int i, int j);
}
// Helper interface for writing walking classes.
private interface Walker extends BiConsumer<String, InstanceTree> {
void accept(InstanceTree child);
@Override
default void accept(String name, InstanceTree child) {
accept(child);
}
}
}

View File

@ -1,7 +1,6 @@
package dev.engine_room.flywheel.lib.model.part;
import java.util.Collections;
import java.util.HashMap;
import java.util.Arrays;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.concurrent.ConcurrentHashMap;
@ -9,7 +8,6 @@ import java.util.function.Consumer;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.Unmodifiable;
import com.mojang.blaze3d.vertex.PoseStack;
@ -35,12 +33,13 @@ public final class MeshTree {
@Nullable
private final Mesh mesh;
private final PartPose initialPose;
@Unmodifiable
private final Map<String, MeshTree> children;
private final MeshTree[] children;
private final String[] childKeys;
private MeshTree(@Nullable Mesh mesh, PartPose initialPose, @Unmodifiable Map<String, MeshTree> children) {
private MeshTree(@Nullable Mesh mesh, PartPose initialPose, MeshTree[] children, String[] childKeys) {
this.mesh = mesh;
this.initialPose = initialPose;
this.childKeys = childKeys;
this.children = children;
}
@ -58,13 +57,17 @@ public final class MeshTree {
private static MeshTree convert(ModelPart modelPart, ThreadLocalObjects objects) {
var modelPartChildren = FlwLibLink.INSTANCE.getModelPartChildren(modelPart);
Map<String, MeshTree> children = new HashMap<>();
modelPartChildren.forEach((name, modelPartChild) -> {
children.put(name, convert(modelPartChild, objects));
});
// Freeze the ordering here. Maybe we want to sort this?
String[] childKeys = modelPartChildren.keySet()
.toArray(new String[0]);
return new MeshTree(compile(modelPart, objects), modelPart.getInitialPose(), Collections.unmodifiableMap(children));
MeshTree[] children = new MeshTree[modelPartChildren.size()];
for (int i = 0; i < childKeys.length; i++) {
children[i] = convert(modelPartChildren.get(childKeys[i]), objects);
}
return new MeshTree(compile(modelPart, objects), modelPart.getInitialPose(), children, childKeys);
}
@Nullable
@ -91,18 +94,35 @@ public final class MeshTree {
return initialPose;
}
@Unmodifiable
public Map<String, MeshTree> children() {
return children;
public int childCount() {
return children.length;
}
public MeshTree child(int index) {
return children[index];
}
public String childName(int index) {
return childKeys[index];
}
public int childIndex(String name) {
return Arrays.binarySearch(childKeys, name);
}
public boolean hasChild(String name) {
return children.containsKey(name);
return childIndex(name) >= 0;
}
@Nullable
public MeshTree child(String name) {
return children.get(name);
int index = childIndex(name);
if (index < 0) {
return null;
}
return child(index);
}
public MeshTree childOrThrow(String name) {
@ -119,7 +139,7 @@ public final class MeshTree {
if (mesh != null) {
consumer.accept(mesh);
}
for (MeshTree child : children.values()) {
for (MeshTree child : children) {
child.traverse(consumer);
}
}

View File

@ -24,7 +24,7 @@ public class RecyclingPoseStack extends PoseStack {
super.pushPose();
} else {
var last = last();
var recycle = recycleBin.pop();
var recycle = recycleBin.removeLast();
recycle.pose()
.set(last.pose());
recycle.normal()
@ -36,7 +36,7 @@ public class RecyclingPoseStack extends PoseStack {
@Override
public void popPose() {
recycleBin.push(FlwLibLink.INSTANCE.getPoseStack(this)
recycleBin.addLast(FlwLibLink.INSTANCE.getPoseStack(this)
.removeLast());
}
}