Skip to content

Commit

Permalink
Shiver me timbers
Browse files Browse the repository at this point in the history
- 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
  • Loading branch information
Jozufozu committed Sep 15, 2024
1 parent 5ffddeb commit 6f2b8fd
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 64 deletions.
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -32,11 +28,12 @@
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();

Expand All @@ -52,20 +49,24 @@ 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;
resetPose();
}

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;
Expand All @@ -77,7 +78,7 @@ private static InstanceTree create(InstancerProvider provider, MeshTree meshTree
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) {
Expand Down Expand Up @@ -105,18 +106,17 @@ public PartPose initialPose() {
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) {
Expand All @@ -133,7 +133,7 @@ public void traverse(Consumer<? super TransformedInstance> consumer) {
if (instance != null) {
consumer.accept(instance);
}
for (InstanceTree child : children.values()) {
for (InstanceTree child : children) {
child.traverse(consumer);
}
}
Expand All @@ -143,7 +143,7 @@ public void traverse(int i, ObjIntConsumer<? super TransformedInstance> consumer
if (instance != null) {
consumer.accept(instance, i);
}
for (InstanceTree child : children.values()) {
for (InstanceTree child : children) {
child.traverse(i, consumer);
}
}
Expand All @@ -153,7 +153,7 @@ public void traverse(int i, int j, ObjIntIntConsumer<? super TransformedInstance
if (instance != null) {
consumer.accept(instance, i, j);
}
for (InstanceTree child : children.values()) {
for (InstanceTree child : children) {
child.traverse(i, j, consumer);
}
}
Expand All @@ -171,16 +171,6 @@ public void translateAndRotate(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) {
poseStack.pushPose();
translateAndRotate(poseStack);
Expand All @@ -192,7 +182,9 @@ private void updateInstancesInner(PoseStack poseStack, Walker walker) {

// 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();
}
Expand Down Expand Up @@ -294,23 +286,14 @@ public void delete() {
if (instance != null) {
instance.delete();
}
children.values()
.forEach(InstanceTree::delete);
for (InstanceTree child : children) {
child.delete();
}
}

@ApiStatus.Experimental
@FunctionalInterface
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);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
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;
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;

Expand All @@ -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;
}

Expand All @@ -58,13 +57,17 @@ private static MeshTree convert(ModelLayerLocation layer) {

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
Expand All @@ -91,18 +94,35 @@ public PartPose initialPose() {
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) {
Expand All @@ -119,7 +139,7 @@ public void traverse(Consumer<Mesh> consumer) {
if (mesh != null) {
consumer.accept(mesh);
}
for (MeshTree child : children.values()) {
for (MeshTree child : children) {
child.traverse(consumer);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public void pushPose() {
super.pushPose();
} else {
var last = last();
var recycle = recycleBin.pop();
var recycle = recycleBin.removeLast();
recycle.pose()
.set(last.pose());
recycle.normal()
Expand All @@ -36,7 +36,7 @@ public void pushPose() {

@Override
public void popPose() {
recycleBin.push(FlwLibLink.INSTANCE.getPoseStack(this)
recycleBin.addLast(FlwLibLink.INSTANCE.getPoseStack(this)
.removeLast());
}
}

0 comments on commit 6f2b8fd

Please sign in to comment.