mirror of
https://github.com/storytold/artcraft.git
synced 2026-10-20 17:02:53 -04:00
Compare commits
6
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ad7a0bcc7f | ||
|
|
9bc3a57390 | ||
|
|
109a9dd1a3 | ||
|
|
b6807a2a3d | ||
|
|
4d738c0384 | ||
|
|
7227156711 |
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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::";
|
||||
|
||||
+5
-5
@@ -187,7 +187,7 @@ export const ControlsTopButtons = () => {
|
||||
return (
|
||||
<div className="flex flex-col gap-2 pl-2 pt-2">
|
||||
<div className="flex gap-1.5">
|
||||
{/* <ButtonDropdown
|
||||
<ButtonDropdown
|
||||
label="File"
|
||||
icon={faFile}
|
||||
className="shadow-xl"
|
||||
@@ -222,9 +222,9 @@ export const ControlsTopButtons = () => {
|
||||
},
|
||||
{
|
||||
disabled:
|
||||
!scene.value.isModified ||
|
||||
scene.value.ownerToken ===
|
||||
authentication.userInfo.value?.user_token,
|
||||
!(scene.value.isModified &&
|
||||
(scene.value.ownerToken === undefined || scene.value.ownerToken ===
|
||||
authentication.userInfo.value?.user_token)),
|
||||
// save scene should be disabled if there are no changes
|
||||
label: "Save scene",
|
||||
description: "Ctrl+S",
|
||||
@@ -285,7 +285,7 @@ export const ControlsTopButtons = () => {
|
||||
},
|
||||
},
|
||||
]}
|
||||
/> */}
|
||||
/>
|
||||
|
||||
<Button
|
||||
icon={outlinerIsShowing.value ? faCheckSquare : faSquare}
|
||||
|
||||
@@ -29,6 +29,8 @@ export interface ObjectJSON {
|
||||
locked: boolean;
|
||||
visible: boolean | undefined;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
rigData: any;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
user_data: Record<string, any>;
|
||||
}
|
||||
|
||||
@@ -50,6 +52,8 @@ export class StoryTellerProxy3DObject {
|
||||
locked: boolean;
|
||||
visible: boolean;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
rigData: any;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
userData: Record<string, any>;
|
||||
|
||||
constructor(version: number, media_file_token: string) {
|
||||
@@ -71,6 +75,7 @@ export class StoryTellerProxy3DObject {
|
||||
this.specular = 0.5;
|
||||
this.locked = false;
|
||||
this.visible = true;
|
||||
this.rigData = undefined;
|
||||
this.userData = {};
|
||||
this.userData.media_file_type = MediaFileType.None;
|
||||
}
|
||||
@@ -129,6 +134,7 @@ export class StoryTellerProxy3DObject {
|
||||
locked: this.locked,
|
||||
visible: this.visible,
|
||||
user_data: this.userData,
|
||||
rigData: this.rigData,
|
||||
};
|
||||
return json;
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
ObjectJSON,
|
||||
} from "./storyteller_proxy_3d_object";
|
||||
import Scene from "../Editor/scene";
|
||||
import { BoneJSONHelper } from "../Editor/KinHelpers/BoneJSONHelper";
|
||||
|
||||
interface LookUpDictionary {
|
||||
[key: string]: StoryTellerProxy3DObject;
|
||||
@@ -49,6 +50,10 @@ export class StoryTellerProxyScene {
|
||||
proxyObject3D.locked = child.userData["locked"];
|
||||
proxyObject3D.visible = child.visible;
|
||||
proxyObject3D.userData = child.userData;
|
||||
// Add rig data if present
|
||||
const boneHelper = new BoneJSONHelper(child);
|
||||
proxyObject3D.rigData = boneHelper.toJSON();
|
||||
|
||||
const json_data = await proxyObject3D.toJSON();
|
||||
return json_data;
|
||||
}
|
||||
@@ -146,6 +151,16 @@ export class StoryTellerProxyScene {
|
||||
new THREE.Vector3(-0.5, 1.5, 0),
|
||||
version,
|
||||
);
|
||||
// Add any missing userdata back to the object
|
||||
obj.userData = json_object.user_data;
|
||||
|
||||
// Add rig data if present
|
||||
if (json_object.rigData) {
|
||||
const boneHelper = new BoneJSONHelper(obj);
|
||||
boneHelper.poseFromBoneJSON(json_object.rigData);
|
||||
} else {
|
||||
console.log("No rig data found");
|
||||
}
|
||||
} else if (token.includes("Point::")) {
|
||||
const keyframe_uuid = token.replace("Point::", "");
|
||||
obj = this.scene.createPoint(
|
||||
|
||||
Reference in New Issue
Block a user