diff --git a/core/build.gradle b/core/build.gradle index f1fe073..c18f6af 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -12,6 +12,7 @@ dependencies { implementation api("org.slf4j:slf4j-api:2.0.17") implementation api("it.unimi.dsi:fastutil:8.5.18") implementation api("io.netty:netty-buffer:4.2.7.Final") + implementation api("org.joml:joml:1.10.8") // Testing testImplementation("org.junit.jupiter:junit-jupiter-api:6.0.1") diff --git a/core/src/main/java/com/zigythebird/playeranimcore/loading/AnimationLoader.java b/core/src/main/java/com/zigythebird/playeranimcore/loading/AnimationLoader.java index cdaa0c6..eeed226 100644 --- a/core/src/main/java/com/zigythebird/playeranimcore/loading/AnimationLoader.java +++ b/core/src/main/java/com/zigythebird/playeranimcore/loading/AnimationLoader.java @@ -291,7 +291,7 @@ private static List> getEasingArgsForAxis(JsonObject entryObj, easingArg; } - private static List addArgsForKeyframes(List frames) { + public static List addArgsForKeyframes(List frames) { if (frames.getFirst().startValue().getFirst() instanceof AccessExpression accessExpression && "disabled".equals(accessExpression.property()) && accessExpression.object() instanceof IdentifierExpression identifierExpression && "pal".equals(identifierExpression.name())) diff --git a/core/src/main/java/com/zigythebird/playeranimcore/loading/BlockyAnimLoader.java b/core/src/main/java/com/zigythebird/playeranimcore/loading/BlockyAnimLoader.java new file mode 100644 index 0000000..14c6e56 --- /dev/null +++ b/core/src/main/java/com/zigythebird/playeranimcore/loading/BlockyAnimLoader.java @@ -0,0 +1,180 @@ +package com.zigythebird.playeranimcore.loading; + +import com.google.gson.*; +import com.zigythebird.playeranimcore.animation.Animation; +import com.zigythebird.playeranimcore.animation.ExtraAnimationData; +import com.zigythebird.playeranimcore.animation.keyframe.BoneAnimation; +import com.zigythebird.playeranimcore.animation.keyframe.Keyframe; +import com.zigythebird.playeranimcore.animation.keyframe.KeyframeStack; +import com.zigythebird.playeranimcore.easing.EasingType; +import com.zigythebird.playeranimcore.enums.TransformType; +import com.zigythebird.playeranimcore.math.Vec3f; +import com.zigythebird.playeranimcore.molang.MolangLoader; +import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import org.joml.Quaternionf; +import org.joml.Vector3f; +import team.unnamed.mocha.parser.ast.Expression; +import team.unnamed.mocha.parser.ast.FloatExpression; + +import java.lang.reflect.Type; +import java.util.*; + +import static com.zigythebird.playeranimcore.loading.UniversalAnimLoader.NO_KEYFRAMES; +import static java.util.Map.entry; + +public class BlockyAnimLoader implements JsonDeserializer { + public static final Gson GSON = new GsonBuilder().registerTypeAdapter(Animation.class, new BlockyAnimLoader()).create(); + + private static final float TIME_SCALE = 20.0f / 60.0f; + + private static final Map RENAMES = Map.of( + "head", "headhy" + ); + + private static final float SCALE = 24.0f / 87.0f; + + private static final Map PIVOTS = Map.ofEntries( + // Body + entry("origin", new Vec3f(0, 0, 0)), + entry("pelvis", new Vec3f(0, 51 * SCALE, 0)), + entry("belly", new Vec3f(0, 55 * SCALE, 0)), + entry("chest", new Vec3f(0, 68 * SCALE, -3 * SCALE)), + + // Head + entry("headhy", new Vec3f(0, 87 * SCALE, -1 * SCALE)), + + // Right Arm + entry("r-shoulder", new Vec3f(14.5f * SCALE, 86.6f * SCALE, -0.94f * SCALE)), + entry("r-arm", new Vec3f(14.5f * SCALE, 86.6f * SCALE, -0.94f * SCALE)), + + // Left Arm + entry("l-shoulder", new Vec3f(-14.5f * SCALE, 86.6f * SCALE, -0.94f * SCALE)), + entry("l-arm", new Vec3f(-14.5f * SCALE, 86.6f * SCALE, -0.94f * SCALE)), + + // Right Leg + entry("r-thigh", new Vec3f(7.5f * SCALE, 50 * SCALE, 1 * SCALE)), + + // Left Leg + entry("l-thigh", new Vec3f(-7.5f * SCALE, 50 * SCALE, 1 * SCALE)), + + // Cape + entry("back-attachment", new Vec3f(0, 79 * SCALE, -18 * SCALE)) + ); + private static final Map PARENTS = Map.ofEntries( + // Body + entry("pelvis", "origin"), + entry("belly", "pelvis"), + entry("chest", "belly"), + entry("body", "pelvis"), // Minecraft + + // Torso + entry("torso", "chest"), // Minecraft TODO + + // Head + entry("headhy", "chest"), + entry("head", "headhy"), // Minecraft + + // Right Arm + entry("r-shoulder", "chest"), + entry("r-arm", "r-shoulder"), + entry("right_arm", "r-arm"), // Minecraft + + // Left Arm + entry("l-shoulder", "chest"), + entry("l-arm", "l-shoulder"), + entry("left_arm", "l-arm"), // Minecraft + + // Right Leg + entry("r-thigh", "pelvis"), + entry("right_leg", "r-thigh"), // Minecraft + + // Left Leg + entry("l-thigh", "pelvis"), + entry("left_leg", "l-thigh"), // Minecraft + + // Cape + entry("back-attachment", "chest"), + entry("cape", "back-attachment") // Minecraft + ); + /*private static final Map BENDS = Map.of( + "r-forearm", "right_arm", + "l-forearm", "left_arm", + "r-calf", "right_leg", + "l-calf", "left_leg" + );*/ + + @Override + public Animation deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { + JsonObject root = json.getAsJsonObject(); + float duration = root.has("duration") ? root.get("duration").getAsFloat() * TIME_SCALE : 0; + boolean loop = root.has("holdLastKeyframe") && root.get("holdLastKeyframe").getAsBoolean(); + + Map animations = new HashMap<>(); + + if (root.has("nodeAnimations")) { + for (Map.Entry entry : root.getAsJsonObject("nodeAnimations").entrySet()) { + String src = entry.getKey().toLowerCase(Locale.ROOT); + JsonObject data = entry.getValue().getAsJsonObject(); + + KeyframeStack rot = parseStack(data.getAsJsonArray("orientation"), TransformType.ROTATION); + KeyframeStack pos = parseStack(data.getAsJsonArray("position"), TransformType.POSITION); + KeyframeStack scl = parseStack(data.getAsJsonArray("shapeStretch"), TransformType.SCALE); + animations.put(RENAMES.getOrDefault(src, src), new BoneAnimation(rot, pos, scl, new ArrayList<>())); + } + } + + return new Animation(new ExtraAnimationData(), duration, loop ? Animation.LoopType.HOLD_ON_LAST_FRAME : Animation.LoopType.PLAY_ONCE, animations, NO_KEYFRAMES, PIVOTS, PARENTS); + } + + private KeyframeStack parseStack(JsonArray json, TransformType type) { + if (json == null || json.isEmpty()) return new KeyframeStack(); + + List xFrames = new ObjectArrayList<>(); + List yFrames = new ObjectArrayList<>(); + List zFrames = new ObjectArrayList<>(); + + List xPrev = null; + List yPrev = null; + List zPrev = null; + float prevTime = 0; + + Expression defaultValue = type == TransformType.SCALE ? FloatExpression.ONE : FloatExpression.ZERO; + + for (JsonElement element : json) { + JsonObject keyframe = element.getAsJsonObject(); + JsonObject delta = keyframe.getAsJsonObject("delta"); + + float curTime = keyframe.get("time").getAsFloat() * TIME_SCALE; + float timeDelta = curTime - prevTime; + + List xValue, yValue, zValue; + if (type == TransformType.ROTATION) { + Vector3f vector3f = new Quaternionf( + delta.get("x").getAsFloat(), delta.get("y").getAsFloat(), delta.get("z").getAsFloat(), delta.get("w").getAsFloat() + ).normalize().getEulerAnglesZYX(new Vector3f()); + + xValue = Collections.singletonList(FloatExpression.of(vector3f.x())); + yValue = Collections.singletonList(FloatExpression.of(-vector3f.y())); + zValue = Collections.singletonList(FloatExpression.of(-vector3f.z())); + } else { + xValue = MolangLoader.parseJson(false, delta.get("x"), defaultValue); + yValue = MolangLoader.parseJson(false, delta.get("y"), defaultValue); + zValue = MolangLoader.parseJson(false, delta.get("z"), defaultValue); + } + + EasingType easingType = keyframe.has("interpolationType") && keyframe.get("interpolationType").getAsString().equals("smooth") ? EasingType.CATMULLROM : EasingType.LINEAR; + xFrames.add(new Keyframe(timeDelta, xPrev == null ? xValue : xPrev, xValue, easingType, Collections.emptyList())); + yFrames.add(new Keyframe(timeDelta, yPrev == null ? yValue : yPrev, yValue, easingType, Collections.emptyList())); + zFrames.add(new Keyframe(timeDelta, zPrev == null ? zValue : zPrev, zValue, easingType, Collections.emptyList())); + + xPrev = xValue; yPrev = yValue; zPrev = zValue; + prevTime = curTime; + } + + return new KeyframeStack( + AnimationLoader.addArgsForKeyframes(xFrames), + AnimationLoader.addArgsForKeyframes(yFrames), + AnimationLoader.addArgsForKeyframes(zFrames) + ); + } +} diff --git a/core/src/main/java/com/zigythebird/playeranimcore/loading/UniversalAnimLoader.java b/core/src/main/java/com/zigythebird/playeranimcore/loading/UniversalAnimLoader.java index 7f5b8c8..ec8929c 100644 --- a/core/src/main/java/com/zigythebird/playeranimcore/loading/UniversalAnimLoader.java +++ b/core/src/main/java/com/zigythebird/playeranimcore/loading/UniversalAnimLoader.java @@ -11,6 +11,7 @@ import com.zigythebird.playeranimcore.util.JsonUtil; import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.io.IOException; import java.io.InputStream; @@ -31,13 +32,21 @@ public class UniversalAnimLoader implements JsonDeserializer loadAnimations(InputStream resource) throws IOException { + return loadAnimations(resource, null); + } + + public static Map loadAnimations(InputStream resource, @Nullable String name) throws IOException { try (Reader reader = new InputStreamReader(resource)) { - return loadAnimations(PlayerAnimLib.GSON.fromJson(reader, JsonObject.class)); + return loadAnimations(PlayerAnimLib.GSON.fromJson(reader, JsonObject.class), name); } } public static Map<@NotNull String, Animation> loadAnimations(JsonObject json) { - if (json.has("animations")) { + return loadAnimations(json, null); + } + + public static Map<@NotNull String, Animation> loadAnimations(JsonObject json, @Nullable String name) { + if (json.has("animations")) { // Bedrock Map animationMap = PlayerAnimLib.GSON.fromJson(json.get("animations"), PlayerAnimLib.ANIMATIONS_MAP_TYPE); if (json.has("parents") && json.has("model")) { Map parents = UniversalAnimLoader.getParents(JsonUtil.getAsJsonObject(json, "parents", new JsonObject())); @@ -52,9 +61,15 @@ public static Map loadAnimations(InputStream resource) throws } } return animationMap; - } else { + } else if (json.has("nodeAnimations")) { // Hytale + Animation animation = BlockyAnimLoader.GSON.fromJson(json, Animation.class); + if (name != null && !animation.data().has(ExtraAnimationData.NAME_KEY)) animation.data().put(ExtraAnimationData.NAME_KEY, name); + return Collections.singletonMap(animation.getNameOrId(), animation); + } else if (json.has("emote")) { // PlayerAnimator Animation animation = PlayerAnimatorLoader.GSON.fromJson(json, Animation.class); return Collections.singletonMap(animation.getNameOrId(), animation); + } else { + throw new JsonParseException("Unsupported format!"); } } diff --git a/gradle.properties b/gradle.properties index 39a29da..da27a7a 100644 --- a/gradle.properties +++ b/gradle.properties @@ -3,7 +3,7 @@ org.gradle.jvmargs=-Xmx2G org.gradle.parallel=true # Mod properties -mod_version = 1.1.5 +mod_version = 1.1.5+dev maven_group = com.zigythebird.playeranim archives_base_name = PlayerAnimationLib diff --git a/minecraft/common/src/main/java/com/zigythebird/playeranim/animation/PlayerAnimResources.java b/minecraft/common/src/main/java/com/zigythebird/playeranim/animation/PlayerAnimResources.java index 647f9e6..238aa81 100644 --- a/minecraft/common/src/main/java/com/zigythebird/playeranim/animation/PlayerAnimResources.java +++ b/minecraft/common/src/main/java/com/zigythebird/playeranim/animation/PlayerAnimResources.java @@ -8,6 +8,7 @@ import net.minecraft.resources.Identifier; import net.minecraft.server.packs.resources.ResourceManager; import net.minecraft.server.packs.resources.ResourceManagerReloadListener; +import org.apache.commons.io.FilenameUtils; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -83,10 +84,13 @@ public static boolean hasAnimation(Identifier id) { public void onResourceManagerReload(ResourceManager manager) { ANIMATIONS.clear(); - for (var resource : manager.listResources("player_animations", resourceLocation -> resourceLocation.getPath().endsWith(".json")).entrySet()) { + for (var resource : manager.listResources("player_animations", identifier -> { + String path = identifier.getPath(); + return path.endsWith(".json") || path.endsWith(".blockyanim"); + }).entrySet()) { String namespace = resource.getKey().getNamespace(); try (InputStream is = resource.getValue().open()) { - for (var entry : UniversalAnimLoader.loadAnimations(is).entrySet()) { + for (var entry : UniversalAnimLoader.loadAnimations(is, FilenameUtils.getBaseName(resource.getKey().getPath())).entrySet()) { ANIMATIONS.put(Identifier.fromNamespaceAndPath(namespace, entry.getKey()), entry.getValue()); } } catch (Exception e) { diff --git a/minecraft/common/src/main/resources/assets/player_animation_library/player_animations/wavehy.blockyanim b/minecraft/common/src/main/resources/assets/player_animation_library/player_animations/wavehy.blockyanim new file mode 100644 index 0000000..7a60945 --- /dev/null +++ b/minecraft/common/src/main/resources/assets/player_animation_library/player_animations/wavehy.blockyanim @@ -0,0 +1,1117 @@ +{ + "duration": 120, + "holdLastKeyframe": false, + "nodeAnimations": { + "L-Arm": { + "position": [], + "orientation": [ { + "time": 0, + "delta": { + "x": 0.026375, + "y": -0.020314, + "z": 0.017938, + "w": 0.999285 + }, + "interpolationType": "smooth" + }, { + "time": 20, + "delta": { + "x": 0.022799, + "y": -0.020589, + "z": 0.04251, + "w": 0.998624 + }, + "interpolationType": "smooth" + }, { + "time": 40, + "delta": { + "x": 0.019718, + "y": -0.019145, + "z": 0.011385, + "w": 0.999557 + }, + "interpolationType": "smooth" + }, { + "time": 60, + "delta": { + "x": 0.022799, + "y": -0.020589, + "z": 0.04251, + "w": 0.998624 + }, + "interpolationType": "smooth" + }, { + "time": 80, + "delta": { + "x": 0.019308, + "y": -0.019025, + "z": 0.020558, + "w": 0.999421 + }, + "interpolationType": "smooth" + }, { + "time": 100, + "delta": { + "x": 0.021965, + "y": -0.018989, + "z": 0.045559, + "w": 0.99854 + }, + "interpolationType": "smooth" + } ], + "shapeStretch": [], + "shapeVisible": [], + "shapeUvOffset": [] + }, + "R-Arm": { + "position": [], + "orientation": [ { + "time": 0, + "delta": { + "x": 0.025941, + "y": 0.020268, + "z": -0.01793, + "w": 0.999297 + }, + "interpolationType": "smooth" + }, { + "time": 15, + "delta": { + "x": -0.700871, + "y": -0.152173, + "z": -0.397702, + "w": 0.572237 + }, + "interpolationType": "smooth" + }, { + "time": 30, + "delta": { + "x": -0.47296, + "y": -0.407951, + "z": -0.380433, + "w": 0.682023 + }, + "interpolationType": "smooth" + }, { + "time": 45, + "delta": { + "x": 0.72567, + "y": 0.180487, + "z": 0.490968, + "w": -0.446965 + }, + "interpolationType": "smooth" + }, { + "time": 60, + "delta": { + "x": 0.58103, + "y": 0.339661, + "z": 0.494003, + "w": -0.55045 + }, + "interpolationType": "smooth" + }, { + "time": 75, + "delta": { + "x": 0.72079, + "y": 0.191678, + "z": 0.471805, + "w": -0.470235 + }, + "interpolationType": "smooth" + }, { + "time": 90, + "delta": { + "x": -0.494255, + "y": -0.395636, + "z": -0.405003, + "w": 0.659664 + }, + "interpolationType": "smooth" + }, { + "time": 105, + "delta": { + "x": 0.682615, + "y": 0.243175, + "z": 0.463967, + "w": -0.509546 + }, + "interpolationType": "smooth" + } ], + "shapeStretch": [], + "shapeVisible": [], + "shapeUvOffset": [] + }, + "R-Forearm": { + "position": [], + "orientation": [ { + "time": 0, + "delta": { + "x": -0.070992, + "y": 0, + "z": 0, + "w": 0.997477 + }, + "interpolationType": "smooth" + }, { + "time": 15, + "delta": { + "x": 0.279945, + "y": 0, + "z": 0, + "w": -0.960016 + }, + "interpolationType": "smooth" + }, { + "time": 30, + "delta": { + "x": -0.065403, + "y": 0, + "z": 0, + "w": 0.997859 + }, + "interpolationType": "smooth" + }, { + "time": 45, + "delta": { + "x": -0.371541, + "y": 0, + "z": 0, + "w": 0.928417 + }, + "interpolationType": "smooth" + }, { + "time": 60, + "delta": { + "x": -0.087156, + "y": 0, + "z": 0, + "w": 0.996195 + }, + "interpolationType": "smooth" + }, { + "time": 75, + "delta": { + "x": -0.371541, + "y": 0, + "z": 0, + "w": 0.928417 + }, + "interpolationType": "smooth" + }, { + "time": 90, + "delta": { + "x": -0.087156, + "y": 0, + "z": 0, + "w": 0.996195 + }, + "interpolationType": "smooth" + }, { + "time": 105, + "delta": { + "x": -0.371541, + "y": 0, + "z": 0, + "w": 0.928417 + }, + "interpolationType": "smooth" + } ], + "shapeStretch": [], + "shapeVisible": [], + "shapeUvOffset": [] + }, + "L-Forearm": { + "position": [], + "orientation": [ { + "time": 20, + "delta": { + "x": -0.065403, + "y": 0, + "z": 0, + "w": 0.997859 + }, + "interpolationType": "smooth" + }, { + "time": 80, + "delta": { + "x": -0.087156, + "y": 0, + "z": 0, + "w": 0.996195 + }, + "interpolationType": "smooth" + } ], + "shapeStretch": [], + "shapeVisible": [], + "shapeUvOffset": [] + }, + "Cape1": { + "position": [], + "orientation": [ { + "time": 0, + "delta": { + "x": 0.023376, + "y": 0, + "z": 0, + "w": 0.999727 + }, + "interpolationType": "smooth" + }, { + "time": 60, + "delta": { + "x": -0.020254, + "y": 0, + "z": 0, + "w": 0.999795 + }, + "interpolationType": "smooth" + } ], + "shapeStretch": [], + "shapeVisible": [], + "shapeUvOffset": [] + }, + "Cape2": { + "position": [], + "orientation": [ { + "time": 0, + "delta": { + "x": -0.043619, + "y": 0, + "z": 0, + "w": 0.999048 + }, + "interpolationType": "smooth" + }, { + "time": 60, + "delta": { + "x": 0.021815, + "y": 0, + "z": 0, + "w": 0.999762 + }, + "interpolationType": "smooth" + } ], + "shapeStretch": [], + "shapeVisible": [], + "shapeUvOffset": [] + }, + "Cape3": { + "position": [], + "orientation": [ { + "time": 0, + "delta": { + "x": -0.043619, + "y": 0, + "z": 0, + "w": 0.999048 + }, + "interpolationType": "smooth" + }, { + "time": 60, + "delta": { + "x": 0.021815, + "y": 0, + "z": 0, + "w": 0.999762 + }, + "interpolationType": "smooth" + } ], + "shapeStretch": [], + "shapeVisible": [], + "shapeUvOffset": [] + }, + "Chest": { + "position": [], + "orientation": [ { + "time": 5, + "delta": { + "x": 0.043619, + "y": 0, + "z": 0, + "w": 0.999048 + }, + "interpolationType": "smooth" + }, { + "time": 25, + "delta": { + "x": 0.039037, + "y": 0.001479, + "z": -0.03784, + "w": 0.99852 + }, + "interpolationType": "smooth" + }, { + "time": 45, + "delta": { + "x": 0.043619, + "y": 0, + "z": 0, + "w": 0.999048 + }, + "interpolationType": "smooth" + }, { + "time": 65, + "delta": { + "x": 0.039037, + "y": 0.001479, + "z": -0.03784, + "w": 0.99852 + }, + "interpolationType": "smooth" + }, { + "time": 85, + "delta": { + "x": 0.043619, + "y": 0, + "z": 0, + "w": 0.999048 + }, + "interpolationType": "smooth" + }, { + "time": 105, + "delta": { + "x": 0.039037, + "y": 0.001479, + "z": -0.03784, + "w": 0.99852 + }, + "interpolationType": "smooth" + } ], + "shapeStretch": [], + "shapeVisible": [], + "shapeUvOffset": [] + }, + "Belly": { + "position": [], + "orientation": [ { + "time": 0, + "delta": { + "x": -0.021815, + "y": 0, + "z": 0, + "w": 0.999762 + }, + "interpolationType": "smooth" + }, { + "time": 20, + "delta": { + "x": -0.023754, + "y": -0.000666, + "z": -0.02801, + "w": 0.999325 + }, + "interpolationType": "smooth" + }, { + "time": 40, + "delta": { + "x": -0.025814, + "y": -0.0001, + "z": -0.004205, + "w": 0.999658 + }, + "interpolationType": "smooth" + }, { + "time": 60, + "delta": { + "x": -0.023754, + "y": -0.000666, + "z": -0.02801, + "w": 0.999325 + }, + "interpolationType": "smooth" + }, { + "time": 80, + "delta": { + "x": -0.025814, + "y": -0.0001, + "z": -0.004205, + "w": 0.999658 + }, + "interpolationType": "smooth" + }, { + "time": 100, + "delta": { + "x": -0.023754, + "y": -0.000666, + "z": -0.02801, + "w": 0.999325 + }, + "interpolationType": "smooth" + } ], + "shapeStretch": [], + "shapeVisible": [], + "shapeUvOffset": [] + }, + "Head": { + "position": [], + "orientation": [ { + "time": 10, + "delta": { + "x": 0.017452, + "y": 0.000015, + "z": -0.000837, + "w": 0.999847 + }, + "interpolationType": "smooth" + }, { + "time": 30, + "delta": { + "x": 0.017452, + "y": -0.000103, + "z": 0.005919, + "w": 0.99983 + }, + "interpolationType": "smooth" + }, { + "time": 50, + "delta": { + "x": 0.017447, + "y": 0.000418, + "z": -0.02395, + "w": 0.999561 + }, + "interpolationType": "smooth" + }, { + "time": 70, + "delta": { + "x": 0.017452, + "y": -0.000103, + "z": 0.005919, + "w": 0.99983 + }, + "interpolationType": "smooth" + }, { + "time": 90, + "delta": { + "x": 0.017447, + "y": 0.000418, + "z": -0.02395, + "w": 0.999561 + }, + "interpolationType": "smooth" + }, { + "time": 110, + "delta": { + "x": 0.017452, + "y": -0.000103, + "z": 0.005919, + "w": 0.99983 + }, + "interpolationType": "smooth" + } ], + "shapeStretch": [], + "shapeVisible": [], + "shapeUvOffset": [] + }, + "R-Thigh": { + "position": [], + "orientation": [ { + "time": 0, + "delta": { + "x": -0.021637, + "y": -0.006414, + "z": -0.026289, + "w": 0.9994 + }, + "interpolationType": "smooth" + }, { + "time": 60, + "delta": { + "x": 0.000169, + "y": -0.00546, + "z": -0.026107, + "w": 0.999644 + }, + "interpolationType": "smooth" + } ], + "shapeStretch": [], + "shapeVisible": [], + "shapeUvOffset": [] + }, + "L-Thigh": { + "position": [], + "orientation": [ { + "time": 0, + "delta": { + "x": -0.021842, + "y": -0.000416, + "z": 0.026079, + "w": 0.999421 + }, + "interpolationType": "smooth" + }, { + "time": 60, + "delta": { + "x": -0.000035, + "y": -0.000986, + "z": 0.026142, + "w": 0.999658 + }, + "interpolationType": "smooth" + } ], + "shapeStretch": [], + "shapeVisible": [], + "shapeUvOffset": [] + }, + "L-Foot": { + "position": [ { + "time": 0, + "delta": { + "x": 0, + "y": 0, + "z": 0 + }, + "interpolationType": "smooth" + }, { + "time": 60, + "delta": { + "x": -0.006238, + "y": -0.003309, + "z": -0.380529 + }, + "interpolationType": "smooth" + } ], + "orientation": [ { + "time": 0, + "delta": { + "x": -0.02617, + "y": 0.000074, + "z": -0.026157, + "w": 0.999315 + }, + "interpolationType": "smooth" + }, { + "time": 60, + "delta": { + "x": -0.000013, + "y": 0.001519, + "z": -0.026107, + "w": 0.999658 + }, + "interpolationType": "smooth" + } ], + "shapeStretch": [], + "shapeVisible": [], + "shapeUvOffset": [] + }, + "R-Foot": { + "position": [ { + "time": 0, + "delta": { + "x": 0, + "y": 0, + "z": 0 + }, + "interpolationType": "smooth" + }, { + "time": 60, + "delta": { + "x": 0.006238, + "y": -0.00331, + "z": -0.38053 + }, + "interpolationType": "smooth" + } ], + "orientation": [ { + "time": 0, + "delta": { + "x": -0.026145, + "y": 0.002574, + "z": 0.026069, + "w": 0.999315 + }, + "interpolationType": "smooth" + }, { + "time": 60, + "delta": { + "x": 0.000013, + "y": 0.001519, + "z": 0.026107, + "w": 0.999658 + }, + "interpolationType": "smooth" + } ], + "shapeStretch": [], + "shapeVisible": [], + "shapeUvOffset": [] + }, + "L_Side_Cloth_1": { + "position": [], + "orientation": [ { + "time": 0, + "delta": { + "x": 0.000485, + "y": 0.001803, + "z": 0.001082, + "w": 0.999998 + }, + "interpolationType": "smooth" + }, { + "time": 60, + "delta": { + "x": 0.00009, + "y": 0.000337, + "z": -0.000009, + "w": 1 + }, + "interpolationType": "smooth" + } ], + "shapeStretch": [], + "shapeVisible": [], + "shapeUvOffset": [] + }, + "R_Side_Cloth_1": { + "position": [], + "orientation": [ { + "time": 0, + "delta": { + "x": 0.000575, + "y": -0.002132, + "z": -0.00151, + "w": 0.999996 + }, + "interpolationType": "smooth" + }, { + "time": 60, + "delta": { + "x": 0.00009, + "y": -0.000337, + "z": 0.000009, + "w": 1 + }, + "interpolationType": "smooth" + } ], + "shapeStretch": [], + "shapeVisible": [], + "shapeUvOffset": [] + }, + "Back_Cloth_1": { + "position": [], + "orientation": [ { + "time": 0, + "delta": { + "x": 0, + "y": 0, + "z": 0, + "w": 1 + }, + "interpolationType": "smooth" + }, { + "time": 60, + "delta": { + "x": 0, + "y": 0, + "z": 0, + "w": 1 + }, + "interpolationType": "smooth" + } ], + "shapeStretch": [], + "shapeVisible": [], + "shapeUvOffset": [] + }, + "Back_Cloth_2": { + "position": [], + "orientation": [ { + "time": 0, + "delta": { + "x": -0.087156, + "y": 0, + "z": 0, + "w": 0.996195 + }, + "interpolationType": "smooth" + } ], + "shapeStretch": [], + "shapeVisible": [], + "shapeUvOffset": [] + }, + "Front_Cloth_1": { + "position": [], + "orientation": [ { + "time": 0, + "delta": { + "x": 0, + "y": 0, + "z": 0, + "w": 1 + }, + "interpolationType": "smooth" + }, { + "time": 60, + "delta": { + "x": 0, + "y": 0, + "z": 0, + "w": 1 + }, + "interpolationType": "smooth" + } ], + "shapeStretch": [], + "shapeVisible": [], + "shapeUvOffset": [] + }, + "Front_Cloth_2": { + "position": [], + "orientation": [ { + "time": 0, + "delta": { + "x": 0.087156, + "y": 0, + "z": 0, + "w": 0.996195 + }, + "interpolationType": "smooth" + } ], + "shapeStretch": [], + "shapeVisible": [], + "shapeUvOffset": [] + }, + "R-Hand": { + "position": [], + "orientation": [ { + "time": 30, + "delta": { + "x": -0.041636, + "y": 0.045437, + "z": 0.045437, + "w": 0.997064 + }, + "interpolationType": "smooth" + }, { + "time": 90, + "delta": { + "x": -0.06338, + "y": 0.046335, + "z": 0.046335, + "w": 0.995836 + }, + "interpolationType": "smooth" + } ], + "shapeStretch": [], + "shapeVisible": [], + "shapeUvOffset": [] + }, + "L-Hand": { + "position": [], + "orientation": [ { + "time": 40, + "delta": { + "x": -0.041636, + "y": -0.045437, + "z": -0.045437, + "w": 0.997064 + }, + "interpolationType": "smooth" + }, { + "time": 100, + "delta": { + "x": -0.06338, + "y": -0.046335, + "z": -0.046335, + "w": 0.995836 + }, + "interpolationType": "smooth" + } ], + "shapeStretch": [], + "shapeVisible": [], + "shapeUvOffset": [] + }, + "R_Side_Cloth_Side_R": { + "position": [], + "orientation": [ { + "time": 0, + "delta": { + "x": 0, + "y": 0.130526, + "z": 0, + "w": 0.991445 + }, + "interpolationType": "smooth" + } ], + "shapeStretch": [], + "shapeVisible": [], + "shapeUvOffset": [] + }, + "L_Side_Cloth_Side_L": { + "position": [], + "orientation": [ { + "time": 0, + "delta": { + "x": 0, + "y": -0.130526, + "z": 0, + "w": 0.991445 + }, + "interpolationType": "smooth" + } ], + "shapeStretch": [], + "shapeVisible": [], + "shapeUvOffset": [] + }, + "L_Side_Cloth_Side_R": { + "position": [], + "orientation": [ { + "time": 0, + "delta": { + "x": 0, + "y": 0.130526, + "z": 0, + "w": 0.991445 + }, + "interpolationType": "smooth" + } ], + "shapeStretch": [], + "shapeVisible": [], + "shapeUvOffset": [] + }, + "R_Side_Cloth_Side_L": { + "position": [], + "orientation": [ { + "time": 0, + "delta": { + "x": 0, + "y": -0.130526, + "z": 0, + "w": 0.991445 + }, + "interpolationType": "smooth" + } ], + "shapeStretch": [], + "shapeVisible": [], + "shapeUvOffset": [] + }, + "Back_Cloth_R": { + "position": [], + "orientation": [ { + "time": 0, + "delta": { + "x": 0, + "y": 0, + "z": 0, + "w": 1 + }, + "interpolationType": "smooth" + } ], + "shapeStretch": [], + "shapeVisible": [], + "shapeUvOffset": [] + }, + "Back_Cloth_L": { + "position": [], + "orientation": [ { + "time": 0, + "delta": { + "x": 0, + "y": 0, + "z": 0, + "w": 1 + }, + "interpolationType": "smooth" + } ], + "shapeStretch": [], + "shapeVisible": [], + "shapeUvOffset": [] + }, + "Front_Cloth_R": { + "position": [], + "orientation": [ { + "time": 0, + "delta": { + "x": 0, + "y": 0, + "z": 0, + "w": 1 + }, + "interpolationType": "smooth" + } ], + "shapeStretch": [], + "shapeVisible": [], + "shapeUvOffset": [] + }, + "Front_Cloth_L": { + "position": [], + "orientation": [ { + "time": 0, + "delta": { + "x": 0, + "y": 0, + "z": 0, + "w": 1 + }, + "interpolationType": "smooth" + } ], + "shapeStretch": [], + "shapeVisible": [], + "shapeUvOffset": [] + }, + "L_Side_Cloth_2": { + "position": [], + "orientation": [ { + "time": 0, + "delta": { + "x": 0.002847, + "y": -0.021628, + "z": -0.043609, + "w": 0.99881 + }, + "interpolationType": "smooth" + } ], + "shapeStretch": [], + "shapeVisible": [], + "shapeUvOffset": [] + }, + "R_Side_Cloth_2": { + "position": [], + "orientation": [ { + "time": 0, + "delta": { + "x": 0.002847, + "y": 0.021628, + "z": 0.043609, + "w": 0.99881 + }, + "interpolationType": "smooth" + } ], + "shapeStretch": [], + "shapeVisible": [], + "shapeUvOffset": [] + }, + "R-Eyebrow": { + "position": [ { + "time": 0, + "delta": { + "x": 0, + "y": 1, + "z": 0 + }, + "interpolationType": "smooth" + } ], + "orientation": [ { + "time": 0, + "delta": { + "x": 0, + "y": 0, + "z": 0.043619, + "w": 0.999048 + }, + "interpolationType": "smooth" + } ], + "shapeStretch": [], + "shapeVisible": [], + "shapeUvOffset": [] + }, + "L-Eyebrow": { + "position": [ { + "time": 0, + "delta": { + "x": 0, + "y": 1, + "z": 0 + }, + "interpolationType": "smooth" + } ], + "orientation": [ { + "time": 0, + "delta": { + "x": 0, + "y": 0, + "z": -0.043619, + "w": 0.999048 + }, + "interpolationType": "smooth" + } ], + "shapeStretch": [], + "shapeVisible": [], + "shapeUvOffset": [] + }, + "Mouth": { + "position": [], + "orientation": [], + "shapeStretch": [], + "shapeVisible": [], + "shapeUvOffset": [ { + "time": 0, + "delta": { + "x": 0, + "y": -10 + } + } ] + }, + "R-Calf": { + "position": [], + "orientation": [ { + "time": 0, + "delta": { + "x": 0.043619, + "y": 0, + "z": 0, + "w": 0.999048 + }, + "interpolationType": "smooth" + }, { + "time": 60, + "delta": { + "x": 0, + "y": 0, + "z": 0, + "w": 1 + }, + "interpolationType": "smooth" + } ], + "shapeStretch": [], + "shapeVisible": [], + "shapeUvOffset": [] + }, + "L-Calf": { + "position": [], + "orientation": [ { + "time": 0, + "delta": { + "x": 0.043619, + "y": 0, + "z": 0, + "w": 0.999048 + }, + "interpolationType": "smooth" + }, { + "time": 60, + "delta": { + "x": 0, + "y": 0, + "z": 0, + "w": 1 + }, + "interpolationType": "smooth" + } ], + "shapeStretch": [], + "shapeVisible": [], + "shapeUvOffset": [] + }, + "Pelvis": { + "position": [ { + "time": 0, + "delta": { + "x": 0, + "y": -0.925, + "z": -0.15 + }, + "interpolationType": "smooth" + }, { + "time": 60, + "delta": { + "x": 0, + "y": -0.5, + "z": 0 + }, + "interpolationType": "smooth" + } ], + "orientation": [], + "shapeStretch": [], + "shapeVisible": [], + "shapeUvOffset": [] + } + }, + "formatVersion": 1 +} \ No newline at end of file