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; 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.NoSuchElementException;
import java.util.function.BiConsumer;
import java.util.function.BiFunction; import java.util.function.BiFunction;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.function.ObjIntConsumer; import java.util.function.ObjIntConsumer;
@ -32,11 +28,12 @@ import net.minecraft.client.model.geom.PartPose;
public final class InstanceTree { public final class InstanceTree {
private static final ModelCache<Model.ConfiguredMesh> MODEL_CACHE = new ModelCache<>(entry -> new SingleMeshModel(entry.mesh(), entry.material())); private static final ModelCache<Model.ConfiguredMesh> MODEL_CACHE = new ModelCache<>(entry -> new SingleMeshModel(entry.mesh(), entry.material()));
private final MeshTree source;
@Nullable @Nullable
private final TransformedInstance instance; private final TransformedInstance instance;
private final PartPose initialPose; private final PartPose initialPose;
@Unmodifiable @Unmodifiable
private final Map<String, InstanceTree> children; private final InstanceTree[] children;
private final Quaternionf rotation = new Quaternionf(); private final Quaternionf rotation = new Quaternionf();
@ -52,7 +49,8 @@ public final class InstanceTree {
public boolean visible = true; public boolean visible = true;
public boolean skipDraw; 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.instance = instance;
this.initialPose = initialPose; this.initialPose = initialPose;
this.children = children; 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) { 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 + "/"; String pathSlash = path + "/";
meshTree.children().forEach((name, meshTreeChild) -> { for (int i = 0; i < meshTree.childCount(); i++) {
children.put(name, InstanceTree.create(provider, meshTreeChild, meshFinalizerFunc, pathSlash + name)); var meshTreeChild = meshTree.child(i);
}); String name = meshTree.childName(i);
children[i] = InstanceTree.create(provider, meshTreeChild, meshFinalizerFunc, pathSlash + name);
}
Mesh mesh = meshTree.mesh(); Mesh mesh = meshTree.mesh();
TransformedInstance instance; TransformedInstance instance;
@ -77,7 +78,7 @@ public final class InstanceTree {
instance = null; 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) { public static InstanceTree create(InstancerProvider provider, MeshTree meshTree, BiFunction<String, Mesh, Model.ConfiguredMesh> meshFinalizerFunc) {
@ -105,18 +106,17 @@ public final class InstanceTree {
return initialPose; return initialPose;
} }
@Unmodifiable @Nullable
public Map<String, InstanceTree> children() { public InstanceTree child(int index) {
return children; if (index < 0 || index >= children.length) {
} return null;
}
public boolean hasChild(String name) { return children[index];
return children.containsKey(name);
} }
@Nullable @Nullable
public InstanceTree child(String name) { public InstanceTree child(String name) {
return children.get(name); return child(source.childIndex(name));
} }
public InstanceTree childOrThrow(String name) { public InstanceTree childOrThrow(String name) {
@ -133,7 +133,7 @@ public final class InstanceTree {
if (instance != null) { if (instance != null) {
consumer.accept(instance); consumer.accept(instance);
} }
for (InstanceTree child : children.values()) { for (InstanceTree child : children) {
child.traverse(consumer); child.traverse(consumer);
} }
} }
@ -143,7 +143,7 @@ public final class InstanceTree {
if (instance != null) { if (instance != null) {
consumer.accept(instance, i); consumer.accept(instance, i);
} }
for (InstanceTree child : children.values()) { for (InstanceTree child : children) {
child.traverse(i, consumer); child.traverse(i, consumer);
} }
} }
@ -153,7 +153,7 @@ public final class InstanceTree {
if (instance != null) { if (instance != null) {
consumer.accept(instance, i, j); consumer.accept(instance, i, j);
} }
for (InstanceTree child : children.values()) { for (InstanceTree child : children) {
child.traverse(i, j, consumer); child.traverse(i, j, consumer);
} }
} }
@ -171,16 +171,6 @@ public final class InstanceTree {
} }
public void updateInstances(PoseStack poseStack) { 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) { if (visible) {
poseStack.pushPose(); poseStack.pushPose();
translateAndRotate(poseStack); translateAndRotate(poseStack);
@ -192,7 +182,9 @@ public final class InstanceTree {
// Use the bare HashMap.forEach because .values() always allocates a new collection. // 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. // 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(); poseStack.popPose();
} }
@ -294,8 +286,9 @@ public final class InstanceTree {
if (instance != null) { if (instance != null) {
instance.delete(); instance.delete();
} }
children.values() for (InstanceTree child : children) {
.forEach(InstanceTree::delete); child.delete();
}
} }
@ApiStatus.Experimental @ApiStatus.Experimental
@ -303,14 +296,4 @@ public final class InstanceTree {
public interface ObjIntIntConsumer<T> { public interface ObjIntIntConsumer<T> {
void accept(T t, int i, int j); 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; package dev.engine_room.flywheel.lib.model.part;
import java.util.Collections; import java.util.Arrays;
import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.NoSuchElementException; import java.util.NoSuchElementException;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
@ -9,7 +8,6 @@ import java.util.function.Consumer;
import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.Unmodifiable;
import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.blaze3d.vertex.PoseStack;
@ -35,12 +33,13 @@ public final class MeshTree {
@Nullable @Nullable
private final Mesh mesh; private final Mesh mesh;
private final PartPose initialPose; private final PartPose initialPose;
@Unmodifiable private final MeshTree[] children;
private final Map<String, 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.mesh = mesh;
this.initialPose = initialPose; this.initialPose = initialPose;
this.childKeys = childKeys;
this.children = children; this.children = children;
} }
@ -58,13 +57,17 @@ public final class MeshTree {
private static MeshTree convert(ModelPart modelPart, ThreadLocalObjects objects) { private static MeshTree convert(ModelPart modelPart, ThreadLocalObjects objects) {
var modelPartChildren = FlwLibLink.INSTANCE.getModelPartChildren(modelPart); var modelPartChildren = FlwLibLink.INSTANCE.getModelPartChildren(modelPart);
Map<String, MeshTree> children = new HashMap<>();
modelPartChildren.forEach((name, modelPartChild) -> { // Freeze the ordering here. Maybe we want to sort this?
children.put(name, convert(modelPartChild, objects)); 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 @Nullable
@ -91,18 +94,35 @@ public final class MeshTree {
return initialPose; return initialPose;
} }
@Unmodifiable public int childCount() {
public Map<String, MeshTree> children() { return children.length;
return children; }
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) { public boolean hasChild(String name) {
return children.containsKey(name); return childIndex(name) >= 0;
} }
@Nullable @Nullable
public MeshTree child(String name) { 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) { public MeshTree childOrThrow(String name) {
@ -119,7 +139,7 @@ public final class MeshTree {
if (mesh != null) { if (mesh != null) {
consumer.accept(mesh); consumer.accept(mesh);
} }
for (MeshTree child : children.values()) { for (MeshTree child : children) {
child.traverse(consumer); child.traverse(consumer);
} }
} }

View file

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