Skip to content

Commit

Permalink
fix pmxFiles undefined
Browse files Browse the repository at this point in the history
  • Loading branch information
culdo committed Jan 11, 2025
1 parent b520e37 commit 530ed37
Show file tree
Hide file tree
Showing 10 changed files with 84 additions and 45 deletions.
6 changes: 2 additions & 4 deletions app/components/control-bar/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,9 @@ import FullScreenButton from "./fullscreen-button";
function ControlBar() {
const getPlayer = () => useGlobalStore.getState().player
const gui = useGlobalStore(state => state.gui)
const presetInit = useGlobalStore(state => state.presetInit)
const isHoverRef = useRef(false)

useEffect(() => {
if(!presetInit) return
const fullScreenBt = document.getElementById("fsBtn")
const rawPlayer = document.getElementById("rawPlayer")
const controls = document.querySelectorAll(".control-bar")
Expand Down Expand Up @@ -43,10 +41,10 @@ function ControlBar() {
isHoverRef.current = false
})
}
}, [presetInit])
}, [])
return (
<>
{presetInit && <AudioPlayer></AudioPlayer>}
<AudioPlayer></AudioPlayer>
<FullScreenButton></FullScreenButton>
</>
);
Expand Down
14 changes: 7 additions & 7 deletions app/components/three-world/animation/useAnimation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,32 +5,32 @@ import { useFrame } from "@react-three/fiber";
import { use, useEffect } from "react";
import { AnimationMixer, Camera, SkinnedMesh } from "three";

function useAnimation(target: Camera | SkinnedMesh, mixer: AnimationMixer, vmdFile: string, callback?: Function) {
function useVMD(target: Camera | SkinnedMesh, mixer: AnimationMixer, vmdFile: string, onLoop?: Function) {
const loader = useGlobalStore(state => state.loader)
const clip = use(loader.loadAnimation(vmdFile, target, onProgress))

const player = useGlobalStore(state => state.player)
const isMotionUpdating = useGlobalStore(state => state.isMotionUpdating)

const setTime = (currentTime: number) => {
const _onLoop = (currentTime: number) => {
mixer.setTime(currentTime)
callback(currentTime)
onLoop(currentTime)
}
useEffect(() => {
const savedCurrentTime = usePresetStore.getState().currentTime

const action = mixer.clipAction(clip)
action.play()
if(callback) setTime(savedCurrentTime)
if(onLoop) _onLoop(savedCurrentTime)
return () => {
mixer.stopAllAction()
mixer.uncacheRoot(target)
}
}, [target, vmdFile, callback])
}, [target, vmdFile, onLoop])

useFrame(() => {
if (isMotionUpdating.current) setTime(player.currentTime)
if (isMotionUpdating.current) _onLoop(player.currentTime)
}, 1)
}

export default useAnimation;
export default useVMD;
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,23 @@ import usePresetStore from "@/app/stores/usePresetStore";
import { useThree } from "@react-three/fiber";
import { useCallback, useMemo } from "react";
import { AnimationMixer } from "three";
import useAnimation from "../../../animation/useAnimation";
import useVMD from "../../../animation/useAnimation";
import WithSuspense from "@/app/components/suspense";
import usePresetReady from "@/app/stores/usePresetReady";


function MotionFileMode() {
const camera = useThree(state => state.camera)
const cameraMixer = useMemo(() => new AnimationMixer(camera), [camera])
usePresetReady()
const cameraFile = usePresetStore(state => state.cameraFile)
const setTimeCb = useCallback(() => {
camera.up.set(0, 1, 0);
camera.up.applyQuaternion(camera.quaternion);
camera.lookAt(camera.getObjectByName("target").position);
camera.updateProjectionMatrix();
}, [camera, cameraMixer])
useAnimation(camera, cameraMixer, cameraFile, setTimeCb)
useVMD(camera, cameraMixer, cameraFile, setTimeCb)
return <></>;
}

Expand Down
26 changes: 26 additions & 0 deletions app/components/three-world/character/Animation.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import useGlobalStore from "@/app/stores/useGlobalStore";
import useVMD from "../animation/useAnimation";
import usePresetStore from "@/app/stores/usePresetStore";
import { AnimationMixer } from "three";
import { useEffect, useMemo } from "react";

function Animation() {
const runtimeCharacter = useGlobalStore(state => state.runtimeCharacter)
const character = useGlobalStore(state => state.character)
const motionFile = usePresetStore(state => state.motionFile)

const mixer = useMemo(() => new AnimationMixer(character), [character]);

useVMD(character, mixer, motionFile)

useEffect(() => {
mixer.addEventListener('loop', () => {
runtimeCharacter.looped = true;
});
}, [mixer])
return (
<></>
);
}

export default Animation;
22 changes: 8 additions & 14 deletions app/components/three-world/character/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,13 @@ import * as THREE from 'three';
import ModelController from "../ModelController";
import PromisePrimitive from "../promise-primitive";
import Pose from "./Pose";
import WithSuspense from "../../suspense";
import usePresetReady from "@/app/stores/usePresetReady";

