Skip to content

Commit 6b60fbe

Browse files
committed
chore: simplify skinned mesh component
1 parent 50f619e commit 6b60fbe

3 files changed

Lines changed: 59 additions & 134 deletions

File tree

engine/src/main/java/org/terasology/engine/rendering/assets/animation/MeshAnimation.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,4 +28,8 @@ protected MeshAnimation(ResourceUrn urn, AssetType<?, MeshAnimationData> assetTy
2828
public abstract float getTimePerFrame();
2929

3030
public abstract AABBf getAabb();
31+
32+
public float getDuration() {
33+
return getTimePerFrame() * (getFrameCount() - 1);
34+
}
3135
}

engine/src/main/java/org/terasology/engine/rendering/logic/SkinnedMeshComponent.java

Lines changed: 10 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33

44
package org.terasology.engine.rendering.logic;
55

6-
import com.google.common.collect.Lists;
76
import com.google.common.collect.Maps;
7+
import org.joml.Quaternionf;
88
import org.joml.Vector3f;
99
import org.terasology.engine.entitySystem.Owns;
1010
import org.terasology.engine.entitySystem.entity.EntityRef;
@@ -14,9 +14,7 @@
1414
import org.terasology.engine.rendering.assets.mesh.SkinnedMesh;
1515
import org.terasology.engine.world.block.ForceBlockActive;
1616
import org.terasology.nui.Color;
17-
import org.terasology.nui.properties.Range;
1817

19-
import java.util.List;
2018
import java.util.Map;
2119

2220
@ForceBlockActive
@@ -27,55 +25,35 @@ public class SkinnedMeshComponent implements VisualComponent<SkinnedMeshComponen
2725
@Replicate
2826
public Material material;
2927

30-
/**
31-
* Should not be set manually. Stores the data of the selected animation variation.
32-
*/
3328
@Replicate
3429
public MeshAnimation animation;
3530

36-
/**
37-
* If true, an animation from {@link #animationPool} will be played when the current animation is done.
38-
*/
39-
@Replicate
40-
public boolean loop;
41-
42-
/**
43-
* When the current animation is done and loop is true then a random animation will be picked from this pool of
44-
* animations.
45-
*/
46-
@Replicate
47-
public List<MeshAnimation> animationPool = Lists.newArrayList();
48-
49-
public float animationRate = 1.0f;
50-
@Range(min = -2.5f, max = 2.5f)
51-
public float heightOffset;
31+
public float currentTime;
5232

5333
@Owns
5434
public Map<String, EntityRef> boneEntities;
5535
public EntityRef rootBone = EntityRef.NULL;
56-
public float animationTime;
5736

5837
@Replicate
59-
public Vector3f scale = new Vector3f(1, 1, 1);
38+
public float localScale = 1.0f;
39+
@Replicate
40+
public Vector3f localOffset = new Vector3f();
6041
@Replicate
61-
public Vector3f translate = new Vector3f();
42+
public Quaternionf localRotation = new Quaternionf();
6243

6344
@Replicate
64-
public Color color = Color.WHITE;
45+
public Color color = new Color(Color.white);
6546

6647
@Override
6748
public void copyFrom(SkinnedMeshComponent other) {
6849
this.mesh = other.mesh;
6950
this.material = other.material;
7051
this.animation = other.animation;
71-
this.loop = other.loop;
72-
this.animationPool = Lists.newArrayList(other.animationPool);
73-
this.animationRate = other.animationRate;
7452
this.boneEntities = Maps.newHashMap(other.boneEntities);
7553
this.rootBone = other.rootBone;
76-
this.animationTime = other.animationTime;
77-
this.scale = new Vector3f(other.scale);
78-
this.translate = new Vector3f(other.translate);
54+
this.localScale = other.localScale;
55+
this.localOffset.set(other.localOffset);
56+
this.localRotation.set(other.localRotation);
7957
this.color = new Color(other.color);
8058
}
8159
}

engine/src/main/java/org/terasology/engine/rendering/logic/SkinnedMeshRenderer.java

Lines changed: 45 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
import org.terasology.engine.entitySystem.entity.EntityManager;
1818
import org.terasology.engine.entitySystem.entity.EntityRef;
1919
import org.terasology.engine.entitySystem.entity.lifecycleEvents.OnActivatedComponent;
20-
import org.terasology.engine.entitySystem.event.ReceiveEvent;
2120
import org.terasology.engine.entitySystem.systems.BaseComponentSystem;
2221
import org.terasology.engine.entitySystem.systems.RegisterMode;
2322
import org.terasology.engine.entitySystem.systems.RegisterSystem;
@@ -37,12 +36,12 @@
3736
import org.terasology.engine.rendering.world.WorldRenderer;
3837
import org.terasology.engine.utilities.Assets;
3938
import org.terasology.gestalt.assets.management.AssetManager;
39+
import org.terasology.gestalt.entitysystem.event.ReceiveEvent;
4040
import org.terasology.joml.geom.AABBf;
4141
import org.terasology.joml.geom.AABBfc;
4242
import org.terasology.nui.Color;
4343

