Skip to content

Commit 4dd526b

Browse files
committed
Just pushing current stuff.
1 parent 776e72b commit 4dd526b

32 files changed

Lines changed: 1106 additions & 596 deletions

src/library/java/gg/generations/rarecandy/pokeutils/MaterialReference.java

Lines changed: 133 additions & 196 deletions
Large diffs are not rendered by default.

src/library/java/gg/generations/rarecandy/pokeutils/Pair.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,12 @@ public void b(B b) {
2727
this.b = b;
2828
}
2929

30+
public Pair<A, B> set(A a, B b) {
31+
a(a);
32+
b(b);
33+
return this;
34+
}
35+
3036
@Override
3137
public int hashCode() {
3238
return Objects.hash(a, b);

src/library/java/gg/generations/rarecandy/renderer/animation/Animation.java

Lines changed: 97 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import gg.generations.rarecandy.pokeutils.ModelNode;
44
import gg.generations.rarecandy.pokeutils.SkeletalTransform;
55
import org.joml.Matrix4f;
6+
import org.joml.Matrix4fStack;
67
import org.joml.Quaternionf;
78
import org.joml.Vector3f;
89

@@ -11,8 +12,6 @@
1112
import java.util.function.BiConsumer;
1213

1314
public class Animation {
14-
private final static Matrix4f IDENTITY = new Matrix4f();
15-
1615
public static final int FPS_60 = 1000;
1716
public static final int FPS_24 = 400;
1817
public static final int GLB_SPEED = 30;
@@ -21,6 +20,20 @@ public class Animation {
2120
public static Vector3f TRANSLATE = new Vector3f();
2221
protected static Vector3f SCALE = new Vector3f(1, 1, 1);
2322
protected static Vector3f TRANSLATION = new Vector3f();
23+
24+
private static final Matrix4f[] MATRIX = new Matrix4f[220];
25+
private static final Matrix4fStack GLOBAL = new Matrix4fStack(220);
26+
private final static Matrix4f IDENTITY = new Matrix4f();
27+
private static final Matrix4f TEMP_GLOBAL_TRANSFORM = new Matrix4f();
28+
private static final Matrix4f TEMP_BONE_RESULT = new Matrix4f();
29+
private static final Vector3f TEMP_ORIGIN_VECTOR = new Vector3f();
30+
31+
static {
32+
for (int i = 0; i < MATRIX.length; i++) {
33+
MATRIX[i] = new Matrix4f();
34+
}
35+
}
36+
2437
public final int id;
2538
public final double animationDuration;
2639
protected final Skeleton skeleton;
@@ -29,6 +42,10 @@ public class Animation {
2942
private final AnimationNode[] animationNodes;
3043
public Offset[] offsets;
3144

45+
46+
private Matrix4f[] cachedBoneTransforms;
47+
private final Matrix4f cachedIdentity = new Matrix4f().identity();
48+
3249
public float ticksPerSecond;
3350
public boolean loops;
3451
public boolean ignoreInstancedTime = false;
@@ -92,13 +109,23 @@ public float getAnimationTime(double secondsPassed) {
92109
}
93110

94111
public Matrix4f[] getFrameTransform(AnimationInstance instance) {
95-
var boneTransforms = new Matrix4f[this.skeleton.jointMap.size()];
96-
readNodeHierarchy(instance.getCurrentTime(), skeleton.rootNode, new Matrix4f().identity(), boneTransforms, false);
97-
for (int i = 0; i < boneTransforms.length; i++) {
98-
if(boneTransforms[i] == null) boneTransforms[i] = new Matrix4f();
112+
113+
if (cachedBoneTransforms == null || cachedBoneTransforms.length != skeleton.jointMap.size()) {
114+
cachedBoneTransforms = new Matrix4f[skeleton.jointMap.size()];
115+
for (int i = 0; i < cachedBoneTransforms.length; i++) {
116+
cachedBoneTransforms[i] = new Matrix4f();
117+
}
118+
}
119+
120+
// Reset all transforms to identity before populating
121+
for (Matrix4f mat : cachedBoneTransforms) {
122+
mat.identity();
99123
}
100124

101-
return boneTransforms;
125+
GLOBAL.identity();
126+
127+
readNodeHierarchy(instance.getCurrentTime(), skeleton.rootNode, cachedBoneTransforms, false, 0);
128+
return cachedBoneTransforms;
102129
}
103130

104131
public void getFrameOffset(AnimationInstance instance) {
@@ -114,21 +141,26 @@ public void getFrameOffset(AnimationInstance instance) {
114141
}
115142

116143
public Matrix4f[] getFrameTransform(double secondsPassed) {
117-
var boneTransforms = new Matrix4f[this.skeleton.jointMap.size()];
118-
readNodeHierarchy(getAnimationTime(secondsPassed), skeleton.rootNode, new Matrix4f().identity(), boneTransforms, false);
144+
if (cachedBoneTransforms == null || cachedBoneTransforms.length != skeleton.jointMap.size()) {
145+
cachedBoneTransforms = new Matrix4f[skeleton.jointMap.size()];
146+
for (int i = 0; i < cachedBoneTransforms.length; i++) {
147+
cachedBoneTransforms[i] = new Matrix4f();
148+
}
149+
}
119150

120-
for (int i = 0; i < boneTransforms.length; i++) {
121-
if(boneTransforms[i] == null) boneTransforms[i] = new Matrix4f();
151+
for (Matrix4f mat : cachedBoneTransforms) {
152+
mat.identity();
122153
}
123154

124-
return boneTransforms;
155+
readNodeHierarchy(getAnimationTime(secondsPassed), skeleton.rootNode, cachedBoneTransforms, false, 0);
156+
return cachedBoneTransforms;
125157
}
126158

127-
private static final Matrix4f matrix = new Matrix4f();
159+
public void readNodeHierarchy(float animTime, ModelNode node, Matrix4f[] boneTransforms, boolean offsetUsed, int depth) {
160+
128161

129-
public void readNodeHierarchy(float animTime, ModelNode node, Matrix4f parentTransform, Matrix4f[] boneTransforms, boolean offsetUsed) {
130162
var name = node.name;
131-
var nodeTransform = matrix.set(node.transform);
163+
var nodeTransform = MATRIX[depth].set(node.transform); // Reuses existing static 'matrix' field
132164

133165
var animationNodeId = skeleton.boneIdMap.getOrDefault(name, -1);
134166
var bone = skeleton.get(name);
@@ -139,26 +171,44 @@ public void readNodeHierarchy(float animTime, ModelNode node, Matrix4f parentTra
139171
if (animNode != null) {
140172
var scale = ignoreScaling ? SCALE : AnimationMath.calcInterpolatedScaling(animTime, animNode);
141173
var rotation = AnimationMath.calcInterpolatedRotation(animTime, animNode);
142-
var translation = name.equalsIgnoreCase("origin") ? new Vector3f() : AnimationMath.calcInterpolatedPosition(animTime, animNode);
143174

144-
if(!offsetUsed) {
175+
// Reuse pooled Vector3f for "origin" case
176+
Vector3f translation;
177+
if (name.equalsIgnoreCase("origin")) {
178+
translation = TEMP_ORIGIN_VECTOR.set(0);
179+
} else {
180+
translation = AnimationMath.calcInterpolatedPosition(animTime, animNode);
181+
}
182+
183+
if (!offsetUsed) {
145184
offsetUsed = true;
146185
translation.add(rootOffset.position());
147186
rotation.mul(rootOffset.rotation());
148187
}
149188

150-
nodeTransform.identity().translationRotateScale(translation, rotation, scale);
189+
if(!isIdentityTransform(translation, scale, rotation, 1e-5f)) nodeTransform.identity().translationRotateScale(translation, rotation, scale);
151190
}
152191
}
153192

154-
var globalTransform = parentTransform.mul(nodeTransform, new Matrix4f());
155193

156-
if (bone != null) {
157-
boneTransforms[animationNodeId] = globalTransform.mul(bone.inverseBindMatrix, new Matrix4f());
194+
195+
// Reuse pooled Matrix4f for globalTransform
196+
TEMP_GLOBAL_TRANSFORM.set(GLOBAL).mul(nodeTransform);
197+
198+
if (bone != null && animationNodeId >= 0 && animationNodeId < boneTransforms.length) {
199+
// Write directly into pre-allocated array slot
200+
TEMP_GLOBAL_TRANSFORM.mul(bone.inverseBindMatrix, boneTransforms[animationNodeId]);
158201
}
159202

160-
for (var child : node.children)
161-
readNodeHierarchy(animTime, child, globalTransform, boneTransforms, offsetUsed);
203+
var nextDepth = depth + 1;
204+
205+
GLOBAL.pushMatrix();
206+
GLOBAL.set(TEMP_GLOBAL_TRANSFORM);
207+
208+
for (var child : node.children) {
209+
readNodeHierarchy(animTime, child, boneTransforms, offsetUsed, nextDepth);
210+
}
211+
GLOBAL.popMatrix();
162212
}
163213

164214
private boolean isNaN(Matrix4f nodeTransform) {
@@ -243,6 +293,30 @@ public void calcOffset(float animTime, Transform instance) {
243293
instance.scale().set(uScale, vScale);
244294
}
245295
}
296+
297+
public static boolean isIdentityTransform(
298+
Vector3f translation,
299+
Vector3f scale,
300+
Quaternionf rotation,
301+
float eps
302+
) {
303+
boolean tX = translation.x == 0.0f;
304+
boolean tY = translation.y == 0.0f;
305+
boolean tZ = translation.z == 0.0f;
306+
307+
boolean sX = scale.x == 0.0f;
308+
boolean sY = scale.y == 0.0f;
309+
boolean sZ = scale.z == 0.0f;
310+
311+
boolean rX = rotation.x == 0.0f;
312+
boolean rY = rotation.y == 0.0f;
313+
boolean rZ = rotation.z == 0.0f;
314+
boolean rW = rotation.w == 1.0f;
315+
316+
return tX && tY && tZ
317+
&& sX && sY && sZ
318+
&& rX && rY && rZ && rW;
319+
}
246320
}
247321

248322

src/library/java/gg/generations/rarecandy/renderer/animation/AnimationController.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ public class AnimationController {
1616
Arrays.fill(NO_ANIMATION, identity);
1717
}
1818

19-
public final List<AnimationInstance> playingInstances = new ArrayList<>();
19+
public final Set<AnimationInstance> playingInstances = new LinkedHashSet<>();
2020
public final Map<Animation, Matrix4f[]> instanceIgnoringAnimTransforms = new HashMap<>();
2121

2222
public void render(double globalSecondsPassed) {
@@ -44,6 +44,8 @@ public void render(double globalSecondsPassed) {
4444
playingInstance.animation.getFrameOffset(playingInstance);
4545
}
4646

47-
playingInstances.removeAll(instancesToRemove);
47+
for (AnimationInstance animationInstance : instancesToRemove) {
48+
playingInstances.remove(animationInstance);
49+
}
4850
}
4951
}

src/library/java/gg/generations/rarecandy/renderer/animation/AnimationInstance.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ public class AnimationInstance {
1414
public Matrix4f[] matrixTransforms;
1515
public Transform[] offsets;
1616

17+
private boolean registered = false;
1718

1819
protected Animation animation;
1920
protected float currentTime;
@@ -73,6 +74,7 @@ public boolean isPaused() {
7374

7475
public void destroy() {
7576
this.unused = true;
77+
this.registered = false;
7678
}
7779

7880
public boolean shouldDestroy() {
@@ -88,4 +90,15 @@ public Transform getOffset(int material) {
8890

8991
return offsets[material];
9092
}
93+
94+
/**
95+
* Ensures this instance is registered with the controller exactly once.
96+
* O(1) check instead of O(n) contains().
97+
*/
98+
public void ensureRegistered(AnimationController controller) {
99+
if (!registered && !unused) {
100+
controller.playingInstances.add(this);
101+
registered = true;
102+
}
103+
}
91104
}

src/library/java/gg/generations/rarecandy/renderer/animation/AnimationMath.java

Lines changed: 21 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,22 @@
55
import org.joml.Vector3f;
66

77
public class AnimationMath {
8+
private static final Pair<TransformStorage.TimeKey<Quaternionf>, TransformStorage.TimeKey<Quaternionf>> QUATERIONF_KEYPAIR = new Pair<>(null, null);
9+
private static final Pair<TransformStorage.TimeKey<Vector3f>, TransformStorage.TimeKey<Vector3f>> VECTOR3F_KEYPAIR = new Pair<>(null, null);
10+
11+
private static final Vector3f DEST_SCALE = new Vector3f();
12+
private static final Vector3f DEST_TRANSLATION = new Vector3f();
13+
private static final Quaternionf DEST_ROTATION = new Quaternionf();
814

915
public static Vector3f calcInterpolatedPosition(float animTime, Animation.AnimationNode node) {
1016
if (node.positionKeys.size() == 1) return node.getDefaultPosition().value();
1117

1218
var positions = findPositions(animTime, node);
1319
float factor = (float) ((animTime - (float) positions.a().time()) / (positions.b().time() - positions.a().time()));
1420

15-
var start = new Vector3f(positions.a().value());
16-
var end = new Vector3f(positions.b().value());
17-
var delta = new Vector3f(end.sub(start));
18-
return new Vector3f(start.add(delta.mul(factor)));
21+
var start = positions.a().value();
22+
var end = positions.b().value();
23+
return DEST_TRANSLATION.set(end).sub(start).mul(factor).add(start);
1924
}
2025

2126
public static Quaternionf calcInterpolatedRotation(float animTime, Animation.AnimationNode node) {
@@ -24,48 +29,46 @@ public static Quaternionf calcInterpolatedRotation(float animTime, Animation.Ani
2429
var rotations = findRotations(animTime, node);
2530
var deltaTime = (float) (rotations.b().time() - rotations.a().time());
2631
var factor = (animTime - (float) rotations.a().time()) / deltaTime;
27-
var start = new Quaternionf(rotations.a().value());
28-
var end = new Quaternionf(rotations.b().value());
29-
return new Quaternionf(start.slerp(end, factor));
32+
var start = rotations.a().value();
33+
var end = rotations.b().value();
34+
return DEST_ROTATION.set(start).slerp(end, factor);
3035
}
3136

3237
public static Vector3f calcInterpolatedScaling(float animTime, Animation.AnimationNode node) {
3338
if (node.scaleKeys.size() == 1) return node.getDefaultScale().value();
3439

35-
var out = new Vector3f();
3640
var scalings = findScalings(animTime, node);
3741
var deltaTime = (float) (scalings.b().time() - scalings.a().time());
3842
var factor = (animTime - (float) scalings.a().time()) / deltaTime;
39-
var start = new Vector3f(scalings.a().value());
40-
var end = new Vector3f(scalings.b().value());
41-
var delta = new Vector3f(end.sub(start));
42-
return out.add(start.add(delta.mul(factor)));
43+
var start = scalings.a().value();
44+
var end = scalings.b().value();
45+
return DEST_SCALE.set(end).sub(start).mul(factor).add(start);
4346
}
4447

4548
public static Pair<TransformStorage.TimeKey<Vector3f>, TransformStorage.TimeKey<Vector3f>> findPositions(float animTime, Animation.AnimationNode node) {
4649
for (var key : node.positionKeys) {
4750
if (animTime < key.time())
48-
return new Pair<>(node.positionKeys.getBefore(key), key);
51+
return VECTOR3F_KEYPAIR.set(node.positionKeys.getBefore(key), key);
4952
}
5053

51-
return new Pair<>(node.positionKeys.get(0), node.positionKeys.get(1));
54+
return VECTOR3F_KEYPAIR.set(node.positionKeys.get(0), node.positionKeys.get(1));
5255
}
5356

5457
public static Pair<TransformStorage.TimeKey<Quaternionf>, TransformStorage.TimeKey<Quaternionf>> findRotations(float animTime, Animation.AnimationNode node) {
5558
for (var key : node.rotationKeys) {
5659
if (animTime < key.time())
57-
return new Pair<>(node.rotationKeys.getBefore(key), key);
60+
return QUATERIONF_KEYPAIR.set(node.rotationKeys.getBefore(key), key);
5861
}
5962

60-
return new Pair<>(node.rotationKeys.get(0), node.rotationKeys.get(1));
63+
return QUATERIONF_KEYPAIR.set(node.rotationKeys.get(0), node.rotationKeys.get(1));
6164
}
6265

6366
public static Pair<TransformStorage.TimeKey<Vector3f>, TransformStorage.TimeKey<Vector3f>> findScalings(float animTime, Animation.AnimationNode node) {
6467
for (var key : node.scaleKeys) {
6568
if (animTime < key.time())
66-
return new Pair<>(node.scaleKeys.getBefore(key), key);
69+
return VECTOR3F_KEYPAIR.set(node.scaleKeys.getBefore(key), key);
6770
}
6871

69-
return new Pair<>(node.scaleKeys.get(0), node.scaleKeys.get(1));
72+
return VECTOR3F_KEYPAIR.set(node.scaleKeys.get(0), node.scaleKeys.get(1));
7073
}
7174
}

0 commit comments

Comments
 (0)