talking-head-studio 0.4.11 → 0.4.12
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/README.md +279 -193
- package/dist/TalkingHead.d.ts +28 -3
- package/dist/TalkingHead.js +21 -2
- package/dist/TalkingHead.web.d.ts +31 -4
- package/dist/TalkingHead.web.js +11 -1
- package/dist/TalkingHeadVisualization.d.ts +22 -0
- package/dist/TalkingHeadVisualization.js +30 -10
- package/dist/api/studioApi.d.ts +12 -1
- package/dist/api/studioApi.js +16 -2
- package/dist/contract.d.ts +14 -0
- package/dist/contract.js +30 -0
- package/dist/core/avatar/avatarCapabilities.d.ts +60 -0
- package/dist/core/avatar/avatarCapabilities.js +100 -0
- package/dist/core/avatar/backends/gaussian.js +6 -4
- package/dist/core/avatar/motion.d.ts +1713 -0
- package/dist/core/avatar/motion.js +550 -0
- package/dist/core/avatar/motionRuntime.d.ts +46 -0
- package/dist/core/avatar/motionRuntime.js +84 -0
- package/dist/core/avatar/schema.d.ts +33 -5
- package/dist/core/avatar/visemes.d.ts +16 -1
- package/dist/core/avatar/visemes.js +48 -1
- package/dist/editor/AvatarCanvas.js +92 -1
- package/dist/editor/AvatarEditor.native.js +1 -0
- package/dist/editor/AvatarModel.js +1 -0
- package/dist/editor/FaceSqueezeEditor.d.ts +3 -1
- package/dist/editor/FaceSqueezeEditor.js +176 -112
- package/dist/editor/FaceSqueezeEditor.web.d.ts +3 -1
- package/dist/editor/FaceSqueezeEditor.web.js +30 -28
- package/dist/editor/RigidAccessory.js +17 -2
- package/dist/editor/SkinnedClothing.js +1 -0
- package/dist/editor/boneLockedDrag.d.ts +11 -0
- package/dist/editor/boneLockedDrag.js +68 -0
- package/dist/editor/boneSnap.web.d.ts +27 -0
- package/dist/editor/boneSnap.web.js +99 -0
- package/dist/editor/index.web.d.ts +10 -0
- package/dist/editor/index.web.js +26 -0
- package/dist/editor/sounds/haha.wav +0 -0
- package/dist/editor/sounds/owie.wav +0 -0
- package/dist/editor/sounds/stop.wav +0 -0
- package/dist/editor/studioTheme.d.ts +14 -14
- package/dist/editor/studioTheme.js +17 -14
- package/dist/editor/types.d.ts +1 -0
- package/dist/html/accessories.d.ts +7 -0
- package/dist/html/accessories.js +149 -0
- package/dist/html/motion.d.ts +1 -0
- package/dist/html/motion.js +189 -0
- package/dist/html/visemes.d.ts +7 -0
- package/dist/html/visemes.js +348 -0
- package/dist/html.d.ts +1 -1
- package/dist/html.js +55 -732
- package/dist/index.d.ts +7 -3
- package/dist/index.js +17 -1
- package/dist/index.web.d.ts +18 -1
- package/dist/index.web.js +36 -3
- package/dist/sketchfab/api.js +1 -0
- package/dist/sketchfab/glbInspect.d.ts +22 -0
- package/dist/sketchfab/glbInspect.js +58 -0
- package/dist/sketchfab/index.d.ts +3 -0
- package/dist/sketchfab/index.js +8 -1
- package/dist/sketchfab/inspectRemote.d.ts +13 -0
- package/dist/sketchfab/inspectRemote.js +77 -0
- package/dist/sketchfab/types.d.ts +10 -0
- package/dist/studio/AccessoryBrowserScreen.d.ts +6 -0
- package/dist/studio/AccessoryBrowserScreen.js +626 -0
- package/dist/studio/AccessoryPanel.d.ts +10 -0
- package/dist/studio/AccessoryPanel.js +396 -0
- package/dist/studio/AppearancePanel.d.ts +9 -0
- package/dist/studio/AppearancePanel.js +77 -0
- package/dist/studio/AvatarCreatorScreen.d.ts +5 -0
- package/dist/studio/AvatarCreatorScreen.js +806 -0
- package/dist/studio/AvatarEditorScreen.d.ts +14 -0
- package/dist/studio/AvatarEditorScreen.js +510 -0
- package/dist/studio/AvatarGrid.d.ts +23 -0
- package/dist/studio/AvatarGrid.js +257 -0
- package/dist/studio/ColorSwatch.d.ts +8 -0
- package/dist/studio/ColorSwatch.js +100 -0
- package/dist/studio/CreateVoiceProfileSheet.d.ts +8 -0
- package/dist/studio/CreateVoiceProfileSheet.js +242 -0
- package/dist/studio/DetailsPanel.d.ts +15 -0
- package/dist/studio/DetailsPanel.js +239 -0
- package/dist/studio/FilamentEditor.d.ts +2 -0
- package/dist/studio/FilamentEditor.js +6 -0
- package/dist/studio/PrecisionPanel.d.ts +2 -0
- package/dist/studio/PrecisionPanel.js +7 -0
- package/dist/studio/PublicGalleryScreen.d.ts +5 -0
- package/dist/studio/PublicGalleryScreen.js +358 -0
- package/dist/studio/SketchfabModelCard.d.ts +20 -0
- package/dist/studio/SketchfabModelCard.js +104 -0
- package/dist/studio/StudioBrowseHeader.d.ts +9 -0
- package/dist/studio/StudioBrowseHeader.js +28 -0
- package/dist/studio/StudioEmptyState.d.ts +8 -0
- package/dist/studio/StudioEmptyState.js +29 -0
- package/dist/studio/StudioFloatingAction.d.ts +13 -0
- package/dist/studio/StudioFloatingAction.js +42 -0
- package/dist/studio/StudioSectionHeader.d.ts +7 -0
- package/dist/studio/StudioSectionHeader.js +27 -0
- package/dist/studio/StudioSurfaceCard.d.ts +8 -0
- package/dist/studio/StudioSurfaceCard.js +20 -0
- package/dist/studio/VoicePanel.d.ts +15 -0
- package/dist/studio/VoicePanel.js +305 -0
- package/dist/studio/constants.d.ts +3 -0
- package/dist/studio/constants.js +6 -0
- package/dist/studio/index.d.ts +29 -0
- package/dist/studio/index.js +54 -0
- package/dist/studio/useSketchfabCapabilities.d.ts +31 -0
- package/dist/studio/useSketchfabCapabilities.js +82 -0
- package/dist/tts/useDirectVisemeStream.js +15 -10
- package/dist/utils/avatarUtils.js +92 -5
- package/dist/utils/faceLandmarkerToShapeWeights.js +2 -4
- package/dist/voice/useAudioPlayer.js +17 -4
- package/dist/voice/useVoicePreview.js +4 -2
- package/dist/wardrobe/index.d.ts +1 -0
- package/dist/wardrobe/index.js +6 -1
- package/dist/wardrobe/useAccessoryGestures.d.ts +20 -0
- package/dist/wardrobe/useAccessoryGestures.js +94 -0
- package/dist/wardrobe/useAvatarWardrobeHydration.js +8 -2
- package/dist/wardrobe/useStudioAvatar.js +11 -2
- package/dist/wardrobe/wardrobeStore.d.ts +2 -0
- package/dist/wardrobe/wardrobeStore.js +12 -2
- package/dist/wgpu/R3FWebGpuCanvas.d.ts +15 -0
- package/dist/wgpu/R3FWebGpuCanvas.js +176 -0
- package/dist/wgpu/WgpuAvatar.d.ts +26 -2
- package/dist/wgpu/WgpuAvatar.js +296 -39
- package/dist/wgpu/accessoryDefaults.d.ts +12 -0
- package/dist/wgpu/accessoryDefaults.js +19 -0
- package/dist/wgpu/blobShim.d.ts +2 -0
- package/dist/wgpu/blobShim.js +191 -0
- package/dist/wgpu/index.d.ts +1 -0
- package/dist/wgpu/index.js +4 -1
- package/dist/wgpu/loadGLTFFromUri.d.ts +2 -0
- package/dist/wgpu/loadGLTFFromUri.js +75 -0
- package/dist/wgpu/morphTables.js +21 -10
- package/dist/wgpu/motionState.d.ts +20 -0
- package/dist/wgpu/motionState.js +31 -0
- package/dist/wgpu/patchThreeForRN.d.ts +28 -0
- package/dist/wgpu/patchThreeForRN.js +292 -0
- package/dist/wgpu/scenePlacement.d.ts +5 -0
- package/dist/wgpu/scenePlacement.js +50 -0
- package/dist/wgpu/useAuthedModelUri.js +4 -2
- package/dist/wgpu/useNativeGLTF.d.ts +7 -0
- package/dist/wgpu/useNativeGLTF.js +36 -0
- package/package.json +97 -31
|
@@ -53,7 +53,7 @@ function useStudioAvatar({ avatarId }) {
|
|
|
53
53
|
}
|
|
54
54
|
}, [avatarId, populateFromAvatar]);
|
|
55
55
|
(0, react_1.useEffect)(() => {
|
|
56
|
-
fetchAvatar();
|
|
56
|
+
void fetchAvatar();
|
|
57
57
|
}, [fetchAvatar]);
|
|
58
58
|
const equipped = (0, wardrobeStore_1.useWardrobeStore)((s) => s.equipped);
|
|
59
59
|
const placements = (0, wardrobeStore_1.useWardrobeStore)((s) => s.placements);
|
|
@@ -74,7 +74,16 @@ function useStudioAvatar({ avatarId }) {
|
|
|
74
74
|
return true;
|
|
75
75
|
if (voiceProfileId !== avatar.default_voice_profile_id)
|
|
76
76
|
return true;
|
|
77
|
-
const serialized =
|
|
77
|
+
const serialized = Object.values(equipped).map((asset) => {
|
|
78
|
+
const placement = placements[asset.id];
|
|
79
|
+
return {
|
|
80
|
+
asset_id: asset.id,
|
|
81
|
+
bone: placement?.bone ?? 'Head',
|
|
82
|
+
position: placement?.position ?? [0, 0, 0],
|
|
83
|
+
rotation: placement?.rotation ?? [0, 0, 0],
|
|
84
|
+
scale: placement?.scale ?? 1,
|
|
85
|
+
};
|
|
86
|
+
});
|
|
78
87
|
const serverAccs = avatar.appearance?.equippedAccessories ?? [];
|
|
79
88
|
if (serialized.length !== serverAccs.length)
|
|
80
89
|
return true;
|
|
@@ -17,6 +17,8 @@ export interface WardrobeState {
|
|
|
17
17
|
gizmoMode: GizmoMode;
|
|
18
18
|
/** ID of the avatar currently synced to avoid redundant fetches */
|
|
19
19
|
syncedAvatarId: string | null;
|
|
20
|
+
/** ID of the avatar currently being fetched, used to ignore stale sync results. */
|
|
21
|
+
syncingAvatarId: string | null;
|
|
20
22
|
equip: (slot: string, asset: WearableAsset) => void;
|
|
21
23
|
unequip: (slot: string) => void;
|
|
22
24
|
setPlacement: (assetId: string, placement: AssetPlacement) => void;
|
|
@@ -22,12 +22,12 @@ exports.useWardrobeStore = (0, zustand_1.create)()((set, get) => ({
|
|
|
22
22
|
activeAssetId: null,
|
|
23
23
|
gizmoMode: 'translate',
|
|
24
24
|
syncedAvatarId: null,
|
|
25
|
+
syncingAvatarId: null,
|
|
25
26
|
equip: (slot, asset) => set((state) => ({
|
|
26
27
|
equipped: { ...state.equipped, [slot]: asset },
|
|
27
28
|
})),
|
|
28
29
|
unequip: (slot) => set((state) => {
|
|
29
30
|
const removed = state.equipped[slot];
|
|
30
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
31
31
|
const { [slot]: _removed, ...restEquipped } = state.equipped;
|
|
32
32
|
// Clean up placement data for the removed asset
|
|
33
33
|
const placements = { ...state.placements };
|
|
@@ -70,18 +70,26 @@ exports.useWardrobeStore = (0, zustand_1.create)()((set, get) => ({
|
|
|
70
70
|
return;
|
|
71
71
|
if (get().syncedAvatarId === avatarId)
|
|
72
72
|
return;
|
|
73
|
+
if (get().syncingAvatarId === avatarId)
|
|
74
|
+
return;
|
|
73
75
|
get().reset();
|
|
76
|
+
set({ syncingAvatarId: avatarId });
|
|
74
77
|
try {
|
|
75
78
|
const [avatar, allAssets] = await Promise.all([(0, studioApi_1.getAvatar)(avatarId), (0, studioApi_1.listAssets)()]);
|
|
79
|
+
if (get().syncingAvatarId !== avatarId)
|
|
80
|
+
return;
|
|
76
81
|
const lookup = {};
|
|
77
82
|
for (const a of allAssets)
|
|
78
83
|
lookup[a.id] = a;
|
|
79
84
|
const accessories = avatar.appearance?.equippedAccessories ?? [];
|
|
80
85
|
get().hydrateFromApi(accessories, lookup);
|
|
81
|
-
set({ syncedAvatarId: avatarId });
|
|
86
|
+
set({ syncedAvatarId: avatarId, syncingAvatarId: null });
|
|
82
87
|
}
|
|
83
88
|
catch (e) {
|
|
84
89
|
console.warn('[WardrobeStore] Failed to load avatar accessories:', e);
|
|
90
|
+
if (get().syncingAvatarId === avatarId) {
|
|
91
|
+
set({ syncingAvatarId: null });
|
|
92
|
+
}
|
|
85
93
|
}
|
|
86
94
|
},
|
|
87
95
|
serializeForApi: () => {
|
|
@@ -102,5 +110,7 @@ exports.useWardrobeStore = (0, zustand_1.create)()((set, get) => ({
|
|
|
102
110
|
placements: {},
|
|
103
111
|
activeAssetId: null,
|
|
104
112
|
gizmoMode: 'translate',
|
|
113
|
+
syncedAvatarId: null,
|
|
114
|
+
syncingAvatarId: null,
|
|
105
115
|
}),
|
|
106
116
|
}));
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { type ReactNode } from 'react';
|
|
2
|
+
import { type StyleProp, type ViewStyle } from 'react-native';
|
|
3
|
+
import * as THREE from 'three';
|
|
4
|
+
import type { RootState } from '@react-three/fiber';
|
|
5
|
+
type CameraProps = THREE.Camera | Record<string, unknown>;
|
|
6
|
+
interface R3FWebGpuCanvasProps {
|
|
7
|
+
children: ReactNode;
|
|
8
|
+
style?: StyleProp<ViewStyle>;
|
|
9
|
+
camera?: CameraProps;
|
|
10
|
+
clearColor?: string;
|
|
11
|
+
onCreated?: (state: RootState) => void;
|
|
12
|
+
onError?: (message: string) => void;
|
|
13
|
+
}
|
|
14
|
+
export declare function R3FWebGpuCanvas({ children, style, camera, clearColor, onCreated, onError, }: R3FWebGpuCanvasProps): import("react/jsx-runtime").JSX.Element;
|
|
15
|
+
export {};
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.R3FWebGpuCanvas = R3FWebGpuCanvas;
|
|
37
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
38
|
+
const react_1 = require("react");
|
|
39
|
+
const react_native_1 = require("react-native");
|
|
40
|
+
const THREE = __importStar(require("three"));
|
|
41
|
+
const fiber_1 = require("@react-three/fiber");
|
|
42
|
+
const react_native_wgpu_1 = require("react-native-wgpu");
|
|
43
|
+
function formatErrorMessage(error) {
|
|
44
|
+
if (error instanceof Error)
|
|
45
|
+
return error.message;
|
|
46
|
+
return String(error);
|
|
47
|
+
}
|
|
48
|
+
class ReactNativeCanvas {
|
|
49
|
+
constructor(canvas) {
|
|
50
|
+
this.canvas = canvas;
|
|
51
|
+
}
|
|
52
|
+
get width() { return this.canvas.width; }
|
|
53
|
+
set width(width) { this.canvas.width = width; }
|
|
54
|
+
get height() { return this.canvas.height; }
|
|
55
|
+
set height(height) { this.canvas.height = height; }
|
|
56
|
+
get clientWidth() { return this.canvas.width; }
|
|
57
|
+
set clientWidth(width) { this.canvas.width = width; }
|
|
58
|
+
get clientHeight() { return this.canvas.height; }
|
|
59
|
+
set clientHeight(height) { this.canvas.height = height; }
|
|
60
|
+
addEventListener() { }
|
|
61
|
+
removeEventListener() { }
|
|
62
|
+
dispatchEvent() { }
|
|
63
|
+
setPointerCapture() { }
|
|
64
|
+
releasePointerCapture() { }
|
|
65
|
+
}
|
|
66
|
+
const THREE_WITH_WEBGPU = THREE;
|
|
67
|
+
(0, fiber_1.extend)(THREE);
|
|
68
|
+
function R3FWebGpuCanvas({ children, style, camera, clearColor = '#000', onCreated, onError, }) {
|
|
69
|
+
const canvasRef = (0, react_1.useRef)(null);
|
|
70
|
+
const rootRef = (0, react_1.useRef)(null);
|
|
71
|
+
const contextRef = (0, react_1.useRef)(null);
|
|
72
|
+
const rendererRef = (0, react_1.useRef)(null);
|
|
73
|
+
const surfaceRef = (0, react_1.useRef)(null);
|
|
74
|
+
const rendererPatchedRef = (0, react_1.useRef)(false);
|
|
75
|
+
const rendererInitializedRef = (0, react_1.useRef)(false);
|
|
76
|
+
const [layoutSize, setLayoutSize] = (0, react_1.useState)({ width: 0, height: 0 });
|
|
77
|
+
const [configured, setConfigured] = (0, react_1.useState)(false);
|
|
78
|
+
const childrenRef = (0, react_1.useRef)(children);
|
|
79
|
+
(0, react_1.useEffect)(() => {
|
|
80
|
+
childrenRef.current = children;
|
|
81
|
+
}, [children]);
|
|
82
|
+
const handleLayout = (0, react_1.useCallback)((event) => {
|
|
83
|
+
const { width, height } = event.nativeEvent.layout;
|
|
84
|
+
setLayoutSize((prev) => (prev.width === width && prev.height === height ? prev : { width, height }));
|
|
85
|
+
}, []);
|
|
86
|
+
(0, react_1.useEffect)(() => {
|
|
87
|
+
if (!canvasRef.current || layoutSize.width <= 0 || layoutSize.height <= 0)
|
|
88
|
+
return;
|
|
89
|
+
let cancelled = false;
|
|
90
|
+
const configure = async () => {
|
|
91
|
+
try {
|
|
92
|
+
if (!contextRef.current) {
|
|
93
|
+
contextRef.current = canvasRef.current?.getContext('webgpu');
|
|
94
|
+
}
|
|
95
|
+
const context = contextRef.current;
|
|
96
|
+
if (!context)
|
|
97
|
+
throw new Error('Unable to acquire a WebGPU canvas context');
|
|
98
|
+
const surface = surfaceRef.current ?? new ReactNativeCanvas(context.canvas);
|
|
99
|
+
surfaceRef.current = surface;
|
|
100
|
+
const dpr = react_native_1.PixelRatio.get();
|
|
101
|
+
surface.width = Math.max(1, Math.round(layoutSize.width * dpr));
|
|
102
|
+
surface.height = Math.max(1, Math.round(layoutSize.height * dpr));
|
|
103
|
+
const root = rootRef.current ?? (0, fiber_1.createRoot)(surface);
|
|
104
|
+
rootRef.current = root;
|
|
105
|
+
const renderer = rendererRef.current ?? new THREE_WITH_WEBGPU.WebGPURenderer({
|
|
106
|
+
alpha: false,
|
|
107
|
+
antialias: true,
|
|
108
|
+
canvas: surface,
|
|
109
|
+
context,
|
|
110
|
+
});
|
|
111
|
+
rendererRef.current = renderer;
|
|
112
|
+
if (!rendererInitializedRef.current) {
|
|
113
|
+
await renderer.init();
|
|
114
|
+
rendererInitializedRef.current = true;
|
|
115
|
+
}
|
|
116
|
+
await root.configure({
|
|
117
|
+
dpr: 1,
|
|
118
|
+
frameloop: 'always',
|
|
119
|
+
size: {
|
|
120
|
+
width: surface.clientWidth,
|
|
121
|
+
height: surface.clientHeight,
|
|
122
|
+
top: 0,
|
|
123
|
+
left: 0,
|
|
124
|
+
},
|
|
125
|
+
camera: camera,
|
|
126
|
+
gl: renderer,
|
|
127
|
+
onCreated: (state) => {
|
|
128
|
+
if (!rendererPatchedRef.current) {
|
|
129
|
+
const originalRender = state.gl.render.bind(state.gl);
|
|
130
|
+
state.gl.render = (scene, camera) => {
|
|
131
|
+
const result = originalRender(scene, camera);
|
|
132
|
+
context.present();
|
|
133
|
+
return result;
|
|
134
|
+
};
|
|
135
|
+
rendererPatchedRef.current = true;
|
|
136
|
+
}
|
|
137
|
+
state.scene.background = new THREE.Color(clearColor);
|
|
138
|
+
onCreated?.(state);
|
|
139
|
+
},
|
|
140
|
+
});
|
|
141
|
+
if (!cancelled) {
|
|
142
|
+
root.render(childrenRef.current);
|
|
143
|
+
setConfigured(true);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
catch (error) {
|
|
147
|
+
if (!cancelled) {
|
|
148
|
+
setConfigured(false);
|
|
149
|
+
onError?.(formatErrorMessage(error));
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
void configure();
|
|
154
|
+
return () => {
|
|
155
|
+
cancelled = true;
|
|
156
|
+
};
|
|
157
|
+
}, [camera, clearColor, layoutSize.height, layoutSize.width, onCreated, onError]);
|
|
158
|
+
(0, react_1.useEffect)(() => {
|
|
159
|
+
if (!configured || !rootRef.current)
|
|
160
|
+
return;
|
|
161
|
+
rootRef.current.render(children);
|
|
162
|
+
}, [children, configured]);
|
|
163
|
+
(0, react_1.useEffect)(() => () => {
|
|
164
|
+
rootRef.current?.unmount();
|
|
165
|
+
rootRef.current = null;
|
|
166
|
+
contextRef.current = null;
|
|
167
|
+
rendererRef.current = null;
|
|
168
|
+
surfaceRef.current = null;
|
|
169
|
+
rendererPatchedRef.current = false;
|
|
170
|
+
rendererInitializedRef.current = false;
|
|
171
|
+
}, []);
|
|
172
|
+
return ((0, jsx_runtime_1.jsx)(react_native_1.View, { style: [styles.container, style], onLayout: handleLayout, children: (0, jsx_runtime_1.jsx)(react_native_wgpu_1.Canvas, { ref: canvasRef, style: react_native_1.StyleSheet.absoluteFill, transparent: true }) }));
|
|
173
|
+
}
|
|
174
|
+
const styles = react_native_1.StyleSheet.create({
|
|
175
|
+
container: { overflow: 'hidden' },
|
|
176
|
+
});
|
|
@@ -1,26 +1,49 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* WgpuAvatar — drop-in replacement for FilamentAvatar using react-three-fiber
|
|
3
|
-
* + react-native-wgpu
|
|
3
|
+
* + react-native-wgpu. Exposes the same ref interface as
|
|
4
4
|
* FilamentAvatarRef so siteclaw can swap renderers without changing call sites.
|
|
5
5
|
*
|
|
6
6
|
* No SurfaceTexture, no choreographer, no JNI surface lifecycle bugs.
|
|
7
7
|
*
|
|
8
8
|
* Peer deps required by the host app:
|
|
9
|
-
* react-native-wgpu
|
|
9
|
+
* react-native-wgpu
|
|
10
10
|
* @react-three/fiber >= 8
|
|
11
11
|
* @react-three/drei >= 9
|
|
12
12
|
* three >= 0.170
|
|
13
13
|
*/
|
|
14
|
+
import './blobShim';
|
|
14
15
|
import React from 'react';
|
|
15
16
|
import { type StyleProp, type ViewStyle } from 'react-native';
|
|
16
17
|
import * as THREE from 'three';
|
|
17
18
|
import type { TalkingHeadMood, TalkingHeadAccessory, TalkingHeadVisemeSchedule } from '../index';
|
|
19
|
+
import { type MotionKey, type TalkingHeadGesture, type TalkingHeadPose } from '../core/avatar/motion';
|
|
18
20
|
export interface WgpuAvatarRef {
|
|
19
21
|
setMood: (mood: TalkingHeadMood) => void;
|
|
20
22
|
sendAmplitude: (amplitude: number) => void;
|
|
21
23
|
sendViseme: (viseme: string, weight?: number) => void;
|
|
22
24
|
scheduleVisemes: (schedule: TalkingHeadVisemeSchedule) => void;
|
|
23
25
|
clearVisemes: () => void;
|
|
26
|
+
/** Play a procedural body motion (e.g. 'attack', 'defend', 'groove'). */
|
|
27
|
+
playMotion(name: MotionKey): void;
|
|
28
|
+
playMotion(name: string): void;
|
|
29
|
+
/** Stop the active procedural motion and restore the rest pose. */
|
|
30
|
+
stopMotion: () => void;
|
|
31
|
+
/** Procedural fallback for upstream TalkingHead gestures on native WGPU. */
|
|
32
|
+
playGesture: (name: TalkingHeadGesture | string, opts?: {
|
|
33
|
+
ms?: number;
|
|
34
|
+
}) => void;
|
|
35
|
+
stopGesture: () => void;
|
|
36
|
+
/** Procedural fallback for upstream TalkingHead pose templates on native WGPU. */
|
|
37
|
+
playPose: (name: TalkingHeadPose | string) => void;
|
|
38
|
+
stopPose: () => void;
|
|
39
|
+
/** Play an animation clip from a GLB loaded by the native WGPU path. */
|
|
40
|
+
playAnimation: (url: string, opts?: {
|
|
41
|
+
dur?: number;
|
|
42
|
+
index?: number;
|
|
43
|
+
}) => void;
|
|
44
|
+
stopAnimation: () => void;
|
|
45
|
+
/** Triggers a single natural blink — use as a reaction to accessory snap-on. */
|
|
46
|
+
triggerBlink: () => void;
|
|
24
47
|
}
|
|
25
48
|
interface WgpuAvatarProps {
|
|
26
49
|
style?: StyleProp<ViewStyle>;
|
|
@@ -32,6 +55,7 @@ interface WgpuAvatarProps {
|
|
|
32
55
|
accessories?: TalkingHeadAccessory[];
|
|
33
56
|
onReady?: () => void;
|
|
34
57
|
onError?: (message: string) => void;
|
|
58
|
+
onAvatarState?: (state: string) => void;
|
|
35
59
|
/** Called once the R3F scene and camera are available (for editor raycasting). */
|
|
36
60
|
onSceneReady?: (scene: THREE.Scene, camera: THREE.Camera) => void;
|
|
37
61
|
}
|