@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.
Files changed (56) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/dist/components/Avatar/Avatar.js.map +1 -1
  3. package/dist/components/Avatar/AvatarView/AvatarComponent/avatarComponent.js +4 -2
  4. package/dist/components/Avatar/AvatarView/AvatarComponent/avatarComponent.js.map +1 -1
  5. package/dist/components/Avatar/AvatarView/AvatarComponent/components/FullbodyAvatar/AnimationController.d.ts +34 -0
  6. package/dist/components/Avatar/AvatarView/AvatarComponent/components/FullbodyAvatar/AnimationController.js +147 -0
  7. package/dist/components/Avatar/AvatarView/AvatarComponent/components/FullbodyAvatar/AnimationController.js.map +1 -0
  8. package/dist/components/Avatar/AvatarView/AvatarComponent/components/FullbodyAvatar/MorhTargetController.d.ts +17 -0
  9. package/dist/components/Avatar/AvatarView/AvatarComponent/components/FullbodyAvatar/MorhTargetController.js +73 -0
  10. package/dist/components/Avatar/AvatarView/AvatarComponent/components/FullbodyAvatar/MorhTargetController.js.map +1 -0
  11. package/dist/components/Avatar/AvatarView/AvatarComponent/components/FullbodyAvatar/constants.d.ts +17 -0
  12. package/dist/components/Avatar/AvatarView/AvatarComponent/components/FullbodyAvatar/constants.js +25 -0
  13. package/dist/components/Avatar/AvatarView/AvatarComponent/components/FullbodyAvatar/constants.js.map +1 -0
  14. package/dist/components/Avatar/AvatarView/AvatarComponent/components/FullbodyAvatar/fullbodyAvatar.d.ts +2 -0
  15. package/dist/components/Avatar/AvatarView/AvatarComponent/components/FullbodyAvatar/fullbodyAvatar.js +83 -0
  16. package/dist/components/Avatar/AvatarView/AvatarComponent/components/FullbodyAvatar/fullbodyAvatar.js.map +1 -0
  17. package/dist/components/Avatar/AvatarView/AvatarComponent/components/FullbodyAvatar/types.d.ts +35 -0
  18. package/dist/components/Avatar/AvatarView/AvatarComponent/components/FullbodyAvatar/types.js +10 -0
  19. package/dist/components/Avatar/AvatarView/AvatarComponent/components/FullbodyAvatar/types.js.map +1 -0
  20. package/dist/components/Avatar/AvatarView/index.js.map +1 -1
  21. package/dist/context/visemeContext.d.ts +1 -1
  22. package/dist/context/visemeContext.js +2 -2
  23. package/dist/context/visemeContext.js.map +1 -1
  24. package/esm/components/Avatar/Avatar.js.map +1 -1
  25. package/esm/components/Avatar/AvatarView/AvatarComponent/avatarComponent.js +4 -2
  26. package/esm/components/Avatar/AvatarView/AvatarComponent/avatarComponent.js.map +1 -1
  27. package/esm/components/Avatar/AvatarView/AvatarComponent/components/FullbodyAvatar/AnimationController.d.ts +34 -0
  28. package/esm/components/Avatar/AvatarView/AvatarComponent/components/FullbodyAvatar/AnimationController.js +143 -0
  29. package/esm/components/Avatar/AvatarView/AvatarComponent/components/FullbodyAvatar/AnimationController.js.map +1 -0
  30. package/esm/components/Avatar/AvatarView/AvatarComponent/components/FullbodyAvatar/MorhTargetController.d.ts +17 -0
  31. package/esm/components/Avatar/AvatarView/AvatarComponent/components/FullbodyAvatar/MorhTargetController.js +69 -0
  32. package/esm/components/Avatar/AvatarView/AvatarComponent/components/FullbodyAvatar/MorhTargetController.js.map +1 -0
  33. package/esm/components/Avatar/AvatarView/AvatarComponent/components/FullbodyAvatar/constants.d.ts +17 -0
  34. package/esm/components/Avatar/AvatarView/AvatarComponent/components/FullbodyAvatar/constants.js +22 -0
  35. package/esm/components/Avatar/AvatarView/AvatarComponent/components/FullbodyAvatar/constants.js.map +1 -0
  36. package/esm/components/Avatar/AvatarView/AvatarComponent/components/FullbodyAvatar/fullbodyAvatar.d.ts +2 -0
  37. package/esm/components/Avatar/AvatarView/AvatarComponent/components/FullbodyAvatar/fullbodyAvatar.js +80 -0
  38. package/esm/components/Avatar/AvatarView/AvatarComponent/components/FullbodyAvatar/fullbodyAvatar.js.map +1 -0
  39. package/esm/components/Avatar/AvatarView/AvatarComponent/components/FullbodyAvatar/types.d.ts +35 -0
  40. package/esm/components/Avatar/AvatarView/AvatarComponent/components/FullbodyAvatar/types.js +7 -0
  41. package/esm/components/Avatar/AvatarView/AvatarComponent/components/FullbodyAvatar/types.js.map +1 -0
  42. package/esm/components/Avatar/AvatarView/index.js.map +1 -1
  43. package/esm/context/visemeContext.d.ts +1 -1
  44. package/esm/context/visemeContext.js +2 -2
  45. package/esm/context/visemeContext.js.map +1 -1
  46. package/package.json +1 -1
  47. package/src/components/Avatar/Avatar.tsx +2 -3
  48. package/src/components/Avatar/AvatarView/AvatarComponent/avatarComponent.tsx +6 -13
  49. package/src/components/Avatar/AvatarView/AvatarComponent/components/FullbodyAvatar/AnimationController.ts +260 -0
  50. package/src/components/Avatar/AvatarView/AvatarComponent/components/FullbodyAvatar/MorhTargetController.ts +123 -0
  51. package/src/components/Avatar/AvatarView/AvatarComponent/components/FullbodyAvatar/constants.ts +28 -0
  52. package/src/components/Avatar/AvatarView/AvatarComponent/components/FullbodyAvatar/fullbodyAvatar.tsx +147 -0
  53. package/src/components/Avatar/AvatarView/AvatarComponent/components/FullbodyAvatar/types.ts +40 -0
  54. package/src/components/Avatar/AvatarView/index.tsx +4 -4
  55. package/src/context/visemeContext.tsx +2 -2
  56. 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: (currentTime: number) => { name: string; weight: number } | null;
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.01, // 30ms overlap
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.35,
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
- }