diff --git a/frontend/apps/editor3d/app/src/pages/PageEnigma/Editor/KinHelpers/BoneJSONHelper.ts b/frontend/apps/editor3d/app/src/pages/PageEnigma/Editor/KinHelpers/BoneJSONHelper.ts new file mode 100644 index 0000000000..4ee8cce220 --- /dev/null +++ b/frontend/apps/editor3d/app/src/pages/PageEnigma/Editor/KinHelpers/BoneJSONHelper.ts @@ -0,0 +1,76 @@ +import { Object3D, ObjectLoader } from "three"; + +export class BoneJSONHelper { + + model: Object3D; + + constructor(model: Object3D) { + this.model = model; + } + + private findHipBone(objectGroup: Object3D[]): Object3D | undefined { + let hipBone = undefined; + for (const child of objectGroup) { + if (child.type === "Bone" && child.name.toLowerCase().includes("mixamorighips")) { + hipBone = child; + break; + } + } + + return hipBone; + } + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + public toJSON(): any { + if (!this.model.userData.isCharacter) { + return; + } + + if (this.model.children.length < 1) { + return undefined; + } + + // Find the hip bone + const hipBone = this.findHipBone(this.model.children[0].children); + + if (!hipBone) { + console.error("No hip bone found in the model."); + return undefined; + } + + return hipBone.toJSON(); + } + + /* Applies the bone tree in a recursive BFS manner */ + private applyBoneTree(hipBone: Object3D, boneTree: Object3D): void { + hipBone.position.copy(boneTree.position); + hipBone.quaternion.copy(boneTree.quaternion); + hipBone.scale.copy(boneTree.scale); + + const hipChildren = hipBone.children; + const treeChildren = boneTree.children; + + // NOTE: Assumption made that the lengths match! + // This shouldn't be a problem unless the skeletal model is changed + for (let i = 0; i < hipChildren.length; i++) { + const hipChild = hipChildren[i]; + const treeChild = treeChildren[i]; + if (hipChild && treeChild) { + this.applyBoneTree(hipChild, treeChild); + } + } + } + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + public poseFromBoneJSON(boneJSON: any): void { + const hipBone = this.findHipBone(this.model.children[0].children); + if (!hipBone) { + return; + } + + const loader = new ObjectLoader(); + const boneTree = loader.parse(boneJSON); + this.applyBoneTree(hipBone, boneTree); + } + +} diff --git a/frontend/apps/editor3d/app/src/pages/PageEnigma/Editor/api_fetchers.ts b/frontend/apps/editor3d/app/src/pages/PageEnigma/Editor/api_fetchers.ts index daa903e351..17338f706d 100644 --- a/frontend/apps/editor3d/app/src/pages/PageEnigma/Editor/api_fetchers.ts +++ b/frontend/apps/editor3d/app/src/pages/PageEnigma/Editor/api_fetchers.ts @@ -3,6 +3,7 @@ import { uploadNewScene as uploadNewSceneEndpoint, updateExistingScene as updateExistingSceneEndpoint, } from "~/api"; +import { FetchProxy as fetch } from "@storyteller/tauri-utils"; export const uploadNewScene = async (file: File, sceneTitle: string) => { const endpoint = uploadNewSceneEndpoint; diff --git a/frontend/apps/editor3d/app/src/pages/PageEnigma/Editor/editor.ts b/frontend/apps/editor3d/app/src/pages/PageEnigma/Editor/editor.ts index a9ebcc1e31..f63d5c96ef 100644 --- a/frontend/apps/editor3d/app/src/pages/PageEnigma/Editor/editor.ts +++ b/frontend/apps/editor3d/app/src/pages/PageEnigma/Editor/editor.ts @@ -224,7 +224,7 @@ class Editor { // life cycle fix // Version and name. - this.version = 1.2; + this.version = 2.0; // Clock, scene and camera essentials. // global names this.camera_name = "::CAM::"; diff --git a/frontend/apps/editor3d/app/src/pages/PageEnigma/comps/ControlsTopButtons/ControlsTopButtons.tsx b/frontend/apps/editor3d/app/src/pages/PageEnigma/comps/ControlsTopButtons/ControlsTopButtons.tsx index afa09a32d3..80aff32510 100644 --- a/frontend/apps/editor3d/app/src/pages/PageEnigma/comps/ControlsTopButtons/ControlsTopButtons.tsx +++ b/frontend/apps/editor3d/app/src/pages/PageEnigma/comps/ControlsTopButtons/ControlsTopButtons.tsx @@ -187,7 +187,7 @@ export const ControlsTopButtons = () => { return (