function CharacterBase() {
function Character() {

const { scene } = useThree()

usePresetReady()
const helper = useGlobalStore(state => state.helper)
const loader = useGlobalStore(state => state.loader)
const characterPromise = useGlobalStore(state => state.characterPromise)
Expand Down Expand Up @@ -56,7 +58,7 @@ function CharacterBase() {
}),
"position": buildGuiItem(positionKey),
"reset": button(() => {
set({position: [0, 0, 0]})
set({ position: [0, 0, 0] })
}),
"motion name": {
value: motionName,
Expand Down Expand Up @@ -93,7 +95,7 @@ function CharacterBase() {

helper.add(character, {
animation: mmd.animation,
unitStep: 1/60,
unitStep: 1 / 60,
maxStepNum: 1,
});
const runtimeCharacter = helper.objects.get(character)
Expand All @@ -116,7 +118,7 @@ function CharacterBase() {

return character
}
useGlobalStore.setState({characterPromise: load()})
useGlobalStore.setState({ characterPromise: load() })
return () => {
const { character, runtimeCharacter } = useGlobalStore.getState()
runtimeCharacter.mixer.uncacheRoot(character);
Expand All @@ -143,17 +145,9 @@ function CharacterBase() {
}}
/>
<Pose></Pose>
</>
);
}

function Character() {
return (
<>
<CharacterBase></CharacterBase>
<ModelController type="Character"></ModelController>
</>
);
}

export default Character;
export default WithSuspense(Character);
9 changes: 6 additions & 3 deletions app/components/three-world/stage/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,15 @@ import { buildGuiItem, buildLoadModelFn } from "@/app/utils/gui";
import { useThree } from "@react-three/fiber";
import { button, useControls } from "leva";
import path from "path-browserify";
import { useEffect, useState } from "react";
import { useEffect } from "react";
import PromisePrimitive from "../promise-primitive";
import usePresetReady from "@/app/stores/usePresetReady";
import WithSuspense from "../../suspense";

function Stage() {
const { scene } = useThree()

usePresetReady()
const stagePromise = useGlobalStore(state => state.stagePromise)
const stageName = usePresetStore(state => state.stage)
const pmxFiles = usePresetStore(state => state.pmxFiles)
Expand Down Expand Up @@ -65,7 +68,7 @@ function Stage() {

return stage
}
useGlobalStore.setState({stagePromise: init()})
useGlobalStore.setState({ stagePromise: init() })
return () => {
const { stage } = useGlobalStore.getState()
scene.remove(stage);
Expand All @@ -79,4 +82,4 @@ function Stage() {
);
}

export default Stage;
export default WithSuspense(Stage);
5 changes: 0 additions & 5 deletions app/modules/MMDAnimationHelper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ class MMDAnimationHelper {
meshes: THREE.SkinnedMesh[];
smoothCenter: THREE.Object3D<THREE.Object3DEventMap>;
camera: THREE.Camera = new THREE.Camera;
cameraTarget: THREE.Object3D<THREE.Object3DEventMap>;
objects: WeakMap<WeakKey, any>;
configuration: { sync: any; afterglow: any; resetPhysicsOnLoop: any; };
enabled: Record<string, boolean>;
Expand All @@ -52,10 +51,6 @@ class MMDAnimationHelper {
this.smoothCenter = new Object3D();
this.smoothCenter.name = 'smoothCenter';

this.cameraTarget = new Object3D();
this.cameraTarget.name = 'target';
this.cameraTarget.userData.frameNum = 0;

this.objects = new WeakMap();

this.configuration = {
Expand Down
8 changes: 4 additions & 4 deletions app/stores/useGlobalStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,9 @@ export type GlobalState = {
isTransformControl: MutableRefObject<boolean>
bindParentCb: Function,
presetReady: boolean,
presetInit: boolean,
selectedName: string,
enabledTransform: boolean
enabledTransform: boolean,
presetReadyPromise: Promise<void>
}

const useGlobalStore = create<GlobalState>(
Expand Down Expand Up @@ -73,9 +73,9 @@ const useGlobalStore = create<GlobalState>(
})(),
bindParentCb: null,
presetReady: false,
presetInit: false,
selectedName: null,
enabledTransform: true
enabledTransform: true,
presetReadyPromise: new Promise(() => { }),
})
)

Expand Down
9 changes: 9 additions & 0 deletions app/stores/usePresetReady.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { use } from "react";
import useGlobalStore from "./useGlobalStore";

function usePresetReady() {
const presetReadyPromise = useGlobalStore(state => state.presetReadyPromise)
use(presetReadyPromise)
}

export default usePresetReady;
24 changes: 18 additions & 6 deletions app/stores/usePresetStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,7 @@ export type PresetState = typeof defaultConfig & {
character: Record<string, Record<string, string>>,
stage: Record<string, Record<string, string>>
}
},
resetPreset: () => Promise<void>
}
} & {
compositeClips?: CameraClip[]
} & {
Expand Down Expand Up @@ -73,15 +72,28 @@ const usePresetStore = create(
)
)

let presetReadySolve: () => void
useGlobalStore.setState({
presetReadyPromise: new Promise<void>((resolve) => {
presetReadySolve = resolve
})
})
usePresetStore.persist.onFinishHydration(async () => {
const defaultData = await getDefaultDataWithProgress()
usePresetStore.setState({ ...defaultData })
if (!usePresetStore.getState()["pmxFiles"]) {
const defaultData = await getDefaultDataWithProgress()
usePresetStore.setState({ ...defaultData })
}
presetReadySolve()
useGlobalStore.setState({ presetReady: true })
useGlobalStore.setState({ presetInit: true })
})

usePresetStore.persist.onHydrate(() => {
useGlobalStore.setState({ presetReady: false })
useGlobalStore.setState({
presetReady: false,
presetReadyPromise: new Promise<void>((resolve) => {
presetReadySolve = resolve
})
})
})

// move to here to avoid cycle imports
Expand Down

0 comments on commit 530ed37

Please sign in to comment.