Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bedrock loader #8

Draft
wants to merge 27 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
86f862d
Parse bedrock model to java object
NeusFear Jul 13, 2024
04d47bb
Some utility constructors in tuples
NeusFear Jul 13, 2024
aac598f
Add model loading (not working, but not erroring either)
NeusFear Jul 14, 2024
d5d14ee
A lot closer to rendering correctly
NeusFear Jul 14, 2024
52a62b1
Flip normal U direction (wacky moment)
NeusFear Jul 14, 2024
4c917c8
Fix bottom face uv spacing
NeusFear Jul 14, 2024
c4a4176
Add cube rotations to model loader
NeusFear Jul 15, 2024
b691d5d
Fix a few bugs with mesh loading and rendering
NeusFear Jul 27, 2024
3baf900
Add back easing functions from TVE1
NeusFear Jul 27, 2024
6355d29
Add basic bedrock animation loader
NeusFear Jul 27, 2024
9e7107c
Most of the animation class started, need to simplify a lot still
NeusFear Jul 28, 2024
478d3a3
Simplify animation code a lot
NeusFear Jul 28, 2024
a33348c
Make keyframes only store a single transformation component
NeusFear Jul 28, 2024
01151ae
Parse most animation file data into keyframes directly
NeusFear Jul 29, 2024
d44c633
Probably bedrock animation data to animation converter
NeusFear Jul 29, 2024
3907ca4
Generate bone indexes on models
NeusFear Jul 30, 2024
6120533
Generate and pass a bone index map to the model and animations
NeusFear Jul 30, 2024
f739b05
Convert to MemoryUtil in models to solve out of memory errors
NeusFear Jul 30, 2024
da178f6
Fix a couple of bugs with meshes
NeusFear Jul 30, 2024
d9cce03
Add option to bedrock loader to compile to single mesh
NeusFear Jul 30, 2024
1eaf7ec
It runs but is not correct
NeusFear Jul 31, 2024
25032c2
Fix a few issues with animation rendering
NeusFear Aug 1, 2024
3652774
A few animation fixes, still not perfect
NeusFear Aug 15, 2024
f0c0aa0
Simplify bedrock loader (#9)
NeusFear Aug 23, 2024
8eeb6b7
Some attempts at fixing animations
NeusFear Sep 4, 2024
b3536f5
Merge branch 'master' into bedrock-loader
NeusFear Sep 15, 2024
e427a30
Merge branch 'master' into bedrock-loader
NeusFear Sep 23, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions gradleScripts/dependenciesCommon.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,6 @@ dependencies {
api "org.fusesource.jansi:jansi:${jansiVersion}"
api "org.joml:joml:${jomlVersion}"
api "com.electronwill.night-config:toml:${nightConfigVersion}"
api "com.electronwill.night-config:json:${nightConfigVersion}"
api "com.github.zafarkhaja:java-semver:${jsemverVersion}"
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
import com.terminalvelocitycabbage.engine.registry.Identifier;
import com.terminalvelocitycabbage.engine.util.ClassUtils;
import com.terminalvelocitycabbage.engine.util.Toggle;
import com.terminalvelocitycabbage.engine.util.touples.Pair;
import com.terminalvelocitycabbage.engine.util.tuples.Pair;
import com.terminalvelocitycabbage.templates.events.RenderGraphStageExecutionEvent;

import javax.management.ReflectionException;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
package com.terminalvelocitycabbage.engine.client.renderer.animation;

import com.terminalvelocitycabbage.engine.util.Easing;
import com.terminalvelocitycabbage.engine.util.tuples.Pair;
import com.terminalvelocitycabbage.engine.util.tuples.Triplet;
import org.joml.Vector3f;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class Animation {

long time; //The time that this animation has been playing
long currentTime;
final long startDelay;
final long animationLength;
final long loopDelay;
final long loopLength;

//Status
boolean isPlaying;
int playsRemaining; //-1 if indefinite
boolean startFromZero;

//Bone name, keyframe
private final Map<String, List<Keyframe>> keyframes;

public Animation(long startDelay, long animationLength, long loopDelay, Map<String, List<Keyframe>> keyframes) {
this.time = 0;
this.currentTime = 0;
this.startDelay = startDelay;
this.animationLength = animationLength;
this.loopDelay = loopDelay;
this.loopLength = animationLength + loopDelay;
this.isPlaying = false;
this.playsRemaining = 0;
this.startFromZero = false;
this.keyframes = keyframes;
}

public void update(long deltaTime) {

//Get the current position in the looping keyframe
if (isPlaying) {

time = startFromZero ? 0 : time + deltaTime;
currentTime = time - startDelay;

if (playsRemaining != -1 && currentTime > animationLength) {
playsRemaining = Math.max(playsRemaining - 1, 0);
}

if (playsRemaining == 0) {
currentTime = animationLength + startDelay;
} else {
currentTime %= loopLength;
}
}

startFromZero = false;
}

public void play() {
play(true);
}

public void play(boolean startOver) {
this.startFromZero = startOver;
isPlaying = true;
playsRemaining = 1;
}

public void repeat(int timesToRepeat) {
isPlaying = true;
playsRemaining = timesToRepeat;
}

public void loop() {
isPlaying = true;
playsRemaining = -1;
}

public void pause() {
isPlaying = false;
}

public void resume() {
isPlaying = true;
}

public void stop() {
isPlaying = false;
playsRemaining = 0;
time = 0;
currentTime = 0;
}

//Percentage through keyframe, the target transformation
public List<Keyframe> getCurrentKeyframes(String boneName) {

List<Keyframe> currentKeyFrames = new ArrayList<>();

for (Keyframe keyframe : keyframes.get(boneName)) {
if (currentTime > keyframe.startTime && currentTime < keyframe.endTime) currentKeyFrames.add(keyframe);
}

return currentKeyFrames;
}

//Bone name, transformations
Map<String, TransformationSnapshot> currentTransformations = new HashMap<>();
List<Pair<Float, Keyframe>> progressKeyframes = new ArrayList<>();
public Map<String, TransformationSnapshot> getCurrentTransformations() {
currentTransformations.clear();
progressKeyframes.clear();
for (String boneName : keyframes.keySet()) {
for (Keyframe keyframe : getCurrentKeyframes(boneName)) {
progressKeyframes.add(new Pair<>(getKeyframeProgress(keyframe), keyframe));
}
currentTransformations.put(boneName, getCurrentTransforms(progressKeyframes));
}

return currentTransformations;
}

/**
* Return the value from 0 to 1 that defines the progress between KS and KE for this animation
*
* AS = animation start
* KS = keyframe start time
* CT = currentTime
* KE = keyframe end time
* AS KS CT KE
* |-----------|----x--|-----------...
*
* to make the math easier we should just subtract everything by KS
* KS = KS - KS (0)
* CT = CT - KS
* KE = KE - AS
*
* percentage is just CT / KE so
* (CT - KS) / (KE - AS) = percentage
*
* @param keyframe The keyframe we want the progress of
* @return the progress from 0 to 1 of this keyframe
*/
float getKeyframeProgress(Keyframe keyframe) {
return (currentTime - keyframe.startTime) / (keyframe.endTime - keyframe.startTime);
}

TransformationSnapshot getCurrentTransforms(List<Pair<Float, Keyframe>> keyframes) {

//combine all keyframe transformations into one
Vector3f position = new Vector3f();
Vector3f rotation = new Vector3f();
Vector3f scale = new Vector3f(1);

for (Pair<Float, Keyframe> entry : keyframes) {
float progress = entry.getValue0();
Keyframe keyframe = entry.getValue1();
switch (keyframe.component) {
case POSITION -> interpolateComponent(position, progress, keyframe);
case ROTATION -> interpolateComponent(rotation, progress, keyframe);
case SCALE -> interpolateComponent(scale, progress, keyframe);
}
}

return new TransformationSnapshot(position, rotation, scale);
}

void interpolateComponent(Vector3f transformation, float progress, Keyframe keyframe) {
var xTransform = keyframe.endTransformation.x() == 0 ? keyframe.startTransformation.x() : Easing.easeInOut(keyframe.easingFunction, progress) * (keyframe.endTransformation.x() - keyframe.startTransformation.x());
var yTransform = keyframe.endTransformation.y() == 0 ? keyframe.startTransformation.y() : Easing.easeInOut(keyframe.easingFunction, progress) * (keyframe.endTransformation.y() - keyframe.startTransformation.y());
var zTransform = keyframe.endTransformation.z() == 0 ? keyframe.startTransformation.z() : Easing.easeInOut(keyframe.easingFunction, progress) * (keyframe.endTransformation.z() - keyframe.startTransformation.z());
transformation.add(xTransform, yTransform, zTransform);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package com.terminalvelocitycabbage.engine.client.renderer.animation;

import com.terminalvelocitycabbage.engine.client.renderer.model.Model;
import org.joml.Matrix4f;
import org.joml.Quaternionf;
import org.joml.Vector3f;

import java.util.HashMap;
import java.util.Map;

public class AnimationController {

//TODO replace string with identifier once animation registry exists
private final Map<String, Animation> animations;
private final Map<String, Model.Bone> bonesMap;
private final Map<Integer, Model.Bone> boneIndexMap;
private final Map<Integer, TransformationSnapshot> boneTransformations;
private final Map<Integer, Matrix4f> boneTransformationMatrices;

public AnimationController(Map<String, Animation> animations, Map<String, Model.Bone> bonesMap) {
this.animations = animations;
this.bonesMap = bonesMap;
boneIndexMap = new HashMap<>();
boneTransformations = new HashMap<>();
boneTransformationMatrices = new HashMap<>();
bonesMap.values().forEach(bone -> {
boneIndexMap.put(bone.getBoneIndex(), bone);
boneTransformations.put(bone.getBoneIndex(), new TransformationSnapshot(new Vector3f(), new Vector3f(), new Vector3f(1)));
boneTransformationMatrices.put(bone.getBoneIndex(), new Matrix4f());
});
}

public void update(long deltaTime, Model model) {
//Reset all transformations from last frame
boneTransformations.values().forEach(transformationSnapshot -> {
transformationSnapshot.position().zero();
transformationSnapshot.rotation().zero();
transformationSnapshot.scale().set(1);
});
boneTransformationMatrices.values().forEach(Matrix4f::identity);

//Loop through all animations update them and get their contribution to the bone transformations
for (Animation animation : animations.values()) {
animation.update(deltaTime);
//Get this animation's transformations add them together
animation.getCurrentTransformations().forEach(
(boneName, boneTransformation) -> {
var transformation = boneTransformations.get(bonesMap.get(boneName).getBoneIndex());
transformation.position().add(boneTransformation.position());
transformation.rotation().add(boneTransformation.rotation());
transformation.scale().mul(boneTransformation.scale());
}
);
}
//Convert all of these updated and combined transformations into a single transformation matrix for each bone
for (Map.Entry<Integer, Model.Bone> entry : boneIndexMap.entrySet()) {

var index = entry.getKey();
var bone = entry.getValue();

var boneTransformation = boneTransformations.get(index);
var eulerRotation = boneTransformation.rotation();
var rotation = new Quaternionf().rotateXYZ(
(float) Math.toRadians(eulerRotation.x),
(float) Math.toRadians(eulerRotation.y),
(float) Math.toRadians(eulerRotation.z)
);
boneTransformationMatrices.get(index)
.identity()
.scale(boneTransformation.scale())
.rotateAroundLocal(rotation, bone.getPivotPoint().x, bone.getPivotPoint().y, bone.getPivotPoint().z)
.translate(boneTransformation.position());
//.translate(bone.getOffset());
}
}

public Animation getAnimation(String animationName) {
return animations.get(animationName);
}

public void stopAll() {
animations.values().forEach(Animation::stop);
}

public Map<Integer, Matrix4f> getBoneTransformations() {
return boneTransformationMatrices;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package com.terminalvelocitycabbage.engine.client.renderer.animation;

import com.terminalvelocitycabbage.engine.util.Easing;
import org.joml.Vector3f;

/**
* Represents a portion of a transformation between two target transformations of a single component.
*/
public class Keyframe {

Component component;
Vector3f startTransformation;
Vector3f endTransformation;
Easing.Function easingFunction;
float startTime;
float endTime;

public Keyframe(Component component, Vector3f startTransformation, Vector3f endTransformation, Easing.Function easingFunction, float startTime, float endTime) {
this.component = component;
this.startTransformation = startTransformation;
this.endTransformation = endTransformation;
this.easingFunction = easingFunction;
this.startTime = startTime;
this.endTime = endTime;
}

public Component getComponent() {
return component;
}

public Vector3f getStartTransformation() {
return startTransformation;
}

public Vector3f getEndTransformation() {
return endTransformation;
}

public Easing.Function getEasingFunction() {
return easingFunction;
}

public float getStartTime() {
return startTime;
}

public float getEndTime() {
return endTime;
}

@Override
public String toString() {
return "Keyframe{" +
"component=" + component +
", startTransformation=" + startTransformation +
", endTransformation=" + endTransformation +
", easingFunction=" + easingFunction +
", startTime=" + startTime +
", endTime=" + endTime +
'}';
}

public enum Component {
POSITION,
ROTATION,
SCALE
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.terminalvelocitycabbage.engine.client.renderer.animation;

import org.joml.Vector3f;

public record TransformationSnapshot(Vector3f position, Vector3f rotation, Vector3f scale) {
}
Loading