diff --git a/src/main/java/com/terminalvelocitycabbage/engine/client/renderer/animation/Animation.java b/src/main/java/com/terminalvelocitycabbage/engine/client/renderer/animation/Animation.java index 8bb9ad4..28dcb61 100644 --- a/src/main/java/com/terminalvelocitycabbage/engine/client/renderer/animation/Animation.java +++ b/src/main/java/com/terminalvelocitycabbage/engine/client/renderer/animation/Animation.java @@ -110,9 +110,9 @@ public List getCurrentKeyframes(String boneName) { } //Bone name, transformations - Map> currentTransformations = new HashMap<>(); + Map currentTransformations = new HashMap<>(); List> progressKeyframes = new ArrayList<>(); - public Map> getCurrentTransformations() { + public Map getCurrentTransformations() { currentTransformations.clear(); progressKeyframes.clear(); for (String boneName : keyframes.keySet()) { @@ -150,7 +150,7 @@ float getKeyframeProgress(Keyframe keyframe) { return (currentTime - keyframe.startTime) / (keyframe.endTime - keyframe.startTime); } - Triplet getCurrentTransforms(List> keyframes) { + TransformationSnapshot getCurrentTransforms(List> keyframes) { //combine all keyframe transformations into one Vector3f position = new Vector3f(); @@ -167,7 +167,7 @@ Triplet getCurrentTransforms(List(position, rotation, scale); + return new TransformationSnapshot(position, rotation, scale); } void interpolateComponent(Vector3f transformation, float progress, Keyframe keyframe) { diff --git a/src/main/java/com/terminalvelocitycabbage/engine/client/renderer/animation/AnimationController.java b/src/main/java/com/terminalvelocitycabbage/engine/client/renderer/animation/AnimationController.java index a2c5456..b4d5ea3 100644 --- a/src/main/java/com/terminalvelocitycabbage/engine/client/renderer/animation/AnimationController.java +++ b/src/main/java/com/terminalvelocitycabbage/engine/client/renderer/animation/AnimationController.java @@ -1,6 +1,6 @@ package com.terminalvelocitycabbage.engine.client.renderer.animation; -import com.terminalvelocitycabbage.engine.util.tuples.Triplet; +import com.terminalvelocitycabbage.engine.client.renderer.model.Model; import org.joml.Matrix4f; import org.joml.Quaternionf; import org.joml.Vector3f; @@ -11,26 +11,31 @@ public class AnimationController { //TODO replace string with identifier once animation registry exists - Map animations; - private final Map boneIndexMap; - Map> boneTransformations; //Pos, Rot, Scale - Map boneTransformationMatrices; + private final Map animations; + private final Map bonesMap; + private final Map boneIndexMap; + private final Map boneTransformations; + private final Map boneTransformationMatrices; - public AnimationController(Map animations, Map boneIndexMap) { + public AnimationController(Map animations, Map bonesMap) { this.animations = animations; - this.boneIndexMap = boneIndexMap; + this.bonesMap = bonesMap; + boneIndexMap = new HashMap<>(); boneTransformations = new HashMap<>(); boneTransformationMatrices = new HashMap<>(); - boneIndexMap.values().forEach(boneIndex -> boneTransformations.put(boneIndex, new Triplet<>(new Vector3f(), new Vector3f(), new Vector3f(1)))); - boneIndexMap.values().forEach(boneIndex -> boneTransformationMatrices.put(boneIndex, new Matrix4f())); + 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) { + public void update(long deltaTime, Model model) { //Reset all transformations from last frame - boneTransformations.values().forEach(vector3fVector3fVector3fTriplet -> { - vector3fVector3fVector3fTriplet.getValue0().zero(); - vector3fVector3fVector3fTriplet.getValue1().zero(); - vector3fVector3fVector3fTriplet.getValue2().set(1); + boneTransformations.values().forEach(transformationSnapshot -> { + transformationSnapshot.position().zero(); + transformationSnapshot.rotation().zero(); + transformationSnapshot.scale().set(1); }); boneTransformationMatrices.values().forEach(Matrix4f::identity); @@ -40,20 +45,30 @@ public void update(long deltaTime) { //Get this animation's transformations add them together animation.getCurrentTransformations().forEach( (boneName, boneTransformation) -> { - boneTransformations.get(boneIndexMap.get(boneName)).getValue0().add(boneTransformation.getValue0()); - boneTransformations.get(boneIndexMap.get(boneName)).getValue1().add(boneTransformation.getValue1()); - boneTransformations.get(boneIndexMap.get(boneName)).getValue2().mul(boneTransformation.getValue2()); + 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 (int i = 0; i < boneTransformationMatrices.size(); i++) { - var boneTransformation = boneTransformations.get(i); - var eulerRotation = boneTransformation.getValue1(); - var rotation = new Quaternionf().rotateXYZ((float) Math.toRadians(eulerRotation.x), (float) Math.toRadians(eulerRotation.y), (float) Math.toRadians(eulerRotation.z)); - boneTransformationMatrices.get(i) + for (Map.Entry 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() - .translationRotateScale(boneTransformation.getValue0(), rotation, boneTransformation.getValue2()); + .translationRotateScale(boneTransformation.position(), rotation, boneTransformation.scale()); + //.translate(bone.getOffset()); } } diff --git a/src/main/java/com/terminalvelocitycabbage/engine/client/renderer/animation/TransformationSnapshot.java b/src/main/java/com/terminalvelocitycabbage/engine/client/renderer/animation/TransformationSnapshot.java new file mode 100644 index 0000000..8c19250 --- /dev/null +++ b/src/main/java/com/terminalvelocitycabbage/engine/client/renderer/animation/TransformationSnapshot.java @@ -0,0 +1,6 @@ +package com.terminalvelocitycabbage.engine.client.renderer.animation; + +import org.joml.Vector3f; + +public record TransformationSnapshot(Vector3f position, Vector3f rotation, Vector3f scale) { +} diff --git a/src/main/java/com/terminalvelocitycabbage/engine/client/renderer/animation/bedrock/BedrockAnimationData.java b/src/main/java/com/terminalvelocitycabbage/engine/client/renderer/animation/bedrock/BedrockAnimationData.java index 4356e94..5edc7b9 100644 --- a/src/main/java/com/terminalvelocitycabbage/engine/client/renderer/animation/bedrock/BedrockAnimationData.java +++ b/src/main/java/com/terminalvelocitycabbage/engine/client/renderer/animation/bedrock/BedrockAnimationData.java @@ -65,7 +65,7 @@ public AnimationController toAnimationController(Model model) { convertedAnimations.put(animationName, animation); } - return new AnimationController(convertedAnimations, model.getBoneIndexMap()); + return new AnimationController(convertedAnimations, model.getBones()); } public record AnimationData ( @@ -173,7 +173,7 @@ private static List parseTransformationData(Keyframe.Component compone previousKeyframeEndTransformation = previousKeyframe.getEndTransformation(); previousKeyframeEndTime = previousKeyframe.getEndTime(); } - var endTimeMillis = Float.parseFloat(endTimeSeconds) * 1000; + var endTimeMillis = Float.parseFloat(endTimeSeconds) * 1000f; //Create the keyframes if (keyframeConfigOrTransformation.toString().startsWith("[")) { //linear is simplified out in bbmodel diff --git a/src/main/java/com/terminalvelocitycabbage/engine/client/renderer/model/Model.java b/src/main/java/com/terminalvelocitycabbage/engine/client/renderer/model/Model.java index a77bd34..3688087 100644 --- a/src/main/java/com/terminalvelocitycabbage/engine/client/renderer/model/Model.java +++ b/src/main/java/com/terminalvelocitycabbage/engine/client/renderer/model/Model.java @@ -1,55 +1,42 @@ package com.terminalvelocitycabbage.engine.client.renderer.model; import com.terminalvelocitycabbage.engine.client.renderer.elements.VertexFormat; +import com.terminalvelocitycabbage.engine.debug.Log; import org.joml.Quaternionf; import org.joml.Vector3f; -import java.util.ArrayList; -import java.util.List; import java.util.Map; public class Model { VertexFormat format; - List parts; - Map boneIndexMap; - boolean compiledMesh; + Map parts; Mesh mesh; - public Model(VertexFormat format, List parts, Map boneIndexMap, Mesh mesh) { - this(format, parts, boneIndexMap); - this.mesh = mesh; - this.compiledMesh = true; - } - - public Model(VertexFormat format, List parts, Map boneIndexMap) { + public Model(VertexFormat format, Map bones, Mesh mesh) { this.format = format; - this.parts = parts; - this.boneIndexMap = boneIndexMap; - this.compiledMesh = false; + this.parts = bones; + this.parts.values().forEach(bone -> bone.model = this); + this.mesh = mesh; } public void render() { - if (compiledMesh) { - mesh.render(); - } else { - for (Part part : parts) { - part.render(); - } - } + mesh.render(); } public void cleanup() { - for (Part part : parts) { - part.cleanup(); - } + mesh.cleanup(); } public VertexFormat getFormat() { return format; } - public List getParts() { + public Bone getBone(String partName) { + return parts.get(partName); + } + + public Map getBones() { return parts; } @@ -57,71 +44,53 @@ public Mesh getMesh() { return mesh; } - public Map getBoneIndexMap() { - return boneIndexMap; - } + public static class Bone { - public static class Part { + Model model; String name; - Part parent; - List children; - Mesh mesh; + String parentName; int boneIndex; boolean dirty; - Vector3f pivotPoint; - Vector3f origin; + Vector3f offset; Quaternionf rotation; Vector3f scale; - public Part(String name, Part parent, Mesh mesh, Vector3f pivotPoint, Quaternionf rotation, int boneIndex) { + public Bone(String name, String parentName, Vector3f pivotPoint, Quaternionf rotation, Vector3f scale, int boneIndex) { this.name = name; - this.parent = parent; - this.children = new ArrayList<>(); - this.mesh = mesh; + this.parentName = parentName; this.dirty = true; - this.pivotPoint = pivotPoint; + this.offset = pivotPoint; this.rotation = rotation; + this.scale = scale; this.boneIndex = boneIndex; } - public void render() { - if (mesh != null) mesh.render(); - for (Part child : children) { - child.render(); - } + public String getParentName() { + return parentName; } - public void cleanup() { - mesh.cleanup(); - for (Part child : children) { - child.cleanup(); - } - } - - public void addChild(Part child) { - children.add(child); - } - - public Part getParent() { - return parent; - } - - public List getChildren() { - return children; + public String getName() { + return name; } - public Mesh getMesh() { - return mesh; + //TODO cache this offset so we don't query every frame, do this on model instantiation + public Vector3f getOffset() { + Vector3f retOffset = new Vector3f(offset); + var parent = model.getBone(parentName); + if (parent != null) { + retOffset.add(parent.getOffset(), retOffset); + } + return retOffset; } - public String getName() { - return name; + public int getBoneIndex() { + return boneIndex; } } diff --git a/src/main/java/com/terminalvelocitycabbage/engine/client/renderer/model/bedrock/BedrockModelData.java b/src/main/java/com/terminalvelocitycabbage/engine/client/renderer/model/bedrock/BedrockModelData.java index 0cf166f..9ef48b0 100644 --- a/src/main/java/com/terminalvelocitycabbage/engine/client/renderer/model/bedrock/BedrockModelData.java +++ b/src/main/java/com/terminalvelocitycabbage/engine/client/renderer/model/bedrock/BedrockModelData.java @@ -46,13 +46,15 @@ private static class BedrockCube { int[] uv; float[] pivot; float[] rotation; + float[] scale; - public BedrockCube(float[] origin, int[] size, int[] uv, float[] pivot, float[] rotation) { + public BedrockCube(float[] origin, int[] size, int[] uv, float[] pivot, float[] rotation, float[] scale) { this.origin = origin; this.size = size; this.uv = uv; this.pivot = pivot; this.rotation = rotation; + this.scale = scale; } /** @@ -233,13 +235,15 @@ private static class BedrockBone { String parent; float[] pivot; float[] rotation; + float[] scale; BedrockCube[] cubes; - public BedrockBone(String name, String parent, float[] pivot, float[] rotation, BedrockCube[] cubes) { + public BedrockBone(String name, String parent, float[] pivot, float[] rotation, float[] scale, BedrockCube[] cubes) { this.name = name; this.parent = parent; this.pivot = pivot; this.rotation = rotation; + this.scale = scale; this.cubes = cubes; } @@ -250,6 +254,7 @@ public String toString() { ", parent='" + parent + '\'' + ", pivot=" + Arrays.toString(pivot) + ", rotation=" + Arrays.toString(rotation) + + ", scale=" + Arrays.toString(scale) + ", cubes=" + Arrays.toString(cubes) + '}'; } @@ -327,8 +332,9 @@ private static BedrockBone parseBone(Config config) { String parent = config.getOrElse("parent", "none"); float[] pivot = ConfigUtils.numberListToFloatArray(config.get("pivot")); float[] rotation = ConfigUtils.numberListToFloatArray(config.get("rotation")); + float[] scale = ConfigUtils.numberListToFloatArray(config.get("scale")); - return new BedrockBone(name, parent, pivot, rotation, parseCubes(config)); + return new BedrockBone(name, parent, pivot, rotation, scale, parseCubes(config)); } private static BedrockCube[] parseCubes(Config config) { @@ -348,9 +354,10 @@ private static BedrockCube parseCube(Config cube) { int[] uv = ConfigUtils.numberListToIntArray(cube.get("uv")); float[] pivot = ConfigUtils.numberListToFloatArray(cube.get("pivot")); float[] rotation = ConfigUtils.numberListToFloatArray(cube.get("rotation")); - //TODO inflate + //TODO verify inflate + float[] scale = ConfigUtils.numberListToFloatArray(cube.get("inflate")); - return new BedrockCube(origin, size, uv, pivot, rotation); + return new BedrockCube(origin, size, uv, pivot, rotation, scale); } private static BedrockGeometryDescription parseGeometryDescription(Config config) { @@ -367,75 +374,38 @@ private static BedrockGeometryDescription parseGeometryDescription(Config config } - private record StagedModelPart(String name, String parentName, List children, Mesh mesh, float[] bonePivot, float[] boneRotation, int boneIndex) { } - - public Model toModel(boolean compileMesh) { + public Model toModel() { //The information needed to create a model part extracted from all bones - Map partsStaging = new HashMap<>(); + List meshesToCompile = new ArrayList<>(); + Map modelBones = new HashMap<>(); //Loop through all bones to get staging data for a model part - Map boneIndexMap = new HashMap<>(); for (int i = 0; i < bones.length; i++) { + //This iteration's bone BedrockBone bone = bones[i]; - List meshes = new ArrayList<>(); + + //Get all the meshes from these bones and add them to a list for later compilation for (BedrockCube cube : bone.cubes) { //Convert cubes to meshes - meshes.add(cube.toMesh(geometryDescription.textureWidth, geometryDescription.textureHeight, i)); - } - partsStaging.put(bone.name, new StagedModelPart(bone.name, bone.parent, new ArrayList<>(), Mesh.of(meshes), bone.pivot, bone.rotation, i)); - boneIndexMap.put(bone.name, i); - } - - //Assign children to all staged model parts - List roots = new ArrayList<>(); - for (StagedModelPart part : partsStaging.values()) { - if (!part.parentName().equals("none")) { - partsStaging.get(part.parentName()).children().add(part.name()); - } else { - roots.add(part); + meshesToCompile.add(cube.toMesh(geometryDescription.textureWidth, geometryDescription.textureHeight, i)); } - } - //Construct this model from these bones into model parts - List parts = new ArrayList<>(); - for (StagedModelPart part : roots) { + //Default Values var partPivot = new Vector3f(); var partRotation = new Quaternionf(); - if (part.bonePivot().length > 0) partPivot.add(part.bonePivot()[0], part.bonePivot()[1], part.bonePivot()[2]); - if (part.boneRotation().length > 0) partRotation.rotateXYZ(part.boneRotation()[0], part.boneRotation()[1], part.boneRotation()[2]); - var newPart = new Model.Part(part.name(), null, part.mesh(), partPivot, partRotation, part.boneIndex()); - parts.add(newPart); - addChildren(partsStaging, newPart); + var partScale = new Vector3f(1); + //Fill values if they exist + if (bone.pivot.length > 0) partPivot.set(bone.pivot[0], bone.pivot[1], bone.pivot[2]); + if (bone.rotation.length > 0) partRotation.rotateXYZ(bone.rotation[0], bone.rotation[1], bone.rotation[2]); + if (bone.scale.length > 0) partScale.set(bone.scale[0], bone.scale[1], bone.scale[2]); + //Create the part and add it to the list of parts + var newPart = new Model.Bone(bone.name, bone.parent, partPivot, partRotation, partScale, i); + modelBones.put(newPart.getName(), newPart); } - if (compileMesh) { - List meshesToCompile = new ArrayList<>(); - partsStaging.values().forEach((stagedModelPart) -> { - if (stagedModelPart.mesh().getFormat() != null) { - meshesToCompile.add(stagedModelPart.mesh()); - } else { - Log.info("null format: " + stagedModelPart.name()); - } - }); - return new Model(BEDROCK_VERTEX_FORMAT, parts, boneIndexMap, Mesh.of(meshesToCompile)); - } else { - return new Model(BEDROCK_VERTEX_FORMAT, parts, boneIndexMap); - } - } - - private void addChildren(Map boneMap, Model.Part part) { - var childrenNames = boneMap.get(part.getName()).children(); - for (String childName : childrenNames) { - var stagedPart = boneMap.get(childName); - var partPivot = new Vector3f(); - var partRotation = new Quaternionf(); - if (stagedPart.bonePivot().length > 0) partPivot.add(stagedPart.bonePivot()[0], stagedPart.bonePivot()[1], stagedPart.bonePivot()[2]); - if (stagedPart.boneRotation().length > 0) partRotation.rotateXYZ(stagedPart.boneRotation()[0], stagedPart.boneRotation()[1], stagedPart.boneRotation()[2]); - var childPart = new Model.Part(childName, part, stagedPart.mesh(), partPivot, partRotation, stagedPart.boneIndex()); - part.addChild(childPart); - addChildren(boneMap, childPart); - } + //Create a model from all the parts and meshes + return new Model(BEDROCK_VERTEX_FORMAT, modelBones, Mesh.of(meshesToCompile)); } public void print() {