4444
import java.nio.FloatBuffer;
45-
import java.util.List;
4645
import java.util.Random;
4746

4847
/**
@@ -130,94 +129,68 @@ private void updateSkeletalMeshOfEntity(EntityRef entity, float delta) {
130129
return;
131130
}
132131

133-
if (skeletalMeshComp.animation == null && skeletalMeshComp.animationPool != null) {
134-
skeletalMeshComp.animation = randomAnimationData(skeletalMeshComp, random);
135-
}
136-
137132
if (skeletalMeshComp.animation == null) {
138133
return;
139134
}
140135

141136
if (skeletalMeshComp.animation.getFrameCount() < 1) {
142137
return;
143138
}
144-
skeletalMeshComp.animationTime += delta * skeletalMeshComp.animationRate;
145-
float animationDuration = getDurationOfAnimation(skeletalMeshComp);
146-
while (skeletalMeshComp.animationTime >= animationDuration) {
147-
MeshAnimation newAnimation;
148-
if (!skeletalMeshComp.loop) {
149-
newAnimation = null;
150-
} else if (skeletalMeshComp.animationPool != null && !skeletalMeshComp.animationPool.isEmpty()) {
151-
newAnimation = randomAnimationData(skeletalMeshComp, random);
152-
} else {
153-
newAnimation = skeletalMeshComp.animation;
154-
}
155139

156-
if (newAnimation == null) {
157-
MeshAnimation finishedAnimation = skeletalMeshComp.animation;
158-
skeletalMeshComp.animationTime = animationDuration;
159-
MeshAnimationFrame frame = skeletalMeshComp.animation.getFrame(skeletalMeshComp.animation.getFrameCount() - 1);
160-
updateSkeleton(skeletalMeshComp, frame, frame, 1.0f);
161-
// Set animation to null so that AnimEndEvent fires only once
162-
skeletalMeshComp.animation = null;
163-
entity.saveComponent(skeletalMeshComp);
164-
entity.send(new AnimEndEvent(finishedAnimation));
165-
return;
140+
if (skeletalMeshComp.rootBone != null) {
141+
LocationComponent locationComponent = skeletalMeshComp.rootBone.getComponent(LocationComponent.class);
142+
if (locationComponent != null) {
143+
locationComponent.setLocalPosition(skeletalMeshComp.localOffset);
144+
locationComponent.setLocalScale(skeletalMeshComp.localScale);
145+
locationComponent.setLocalRotation(skeletalMeshComp.localRotation);
166146
}
167-
skeletalMeshComp.animationTime -= animationDuration;
168-
if (skeletalMeshComp.animationTime < 0) {
169-
// In case the float calculation wasn't exact:
170-
skeletalMeshComp.animationTime = 0;
147+
}
148+
149+
float animationDuration = skeletalMeshComp.animation.getDuration();
150+
if (skeletalMeshComp.currentTime >= animationDuration) {
151+
skeletalMeshComp.currentTime -= animationDuration;
152+
if (skeletalMeshComp.currentTime < 0) {
153+
skeletalMeshComp.currentTime = 0;
171154
}
172-
skeletalMeshComp.animation = newAnimation;
173-
animationDuration = getDurationOfAnimation(skeletalMeshComp);
174155
}
175-
float framePos = skeletalMeshComp.animationTime / skeletalMeshComp.animation.getTimePerFrame();
176-
int frameAId = (int) framePos;
177-
int frameBId = frameAId + 1;
178-
if (frameBId >= skeletalMeshComp.animation.getFrameCount()) {
179-
// In case the float calcuation wasn't exact:
180-
frameBId = skeletalMeshComp.animation.getFrameCount() - 1;
156+
157+
float framePos = skeletalMeshComp.currentTime / skeletalMeshComp.animation.getTimePerFrame();
158+
int currentFrame = (int) framePos;
159+
int nextFrame = currentFrame + 1;
160+
if (nextFrame >= skeletalMeshComp.animation.getFrameCount()) {
161+
nextFrame = 0;
181162
}
182-
MeshAnimationFrame frameA = skeletalMeshComp.animation.getFrame(frameAId);
183-
MeshAnimationFrame frameB = skeletalMeshComp.animation.getFrame(frameBId);
184-
updateSkeleton(skeletalMeshComp, frameA, frameB, framePos - frameAId);
163+
float frameDelta = framePos - currentFrame;
164+
MeshAnimationFrame animatedFrame1 = skeletalMeshComp.animation.getFrame(currentFrame);
165+
MeshAnimationFrame animatedFrame2 = skeletalMeshComp.animation.getFrame(nextFrame);
166+
updateFrame(skeletalMeshComp, animatedFrame1, animatedFrame2, frameDelta);
185167
entity.saveComponent(skeletalMeshComp);
186168
}
187169

188-
private float getDurationOfAnimation(SkinnedMeshComponent skeletalMeshComp) {
189-
return skeletalMeshComp.animation.getTimePerFrame() * (skeletalMeshComp.animation.getFrameCount() - 1);
190-
}
170+
private void updateFrame(SkinnedMeshComponent skeletalMeshComp, MeshAnimationFrame frameA, MeshAnimationFrame frameB,
171+
float interpolationVal) {
191172

192-
private static MeshAnimation randomAnimationData(SkinnedMeshComponent skeletalMeshComp, Random random) {
193-
List<MeshAnimation> animationPool = skeletalMeshComp.animationPool;
194-
if (animationPool == null) {
195-
return null;
196-
}
197-
if (animationPool.isEmpty()) {
198-
return null;
199-
}
200-
return animationPool.get(random.nextInt(animationPool.size()));
201-
}
202173

203-
private void updateSkeleton(SkinnedMeshComponent skeletalMeshComp, MeshAnimationFrame frameA, MeshAnimationFrame frameB,
204-
float interpolationVal) {
205174
for (int i = 0; i < skeletalMeshComp.animation.getBoneCount(); ++i) {
206175
String boneName = skeletalMeshComp.animation.getBoneName(i);
207176
EntityRef boneEntity = skeletalMeshComp.boneEntities.get(boneName);
208177
if (boneEntity == null) {
209178
continue;
210179
}
180+
211181
LocationComponent boneLoc = boneEntity.getComponent(LocationComponent.class);
212-
if (boneLoc != null) {
213-
Vector3f newPos = frameA.getPosition(i).lerp(frameB.getPosition(i), interpolationVal, new Vector3f());
214-
boneLoc.setLocalPosition(newPos);
215-
Quaternionf newRot = frameA.getRotation(i).slerp(frameB.getRotation(i), interpolationVal, new Quaternionf());
216-
newRot.normalize();
217-
boneLoc.setLocalRotation(newRot);
218-
boneLoc.setLocalScale(frameA.getBoneScale(i).lerp(frameB.getBoneScale(i), interpolationVal, new Vector3f()).x);
219-
boneEntity.saveComponent(boneLoc);
182+
if (boneLoc == null) {
183+
continue;
220184
}
185+
186+
Vector3f newPos = frameA.getPosition(i).lerp(frameB.getPosition(i), interpolationVal, new Vector3f());
187+
boneLoc.setLocalPosition(newPos);
188+
Quaternionf newRot = frameA.getRotation(i).slerp(frameB.getRotation(i), interpolationVal, new Quaternionf());
189+
newRot.normalize();
190+
boneLoc.setLocalRotation(newRot);
191+
boneLoc.setLocalScale(frameA.getBoneScale(i).lerp(frameB.getBoneScale(i), interpolationVal, new Vector3f()).x);
192+
boneEntity.saveComponent(boneLoc);
193+
221194
}
222195
}
223196

@@ -257,10 +230,10 @@ public void renderOpaque() {
257230
aabb = aabb.transform(new Matrix4f().translationRotateScale(worldPos, worldRot, worldScale), new AABBf());
258231

259232
//Scale bounding box for skeletalMesh.
260-
Vector3f scale = skeletalMesh.scale;
233+
float scale = skeletalMesh.localScale;
261234

262235
Vector3f aabbCenter = aabb.center(new Vector3f());
263-
Vector3f scaledExtents = aabb.extent(new Vector3f()).mul(scale.x(), scale.y(), scale.z());
236+
Vector3f scaledExtents = aabb.extent(new Vector3f()).mul(scale, scale, scale);
264237
aabb = new AABBf(aabbCenter, aabbCenter).expand(scaledExtents);
265238

266239
if (!worldRenderer.getActiveCamera().hasInSight(aabb)) {
@@ -279,7 +252,6 @@ public void renderOpaque() {
279252
Vector3f worldPositionCameraSpace = new Vector3f();
280253
worldPos.sub(cameraPosition, worldPositionCameraSpace);
281254

282-
worldPositionCameraSpace.y += skeletalMesh.heightOffset;
283255
Matrix4f matrixCameraSpace = new Matrix4f().translationRotateScale(worldPositionCameraSpace, worldRot, worldScale);
284256

285257
Matrix4f modelViewMatrix = worldRenderer.getActiveCamera().getViewMatrix().mul(matrixCameraSpace, new Matrix4f());
@@ -292,25 +264,21 @@ public void renderOpaque() {
292264
skeletalMesh.material.setFloat("sunlight", worldRenderer.getMainLightIntensityAt(worldPos), true);
293265
skeletalMesh.material.setFloat("blockLight", worldRenderer.getBlockLightIntensityAt(worldPos), true);
294266

295-
Matrix4f[] boneTransforms = new Matrix4f[skeletalMesh.mesh.bones().size()];
267+
Matrix4f boneTransform = new Matrix4f();
296268
for (Bone bone : skeletalMesh.mesh.bones()) {
297269
EntityRef boneEntity = skeletalMesh.boneEntities.get(bone.getName());
298270
if (boneEntity == null) {
299271
boneEntity = EntityRef.NULL;
300272
}
301273
LocationComponent boneLocation = boneEntity.getComponent(LocationComponent.class);
274+
boneTransform.identity();
302275
if (boneLocation != null) {
303-
Matrix4f boneTransform = new Matrix4f();
304276
boneLocation.getRelativeTransform(boneTransform, entity);
305277
boneTransform.mul(bone.getInverseBindMatrix());
306-
boneTransforms[bone.getIndex()] = boneTransform;
307278
} else {
308279
logger.warn("Unable to resolve bone \"{}\"", bone.getName());
309-
boneTransforms[bone.getIndex()] = new Matrix4f();
310280
}
311-
}
312-
for (int i = 0; i < boneTransforms.length; i++) {
313-
skeletalMesh.material.setMatrix4("boneTransforms[" + i + "]", boneTransforms[i], true);
281+
skeletalMesh.material.setMatrix4("boneTransforms[" + bone.getIndex() + "]", boneTransform, true);
314282
}
315283
skeletalMesh.mesh.render();
316284
}
@@ -327,11 +295,7 @@ public void renderOverlay() {
327295

328296
Vector3f cameraPosition = worldRenderer.getActiveCamera().getPosition();
329297

330-
Matrix4f relMat = new Matrix4f();
331-
Matrix4f relFinal = new Matrix4f();
332298
Matrix4f entityTransform = new Matrix4f();
333-
334-
Matrix4f result = new Matrix4f();
335299
Vector3f currentPos = new Vector3f();
336300

337301
int index = 0;
@@ -349,10 +313,6 @@ public void renderOverlay() {
349313
// position is referenced around (0,0,0) (worldposition - cameraposition)
350314
Vector3f worldPositionCameraSpace = cameraPosition.negate(new Vector3f());
351315

352-
// same heightOffset is applied to worldPositionCameraSpace from #renderOpaque()
353-
// TODO: resolve repeated logic for transformation applied to bones
354-
worldPositionCameraSpace.y += skeletalMesh.heightOffset;
355-
356316
Matrix4f matrixCameraSpace = new Matrix4f().translationRotateScale(worldPositionCameraSpace, new Quaternionf(), 1.0f);
357317
Matrix4f modelViewMatrix = new Matrix4f(worldRenderer.getActiveCamera().getViewMatrix()).mul(matrixCameraSpace);
358318
material.setMatrix4("projectionMatrix", worldRenderer.getActiveCamera().getProjectionMatrix());
@@ -370,27 +330,10 @@ public void renderOverlay() {
370330
LocationComponent locCompA = boneEntity.getComponent(LocationComponent.class);
371331
LocationComponent locCompB = boneParentEntity.getComponent(LocationComponent.class);
372332

373-
// need to calculate the relative transformation from the entity to the start of the bone
374-
locCompA.getRelativeTransform(relMat.identity(), entity);
375-
// entityTransform * (scale, translation) * relativeMat * [x,y,z,1]
376-
result.set(entityTransform)
377-
.mul(relFinal.identity()
378-
.scale(skeletalMesh.scale)
379-
.translate(skeletalMesh.translate)
380-
.mul(relMat))
381-
.transformPosition(currentPos.zero()); // get the position of the start of the bone
333+
locCompA.getWorldPosition(currentPos);
382334
meshData.position.put(currentPos); // the start of the bone
383335

384-
// need to calculate the relative transformation from the entity to the connecting bone
385-
locCompB.getRelativeTransform(relMat.identity(), entity);
386-
// entityTransform * (scale, translation) * relativeMat * [x,y,z,1]
387-
result.set(entityTransform)
388-
.mul(relFinal
389-
.identity()
390-
.scale(skeletalMesh.scale)
391-
.translate(skeletalMesh.translate)
392-
.mul(relMat))
393-
.transformPosition(currentPos.zero()); // get the position to the connecting bone
336+
locCompB.getWorldPosition(currentPos);
394337
meshData.position.put(currentPos); // the end of the bone
395338

396339
meshData.color0.put(Color.white);

0 commit comments

Comments
 (0)