@memori.ai/memori-react 7.8.4 → 7.8.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +19 -0
- package/dist/components/Avatar/Avatar.js.map +1 -1
- package/dist/components/Avatar/AvatarView/AvatarComponent/avatarComponent.js +4 -2
- package/dist/components/Avatar/AvatarView/AvatarComponent/avatarComponent.js.map +1 -1
- package/dist/components/Avatar/AvatarView/AvatarComponent/components/FullbodyAvatar/AnimationController.d.ts +34 -0
- package/dist/components/Avatar/AvatarView/AvatarComponent/components/FullbodyAvatar/AnimationController.js +147 -0
- package/dist/components/Avatar/AvatarView/AvatarComponent/components/FullbodyAvatar/AnimationController.js.map +1 -0
- package/dist/components/Avatar/AvatarView/AvatarComponent/components/FullbodyAvatar/MorhTargetController.d.ts +17 -0
- package/dist/components/Avatar/AvatarView/AvatarComponent/components/FullbodyAvatar/MorhTargetController.js +73 -0
- package/dist/components/Avatar/AvatarView/AvatarComponent/components/FullbodyAvatar/MorhTargetController.js.map +1 -0
- package/dist/components/Avatar/AvatarView/AvatarComponent/components/FullbodyAvatar/constants.d.ts +17 -0
- package/dist/components/Avatar/AvatarView/AvatarComponent/components/FullbodyAvatar/constants.js +25 -0
- package/dist/components/Avatar/AvatarView/AvatarComponent/components/FullbodyAvatar/constants.js.map +1 -0
- package/dist/components/Avatar/AvatarView/AvatarComponent/components/FullbodyAvatar/fullbodyAvatar.d.ts +2 -0
- package/dist/components/Avatar/AvatarView/AvatarComponent/components/FullbodyAvatar/fullbodyAvatar.js +83 -0
- package/dist/components/Avatar/AvatarView/AvatarComponent/components/FullbodyAvatar/fullbodyAvatar.js.map +1 -0
- package/dist/components/Avatar/AvatarView/AvatarComponent/components/FullbodyAvatar/types.d.ts +35 -0
- package/dist/components/Avatar/AvatarView/AvatarComponent/components/FullbodyAvatar/types.js +10 -0
- package/dist/components/Avatar/AvatarView/AvatarComponent/components/FullbodyAvatar/types.js.map +1 -0
- package/dist/components/Avatar/AvatarView/index.js.map +1 -1
- package/dist/context/visemeContext.d.ts +1 -1
- package/dist/context/visemeContext.js +2 -2
- package/dist/context/visemeContext.js.map +1 -1
- package/esm/components/Avatar/Avatar.js.map +1 -1
- package/esm/components/Avatar/AvatarView/AvatarComponent/avatarComponent.js +4 -2
- package/esm/components/Avatar/AvatarView/AvatarComponent/avatarComponent.js.map +1 -1
- package/esm/components/Avatar/AvatarView/AvatarComponent/components/FullbodyAvatar/AnimationController.d.ts +34 -0
- package/esm/components/Avatar/AvatarView/AvatarComponent/components/FullbodyAvatar/AnimationController.js +143 -0
- package/esm/components/Avatar/AvatarView/AvatarComponent/components/FullbodyAvatar/AnimationController.js.map +1 -0
- package/esm/components/Avatar/AvatarView/AvatarComponent/components/FullbodyAvatar/MorhTargetController.d.ts +17 -0
- package/esm/components/Avatar/AvatarView/AvatarComponent/components/FullbodyAvatar/MorhTargetController.js +69 -0
- package/esm/components/Avatar/AvatarView/AvatarComponent/components/FullbodyAvatar/MorhTargetController.js.map +1 -0
- package/esm/components/Avatar/AvatarView/AvatarComponent/components/FullbodyAvatar/constants.d.ts +17 -0
- package/esm/components/Avatar/AvatarView/AvatarComponent/components/FullbodyAvatar/constants.js +22 -0
- package/esm/components/Avatar/AvatarView/AvatarComponent/components/FullbodyAvatar/constants.js.map +1 -0
- package/esm/components/Avatar/AvatarView/AvatarComponent/components/FullbodyAvatar/fullbodyAvatar.d.ts +2 -0
- package/esm/components/Avatar/AvatarView/AvatarComponent/components/FullbodyAvatar/fullbodyAvatar.js +80 -0
- package/esm/components/Avatar/AvatarView/AvatarComponent/components/FullbodyAvatar/fullbodyAvatar.js.map +1 -0
- package/esm/components/Avatar/AvatarView/AvatarComponent/components/FullbodyAvatar/types.d.ts +35 -0
- package/esm/components/Avatar/AvatarView/AvatarComponent/components/FullbodyAvatar/types.js +7 -0
- package/esm/components/Avatar/AvatarView/AvatarComponent/components/FullbodyAvatar/types.js.map +1 -0
- package/esm/components/Avatar/AvatarView/index.js.map +1 -1
- package/esm/context/visemeContext.d.ts +1 -1
- package/esm/context/visemeContext.js +2 -2
- package/esm/context/visemeContext.js.map +1 -1
- package/package.json +1 -1
- package/src/components/Avatar/Avatar.tsx +2 -3
- package/src/components/Avatar/AvatarView/AvatarComponent/avatarComponent.tsx +6 -13
- package/src/components/Avatar/AvatarView/AvatarComponent/components/FullbodyAvatar/AnimationController.ts +260 -0
- package/src/components/Avatar/AvatarView/AvatarComponent/components/FullbodyAvatar/MorhTargetController.ts +123 -0
- package/src/components/Avatar/AvatarView/AvatarComponent/components/FullbodyAvatar/constants.ts +28 -0
- package/src/components/Avatar/AvatarView/AvatarComponent/components/FullbodyAvatar/fullbodyAvatar.tsx +147 -0
- package/src/components/Avatar/AvatarView/AvatarComponent/components/FullbodyAvatar/types.ts +40 -0
- package/src/components/Avatar/AvatarView/index.tsx +4 -4
- package/src/context/visemeContext.tsx +2 -2
- package/src/components/Avatar/AvatarView/AvatarComponent/components/fullbodyAvatar.tsx +0 -281
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
export enum AnimationState {
|
|
2
|
+
LOADING = 'LOADING',
|
|
3
|
+
EMOTION = 'EMOTION',
|
|
4
|
+
IDLE = 'IDLE',
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export interface AnimationConfig {
|
|
8
|
+
fadeInDuration: number;
|
|
9
|
+
fadeOutDuration: number;
|
|
10
|
+
idleCount: number;
|
|
11
|
+
timeScale: number;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface FullbodyAvatarProps {
|
|
15
|
+
url: string;
|
|
16
|
+
sex: 'MALE' | 'FEMALE';
|
|
17
|
+
onLoaded?: () => void;
|
|
18
|
+
currentBaseAction: {
|
|
19
|
+
action: string;
|
|
20
|
+
weight: number;
|
|
21
|
+
};
|
|
22
|
+
timeScale: number;
|
|
23
|
+
isZoomed?: boolean;
|
|
24
|
+
eyeBlink?: boolean;
|
|
25
|
+
stopProcessing: () => void;
|
|
26
|
+
resetVisemeQueue: () => void;
|
|
27
|
+
updateCurrentViseme: (
|
|
28
|
+
currentTime: number
|
|
29
|
+
) => { name: string; weight: number } | null;
|
|
30
|
+
smoothMorphTarget?: boolean;
|
|
31
|
+
morphTargetSmoothing?: number;
|
|
32
|
+
morphTargetInfluences: Record<string, number>;
|
|
33
|
+
setMorphTargetDictionary: (
|
|
34
|
+
morphTargetDictionary: Record<string, number>
|
|
35
|
+
) => void;
|
|
36
|
+
setMorphTargetInfluences: (
|
|
37
|
+
morphTargetInfluences: Record<string, number>
|
|
38
|
+
) => void;
|
|
39
|
+
emotionMorphTargets: Record<string, number>;
|
|
40
|
+
}
|
|
@@ -3,7 +3,7 @@ import React, { Suspense } from 'react';
|
|
|
3
3
|
import { Canvas } from '@react-three/fiber';
|
|
4
4
|
import { OrbitControls, SpotLight, Environment } from '@react-three/drei';
|
|
5
5
|
import { isAndroid, isiOS } from '../../../helpers/utils';
|
|
6
|
-
import {AvatarView} from './AvatarComponent/avatarComponent';
|
|
6
|
+
import { AvatarView } from './AvatarComponent/avatarComponent';
|
|
7
7
|
import Loader from './AvatarComponent/components/loader';
|
|
8
8
|
|
|
9
9
|
export interface Props {
|
|
@@ -25,7 +25,9 @@ export interface Props {
|
|
|
25
25
|
setMeshRef?: any;
|
|
26
26
|
stopProcessing: () => void;
|
|
27
27
|
resetVisemeQueue: () => void;
|
|
28
|
-
updateCurrentViseme: (
|
|
28
|
+
updateCurrentViseme: (
|
|
29
|
+
currentTime: number
|
|
30
|
+
) => { name: string; weight: number } | null;
|
|
29
31
|
}
|
|
30
32
|
|
|
31
33
|
const defaultStyles = {
|
|
@@ -70,8 +72,6 @@ const getLightingComponent = () =>
|
|
|
70
72
|
) : (
|
|
71
73
|
<Environment files="https://raw.githack.com/pmndrs/drei-assets/456060a26bbeb8fdf79326f224b6d99b8bcce736/hdri/venice_sunset_1k.hdr" />
|
|
72
74
|
);
|
|
73
|
-
|
|
74
|
-
|
|
75
75
|
|
|
76
76
|
export default function ContainerAvatarView({
|
|
77
77
|
url,
|
|
@@ -64,13 +64,13 @@ const CONSTANTS = {
|
|
|
64
64
|
// Overlap between consecutive visemes
|
|
65
65
|
// Increased for smoother blending between mouth shapes
|
|
66
66
|
// Should be about 1/3 of the viseme duration for natural transition
|
|
67
|
-
VISEME_OVERLAP: 0.
|
|
67
|
+
VISEME_OVERLAP: 0.02, // 30ms overlap
|
|
68
68
|
|
|
69
69
|
// Smoothing factor for weight transitions
|
|
70
70
|
// Lower value = smoother but slower transitions
|
|
71
71
|
// Higher value = faster but potentially jerky transitions
|
|
72
72
|
// 0.4 provides good balance between responsiveness and smoothness
|
|
73
|
-
SMOOTHING_FACTOR: 0.
|
|
73
|
+
SMOOTHING_FACTOR: 0.4,
|
|
74
74
|
|
|
75
75
|
// How often to log debug information (in frames)
|
|
76
76
|
// Adjusted to be less frequent to reduce console spam
|
|
@@ -1,281 +0,0 @@
|
|
|
1
|
-
import React, { useEffect, useRef, useMemo, useCallback } from 'react';
|
|
2
|
-
import {
|
|
3
|
-
Vector3,
|
|
4
|
-
Euler,
|
|
5
|
-
AnimationMixer,
|
|
6
|
-
SkinnedMesh,
|
|
7
|
-
Object3D,
|
|
8
|
-
MathUtils,
|
|
9
|
-
AnimationAction,
|
|
10
|
-
LoopOnce,
|
|
11
|
-
} from 'three';
|
|
12
|
-
import { useAnimations, useGLTF } from '@react-three/drei';
|
|
13
|
-
import { useGraph, useFrame } from '@react-three/fiber';
|
|
14
|
-
|
|
15
|
-
interface FullbodyAvatarProps {
|
|
16
|
-
url: string;
|
|
17
|
-
sex: 'MALE' | 'FEMALE';
|
|
18
|
-
onLoaded?: () => void;
|
|
19
|
-
currentBaseAction: {
|
|
20
|
-
action: string;
|
|
21
|
-
weight: number;
|
|
22
|
-
};
|
|
23
|
-
timeScale: number;
|
|
24
|
-
isZoomed?: boolean;
|
|
25
|
-
eyeBlink?: boolean;
|
|
26
|
-
stopProcessing: () => void;
|
|
27
|
-
resetVisemeQueue: () => void;
|
|
28
|
-
updateCurrentViseme: (
|
|
29
|
-
currentTime: number
|
|
30
|
-
) => { name: string; weight: number } | null;
|
|
31
|
-
smoothMorphTarget?: boolean;
|
|
32
|
-
morphTargetSmoothing?: number;
|
|
33
|
-
morphTargetInfluences: Record<string, number>;
|
|
34
|
-
setMorphTargetDictionary: (
|
|
35
|
-
morphTargetDictionary: Record<string, number>
|
|
36
|
-
) => void;
|
|
37
|
-
setMorphTargetInfluences: (
|
|
38
|
-
morphTargetInfluences: Record<string, number>
|
|
39
|
-
) => void;
|
|
40
|
-
emotionMorphTargets: Record<string, number>;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
const AVATAR_POSITION = new Vector3(0, -1, 0);
|
|
44
|
-
const AVATAR_ROTATION = new Euler(0.175, 0, 0);
|
|
45
|
-
const AVATAR_POSITION_ZOOMED = new Vector3(0, -1.45, 0);
|
|
46
|
-
|
|
47
|
-
const ANIMATION_URLS = {
|
|
48
|
-
MALE: 'https://assets.memori.ai/api/v2/asset/2c5e88a4-cf62-408b-9ef0-518b099dfcb2.glb',
|
|
49
|
-
FEMALE:
|
|
50
|
-
'https://assets.memori.ai/api/v2/asset/8d1a5853-f05a-4a34-9f99-6eff64986081.glb',
|
|
51
|
-
};
|
|
52
|
-
|
|
53
|
-
const BLINK_CONFIG = {
|
|
54
|
-
minInterval: 1000,
|
|
55
|
-
maxInterval: 5000,
|
|
56
|
-
blinkDuration: 150,
|
|
57
|
-
};
|
|
58
|
-
|
|
59
|
-
const EMOTION_SMOOTHING = 0.3;
|
|
60
|
-
const VISME_SMOOTHING = 0.5;
|
|
61
|
-
|
|
62
|
-
export default function FullbodyAvatar({
|
|
63
|
-
url,
|
|
64
|
-
sex,
|
|
65
|
-
currentBaseAction,
|
|
66
|
-
timeScale,
|
|
67
|
-
isZoomed,
|
|
68
|
-
eyeBlink,
|
|
69
|
-
updateCurrentViseme,
|
|
70
|
-
setMorphTargetDictionary,
|
|
71
|
-
setMorphTargetInfluences,
|
|
72
|
-
emotionMorphTargets,
|
|
73
|
-
}: FullbodyAvatarProps) {
|
|
74
|
-
const { scene } = useGLTF(url);
|
|
75
|
-
const { animations } = useGLTF(ANIMATION_URLS[sex]);
|
|
76
|
-
const { actions } = useAnimations(animations, scene);
|
|
77
|
-
|
|
78
|
-
const mixerRef = useRef<AnimationMixer>();
|
|
79
|
-
const headMeshRef = useRef<SkinnedMesh>();
|
|
80
|
-
const currentActionRef = useRef<AnimationAction | null>(null);
|
|
81
|
-
const isTransitioningToIdleRef = useRef(false);
|
|
82
|
-
|
|
83
|
-
const lastBlinkTimeRef = useRef(0);
|
|
84
|
-
const nextBlinkTimeRef = useRef(0);
|
|
85
|
-
const isBlinkingRef = useRef(false);
|
|
86
|
-
const blinkStartTimeRef = useRef(0);
|
|
87
|
-
|
|
88
|
-
const currentEmotionRef = useRef<Record<string, number>>({});
|
|
89
|
-
const previousEmotionKeysRef = useRef<Set<string>>(new Set());
|
|
90
|
-
|
|
91
|
-
// Memoize the scene traversal
|
|
92
|
-
const headMesh = useMemo(() => {
|
|
93
|
-
let foundMesh: SkinnedMesh | undefined;
|
|
94
|
-
scene.traverse((object: Object3D) => {
|
|
95
|
-
if (
|
|
96
|
-
object instanceof SkinnedMesh &&
|
|
97
|
-
(object.name === 'GBNL__Head' || object.name === 'Wolf3D_Avatar')
|
|
98
|
-
) {
|
|
99
|
-
foundMesh = object;
|
|
100
|
-
}
|
|
101
|
-
});
|
|
102
|
-
return foundMesh;
|
|
103
|
-
}, [scene]);
|
|
104
|
-
|
|
105
|
-
useEffect(() => {
|
|
106
|
-
if (headMesh) {
|
|
107
|
-
headMeshRef.current = headMesh;
|
|
108
|
-
if (headMesh.morphTargetDictionary && headMesh.morphTargetInfluences) {
|
|
109
|
-
setMorphTargetDictionary(headMesh.morphTargetDictionary);
|
|
110
|
-
const initialInfluences = Object.keys(
|
|
111
|
-
headMesh.morphTargetDictionary
|
|
112
|
-
).reduce((acc, key) => ({ ...acc, [key]: 0 }), {});
|
|
113
|
-
setMorphTargetInfluences(initialInfluences);
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
mixerRef.current = new AnimationMixer(scene);
|
|
117
|
-
}, [headMesh, scene, setMorphTargetDictionary, setMorphTargetInfluences]);
|
|
118
|
-
|
|
119
|
-
// Memoize the animation change handler
|
|
120
|
-
const handleAnimationChange = useCallback(() => {
|
|
121
|
-
if (!actions || !currentBaseAction.action) return;
|
|
122
|
-
|
|
123
|
-
const newAction = actions[currentBaseAction.action];
|
|
124
|
-
if (!newAction) {
|
|
125
|
-
console.warn(
|
|
126
|
-
`Animation "${currentBaseAction.action}" not found in actions.`
|
|
127
|
-
);
|
|
128
|
-
return;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
const fadeOutDuration = 0.8;
|
|
132
|
-
const fadeInDuration = 0.8;
|
|
133
|
-
|
|
134
|
-
if (currentActionRef.current) {
|
|
135
|
-
currentActionRef.current.fadeOut(fadeOutDuration);
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
newAction.reset().fadeIn(fadeInDuration).play();
|
|
139
|
-
currentActionRef.current = newAction;
|
|
140
|
-
newAction.timeScale = timeScale;
|
|
141
|
-
|
|
142
|
-
if (
|
|
143
|
-
currentBaseAction.action.startsWith('Gioia') ||
|
|
144
|
-
currentBaseAction.action.startsWith('Rabbia') ||
|
|
145
|
-
currentBaseAction.action.startsWith('Sorpresa') ||
|
|
146
|
-
currentBaseAction.action.startsWith('Timore') ||
|
|
147
|
-
currentBaseAction.action.startsWith('Tristezza')
|
|
148
|
-
) {
|
|
149
|
-
newAction.setLoop(LoopOnce, 1);
|
|
150
|
-
newAction.clampWhenFinished = true;
|
|
151
|
-
isTransitioningToIdleRef.current = true;
|
|
152
|
-
}
|
|
153
|
-
}, [actions, currentBaseAction, timeScale]);
|
|
154
|
-
|
|
155
|
-
useEffect(() => {
|
|
156
|
-
handleAnimationChange();
|
|
157
|
-
}, [handleAnimationChange]);
|
|
158
|
-
|
|
159
|
-
// Optimize the frame update function
|
|
160
|
-
const updateFrame = useCallback(
|
|
161
|
-
(currentTime: number) => {
|
|
162
|
-
if (
|
|
163
|
-
!headMeshRef.current ||
|
|
164
|
-
!headMeshRef.current.morphTargetDictionary ||
|
|
165
|
-
!headMeshRef.current.morphTargetInfluences
|
|
166
|
-
)
|
|
167
|
-
return;
|
|
168
|
-
|
|
169
|
-
let blinkValue = 0;
|
|
170
|
-
if (eyeBlink) {
|
|
171
|
-
if (currentTime >= nextBlinkTimeRef.current && !isBlinkingRef.current) {
|
|
172
|
-
isBlinkingRef.current = true;
|
|
173
|
-
blinkStartTimeRef.current = currentTime;
|
|
174
|
-
lastBlinkTimeRef.current = currentTime;
|
|
175
|
-
nextBlinkTimeRef.current =
|
|
176
|
-
currentTime +
|
|
177
|
-
Math.random() *
|
|
178
|
-
(BLINK_CONFIG.maxInterval - BLINK_CONFIG.minInterval) +
|
|
179
|
-
BLINK_CONFIG.minInterval;
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
if (isBlinkingRef.current) {
|
|
183
|
-
const blinkProgress =
|
|
184
|
-
(currentTime - blinkStartTimeRef.current) /
|
|
185
|
-
BLINK_CONFIG.blinkDuration;
|
|
186
|
-
if (blinkProgress <= 0.5) {
|
|
187
|
-
blinkValue = blinkProgress * 2;
|
|
188
|
-
} else if (blinkProgress <= 1) {
|
|
189
|
-
blinkValue = 2 - blinkProgress * 2;
|
|
190
|
-
} else {
|
|
191
|
-
isBlinkingRef.current = false;
|
|
192
|
-
blinkValue = 0;
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
// Update current viseme
|
|
198
|
-
const currentViseme = updateCurrentViseme(currentTime / 1000);
|
|
199
|
-
const currentEmotionKeys = new Set(Object.keys(emotionMorphTargets));
|
|
200
|
-
|
|
201
|
-
// Update morph target influences
|
|
202
|
-
Object.entries(headMeshRef.current.morphTargetDictionary).forEach(
|
|
203
|
-
([key, index]) => {
|
|
204
|
-
if (typeof index === 'number') {
|
|
205
|
-
let targetValue = 0;
|
|
206
|
-
|
|
207
|
-
if (currentEmotionKeys.has(key)) {
|
|
208
|
-
const targetEmotionValue = emotionMorphTargets[key];
|
|
209
|
-
const currentEmotionValue = currentEmotionRef.current[key] || 0;
|
|
210
|
-
const newEmotionValue = MathUtils.lerp(
|
|
211
|
-
currentEmotionValue,
|
|
212
|
-
targetEmotionValue * 2.5,
|
|
213
|
-
EMOTION_SMOOTHING
|
|
214
|
-
);
|
|
215
|
-
currentEmotionRef.current[key] = newEmotionValue;
|
|
216
|
-
targetValue += newEmotionValue;
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
if (currentViseme && key === currentViseme.name) {
|
|
220
|
-
targetValue += currentViseme.weight;
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
if (key === 'eyesClosed' && eyeBlink) {
|
|
224
|
-
targetValue += blinkValue;
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
targetValue = MathUtils.clamp(targetValue, 0, 1);
|
|
228
|
-
if (
|
|
229
|
-
headMeshRef.current &&
|
|
230
|
-
headMeshRef.current.morphTargetInfluences
|
|
231
|
-
) {
|
|
232
|
-
headMeshRef.current.morphTargetInfluences[index] = MathUtils.lerp(
|
|
233
|
-
headMeshRef.current.morphTargetInfluences[index],
|
|
234
|
-
targetValue,
|
|
235
|
-
VISME_SMOOTHING
|
|
236
|
-
);
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
);
|
|
241
|
-
|
|
242
|
-
// Update previous emotion keys
|
|
243
|
-
previousEmotionKeysRef.current = currentEmotionKeys;
|
|
244
|
-
|
|
245
|
-
// Transition to idle
|
|
246
|
-
if (isTransitioningToIdleRef.current && currentActionRef.current) {
|
|
247
|
-
if (
|
|
248
|
-
currentActionRef.current.time >=
|
|
249
|
-
currentActionRef.current.getClip().duration
|
|
250
|
-
) {
|
|
251
|
-
const idleNumber = Math.floor(Math.random() * 5) + 1;
|
|
252
|
-
const idleAction =
|
|
253
|
-
actions[`Idle${idleNumber === 3 ? 4 : idleNumber}`];
|
|
254
|
-
|
|
255
|
-
if (idleAction) {
|
|
256
|
-
currentActionRef.current.fadeOut(0.5);
|
|
257
|
-
idleAction.reset().fadeIn(0.5).play();
|
|
258
|
-
currentActionRef.current = idleAction;
|
|
259
|
-
isTransitioningToIdleRef.current = false;
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
mixerRef.current?.update(0.01);
|
|
265
|
-
},
|
|
266
|
-
[actions, emotionMorphTargets, eyeBlink, updateCurrentViseme]
|
|
267
|
-
);
|
|
268
|
-
|
|
269
|
-
useFrame(state => {
|
|
270
|
-
updateFrame(state.clock.elapsedTime * 1000);
|
|
271
|
-
});
|
|
272
|
-
|
|
273
|
-
return (
|
|
274
|
-
<group
|
|
275
|
-
position={isZoomed ? AVATAR_POSITION_ZOOMED : AVATAR_POSITION}
|
|
276
|
-
rotation={AVATAR_ROTATION}
|
|
277
|
-
>
|
|
278
|
-
<primitive object={scene} />
|
|
279
|
-
</group>
|
|
280
|
-
);
|
|
281
|
-
}
|