@tomorrowevening/hermes 0.0.35 → 0.0.37

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 (71) hide show
  1. package/dist/hermes.cjs.js +144 -0
  2. package/dist/hermes.esm.js +3849 -0
  3. package/dist/hermes.umd.js +144 -0
  4. package/dist/style.css +1 -1
  5. package/package.json +20 -16
  6. package/src/core/Application.ts +111 -0
  7. package/src/core/RemoteController.ts +60 -0
  8. package/src/core/remote/BaseRemote.ts +16 -0
  9. package/src/core/remote/RemoteComponents.ts +45 -0
  10. package/src/core/remote/RemoteTheatre.ts +300 -0
  11. package/src/core/remote/RemoteThree.ts +143 -0
  12. package/src/core/remote/RemoteTweakpane.ts +194 -0
  13. package/src/core/types.ts +56 -0
  14. package/src/editor/Editor.tsx +20 -0
  15. package/src/editor/components/Draggable.tsx +40 -0
  16. package/src/editor/components/DraggableItem.tsx +22 -0
  17. package/src/editor/components/Dropdown.tsx +38 -0
  18. package/src/editor/components/DropdownItem.tsx +64 -0
  19. package/src/editor/components/NavButton.tsx +11 -0
  20. package/src/editor/components/content.ts +2 -0
  21. package/src/editor/components/icons/CloseIcon.tsx +7 -0
  22. package/src/editor/components/icons/DragIcon.tsx +9 -0
  23. package/src/editor/components/types.ts +41 -0
  24. package/src/editor/global.ts +20 -0
  25. package/src/editor/multiView/CameraWindow.tsx +74 -0
  26. package/src/editor/multiView/InfiniteGridHelper.ts +24 -0
  27. package/src/editor/multiView/InfiniteGridMaterial.ts +127 -0
  28. package/src/editor/multiView/MultiView.scss +101 -0
  29. package/src/editor/multiView/MultiView.tsx +636 -0
  30. package/src/editor/multiView/MultiViewData.ts +59 -0
  31. package/src/editor/multiView/UVMaterial.ts +55 -0
  32. package/src/editor/scss/_debug.scss +58 -0
  33. package/src/editor/scss/_draggable.scss +43 -0
  34. package/src/editor/scss/_dropdown.scss +84 -0
  35. package/src/editor/scss/_sidePanel.scss +278 -0
  36. package/src/editor/scss/_theme.scss +9 -0
  37. package/src/editor/scss/index.scss +67 -0
  38. package/src/editor/sidePanel/Accordion.tsx +41 -0
  39. package/src/editor/sidePanel/ChildObject.tsx +57 -0
  40. package/src/editor/sidePanel/ContainerObject.tsx +11 -0
  41. package/src/editor/sidePanel/SidePanel.tsx +64 -0
  42. package/src/editor/sidePanel/ToggleBtn.tsx +27 -0
  43. package/src/editor/sidePanel/inspector/Inspector.tsx +119 -0
  44. package/src/editor/sidePanel/inspector/InspectorField.tsx +198 -0
  45. package/src/editor/sidePanel/inspector/InspectorGroup.tsx +50 -0
  46. package/src/editor/sidePanel/inspector/SceneInspector.tsx +84 -0
  47. package/src/editor/sidePanel/inspector/inspector.scss +161 -0
  48. package/src/editor/sidePanel/inspector/utils/InspectAnimation.tsx +102 -0
  49. package/src/editor/sidePanel/inspector/utils/InspectCamera.tsx +75 -0
  50. package/src/editor/sidePanel/inspector/utils/InspectLight.tsx +62 -0
  51. package/src/editor/sidePanel/inspector/utils/InspectMaterial.tsx +710 -0
  52. package/src/editor/sidePanel/inspector/utils/InspectTransform.tsx +113 -0
  53. package/src/editor/sidePanel/types.ts +130 -0
  54. package/src/editor/sidePanel/utils.ts +278 -0
  55. package/src/editor/utils.ts +117 -0
  56. package/src/example/CustomEditor.tsx +78 -0
  57. package/src/example/components/App.css +6 -0
  58. package/src/example/components/App.tsx +246 -0
  59. package/src/example/constants.ts +52 -0
  60. package/src/example/index.scss +45 -0
  61. package/src/example/main.tsx +37 -0
  62. package/src/example/three/BaseScene.ts +42 -0
  63. package/src/example/three/CustomMaterial.ts +72 -0
  64. package/src/example/three/FBXAnimation.ts +26 -0
  65. package/src/example/three/Scene1.ts +225 -0
  66. package/src/example/three/Scene2.ts +138 -0
  67. package/src/example/three/loader.ts +110 -0
  68. package/src/index.ts +27 -0
  69. package/src/vite-env.d.ts +1 -0
  70. package/dist/hermes.js +0 -3901
  71. package/dist/hermes.umd.cjs +0 -144
@@ -0,0 +1,6 @@
1
+ #box {
2
+ background: #FF0000;
3
+ width: 100px;
4
+ height: 100px;
5
+ position: absolute;
6
+ }
@@ -0,0 +1,246 @@
1
+ // Libs
2
+ import React, { useEffect, useRef, useState } from 'react';
3
+ import { types } from '@theatre/core';
4
+ import { WebGLRenderer } from 'three';
5
+ import Stats from 'stats-gl';
6
+ // Models
7
+ import { app, Events, IS_DEV, threeDispatcher } from '../constants';
8
+ // Components
9
+ import './App.css';
10
+ import BaseScene from '../three/BaseScene';
11
+ import Scene1 from '../three/Scene1';
12
+ import Scene2 from '../three/Scene2';
13
+ import { debugDispatcher, ToolEvents } from '../../editor/global';
14
+ import { loadAssets } from '../three/loader';
15
+ import { dispose } from '../../editor/utils';
16
+ import SceneInspector from '../../editor/sidePanel/inspector/SceneInspector';
17
+ import RemoteTheatre from '../../core/remote/RemoteTheatre';
18
+
19
+ let renderer: WebGLRenderer;
20
+ let currentScene: BaseScene;
21
+ let sceneName = '';
22
+
23
+ const useTweakpane = false;
24
+
25
+ function App() {
26
+ const elementRef = useRef<HTMLDivElement>(null);
27
+ const canvasRef = useRef<HTMLCanvasElement>(null);
28
+ const [loaded, setLoaded] = useState(false);
29
+
30
+ // Theatre
31
+ useEffect(() => {
32
+ if (!loaded) return;
33
+ if (app.theatre === undefined) return;
34
+
35
+ const container = elementRef.current!;
36
+ container.style.visibility = app.editor ? 'hidden' : 'inherit';
37
+
38
+ // Theatre Example
39
+ app.theatre.sheet('App');
40
+ const sheetObj = app.theatre?.sheetObject(
41
+ 'App',
42
+ 'Box',
43
+ {
44
+ x: types.number(100, {range: [0, window.innerWidth]}),
45
+ y: types.number(100, {range: [0, window.innerHeight]}),
46
+ },
47
+ (values: any) => {
48
+ container.style.left = `${values.x}px`;
49
+ container.style.top = `${values.y}px`;
50
+ },
51
+ );
52
+ return () => {
53
+ if (sheetObj !== undefined) app.theatre?.unsubscribe(sheetObj);
54
+ app.dispose();
55
+ };
56
+ }, [loaded]);
57
+
58
+ // Renderer setup
59
+ if (!app.editor) {
60
+ useEffect(() => {
61
+ renderer = new WebGLRenderer({
62
+ canvas: canvasRef.current!,
63
+ stencil: false
64
+ });
65
+ renderer.autoClear = false;
66
+ renderer.shadowMap.enabled = true;
67
+ renderer.setPixelRatio(devicePixelRatio);
68
+ renderer.setClearColor(0x000000);
69
+ return () => {
70
+ renderer.dispose();
71
+ };
72
+ }, []);
73
+ }
74
+
75
+ // ThreeJS
76
+ useEffect(() => {
77
+ if (!loaded) return;
78
+
79
+ let stats: Stats;
80
+ if (!app.editor) {
81
+ stats = new Stats();
82
+ stats.init(renderer);
83
+ document.body.appendChild(stats.dom);
84
+ }
85
+
86
+ // Start RAF
87
+ let raf = -1;
88
+
89
+ const onResize = () => {
90
+ const width = window.innerWidth;
91
+ const height = window.innerHeight;
92
+ currentScene?.resize(width, height);
93
+ renderer.setSize(width, height);
94
+ };
95
+
96
+ const updateApp = () => {
97
+ RemoteTheatre.getRafDriver().tick(performance.now());
98
+ currentScene?.update();
99
+ stats.begin();
100
+ renderer.clear();
101
+ currentScene?.draw();
102
+ stats.end();
103
+ stats.update();
104
+ raf = requestAnimationFrame(updateApp);
105
+ };
106
+
107
+ if (!app.editor) {
108
+ window.addEventListener('resize', onResize);
109
+ onResize();
110
+ updateApp();
111
+ }
112
+
113
+ return () => {
114
+ if (currentScene !== undefined) {
115
+ app.three.removeCamera(currentScene.camera);
116
+ dispose(currentScene);
117
+ }
118
+ window.removeEventListener('resize', onResize);
119
+ cancelAnimationFrame(raf);
120
+ raf = -1;
121
+ };
122
+ }, [loaded]);
123
+
124
+ // Preload
125
+ useEffect(() => {
126
+ const onLoad = () => {
127
+ threeDispatcher.removeEventListener(Events.LOAD_COMPLETE, onLoad);
128
+ setLoaded(true);
129
+ };
130
+ threeDispatcher.addEventListener(Events.LOAD_COMPLETE, onLoad);
131
+ loadAssets();
132
+ }, [setLoaded]);
133
+
134
+ const createScene = () => {
135
+ if (currentScene !== undefined) {
136
+ if (currentScene.camera !== undefined) app.three.removeCamera(currentScene.camera);
137
+ dispose(currentScene);
138
+ }
139
+ if (sceneName === 'scene1') {
140
+ currentScene = new Scene1(renderer);
141
+ } else {
142
+ currentScene = new Scene2(renderer);
143
+ }
144
+ currentScene.resize(window.innerWidth, window.innerHeight);
145
+ app.three.setScene(currentScene);
146
+ app.three.addCamera(currentScene.camera);
147
+ };
148
+
149
+ const createScene1 = () => {
150
+ if (sceneName === 'scene1') return;
151
+ sceneName = 'scene1';
152
+ createScene();
153
+ };
154
+
155
+ const createScene2 = () => {
156
+ if (sceneName === 'scene2') return;
157
+ sceneName = 'scene2';
158
+ createScene();
159
+ };
160
+
161
+ // Debug Components
162
+ if (IS_DEV) {
163
+ useEffect(() => {
164
+ const container = elementRef.current!;
165
+ container.style.visibility = app.editor ? 'hidden' : 'inherit';
166
+
167
+ // Tweakpane Example
168
+ if (useTweakpane) {
169
+ const testFolder = app.debug.addFolder('Test Folder');
170
+
171
+ app.debug.button('Test Button', () => {
172
+ console.log('Test button works!');
173
+ }, testFolder);
174
+
175
+ const test = { opacity: 1, rotation: 0 };
176
+ app.debug.bind(test, 'opacity', {
177
+ min: 0,
178
+ max: 1,
179
+ onChange: (value: number) => {
180
+ container.style.opacity = value.toFixed(2);
181
+ }
182
+ }, testFolder);
183
+
184
+ app.debug.bind(test, 'rotation', {
185
+ min: 0,
186
+ max: 360,
187
+ onChange: (value: number) => {
188
+ container.style.transform = `rotate(${value}deg)`;
189
+ }
190
+ }, testFolder);
191
+ }
192
+
193
+ // Components Example
194
+ const onCustom = (evt: any) => {
195
+ console.log('Custom:', evt.value);
196
+ };
197
+ const selectDropdown = (evt: any) => {
198
+ const scene = evt.value.value;
199
+ if (scene === 'scene1') {
200
+ createScene1();
201
+ } else if (scene === 'scene2') {
202
+ createScene2();
203
+ }
204
+ };
205
+ debugDispatcher.addEventListener(ToolEvents.CUSTOM, onCustom);
206
+ debugDispatcher.addEventListener(ToolEvents.SELECT_DROPDOWN, selectDropdown);
207
+ return () => {
208
+ debugDispatcher.removeEventListener(ToolEvents.CUSTOM, onCustom);
209
+ debugDispatcher.removeEventListener(ToolEvents.SELECT_DROPDOWN, selectDropdown);
210
+ };
211
+ }, []);
212
+ }
213
+
214
+ return (
215
+ <>
216
+ {!loaded && <p className='loading'>Loading...</p>}
217
+ {app.isApp && <canvas ref={canvasRef} />}
218
+
219
+ <div id='box' ref={elementRef}>
220
+ <button onClick={() => {
221
+ // app.send({
222
+ // target: 'editor',
223
+ // event: 'custom',
224
+ // data: 'hello editor!'
225
+ // });
226
+ app.theatre.playSheet('Scene1');
227
+ }}>Click</button>
228
+ </div>
229
+
230
+ <div style={{
231
+ position: 'absolute',
232
+ bottom: '20px',
233
+ left: '20px',
234
+ }}>
235
+ <button onClick={createScene1}>Scene 1</button>
236
+ <button onClick={createScene2}>Scene 2</button>
237
+ </div>
238
+
239
+ {IS_DEV && (
240
+ <SceneInspector three={app.three} />
241
+ )}
242
+ </>
243
+ );
244
+ }
245
+
246
+ export default App;
@@ -0,0 +1,52 @@
1
+ import { EventDispatcher } from 'three';
2
+ import Application from '../core/Application';
3
+ import RemoteComponents from '../core/remote/RemoteComponents';
4
+ import RemoteTheatre from '../core/remote/RemoteTheatre';
5
+ import RemoteThree from '../core/remote/RemoteThree';
6
+ import RemoteTweakpane from '../core/remote/RemoteTweakpane';
7
+ import { json } from './three/loader';
8
+
9
+ export const IS_DEV = true;
10
+
11
+ class CustomApp extends Application {
12
+ constructor() {
13
+ super('ws://localhost:8080', IS_DEV);
14
+
15
+ this.addComponent('theatre', new RemoteTheatre(this));
16
+ this.addComponent('three', new RemoteThree(this));
17
+
18
+ if (IS_DEV) {
19
+ this.addComponent('components', new RemoteComponents(this));
20
+ this.addComponent('debug', new RemoteTweakpane(this));
21
+ }
22
+ }
23
+
24
+ onLoad(): Promise<void> {
25
+ const state = json.get('animation');
26
+ return this.theatre.init('RemoteApp', { state });
27
+ }
28
+
29
+ // Components
30
+
31
+ get debug(): RemoteTweakpane {
32
+ return this.components.get('debug') as RemoteTweakpane;
33
+ }
34
+
35
+ get debugComponents(): RemoteComponents {
36
+ return this.components.get('components') as RemoteComponents;
37
+ }
38
+
39
+ get theatre(): RemoteTheatre {
40
+ return this.components.get('theatre') as RemoteTheatre;
41
+ }
42
+
43
+ get three(): RemoteThree {
44
+ return this.components.get('three') as RemoteThree;
45
+ }
46
+ }
47
+
48
+ export const app = new CustomApp();
49
+ export const threeDispatcher = new EventDispatcher();
50
+ export const Events = {
51
+ LOAD_COMPLETE: 'Events::loadComplete'
52
+ };
@@ -0,0 +1,45 @@
1
+ :root {
2
+ font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
3
+ line-height: 1.5;
4
+ font-weight: 400;
5
+
6
+ color-scheme: light dark;
7
+ color: rgba(255, 255, 255, 0.87);
8
+ background-color: #242424;
9
+
10
+ font-synthesis: none;
11
+ text-rendering: optimizeLegibility;
12
+ -webkit-font-smoothing: antialiased;
13
+ -moz-osx-font-smoothing: grayscale;
14
+ -webkit-text-size-adjust: 100%;
15
+ }
16
+
17
+ html, body {
18
+ min-width: 100%;
19
+ min-height: 100%;
20
+ width: 100%;
21
+ height: 100%;
22
+ }
23
+
24
+ body {
25
+ padding: 0;
26
+ margin: 0;
27
+ overflow: hidden;
28
+ }
29
+
30
+ button {
31
+ &:focus {
32
+ outline: none;
33
+ }
34
+
35
+ &:hover {
36
+ cursor: pointer;
37
+ }
38
+ }
39
+
40
+ .loading {
41
+ position: absolute;
42
+ left: 50%;
43
+ top: 50%;
44
+ transform: translate(calc(-50% - 150px), -50%);
45
+ }
@@ -0,0 +1,37 @@
1
+ // Libs
2
+ import React from 'react';
3
+ import ReactDOM from 'react-dom/client';
4
+ import studio from '@theatre/studio';
5
+ // Models
6
+ import { app, IS_DEV } from './constants';
7
+ // Components
8
+ import './index.scss';
9
+ import App from './components/App';
10
+ import CustomEditor from './CustomEditor';
11
+ // Tools
12
+ import RemoteController from '../core/RemoteController';
13
+ import RemoteTheatre from '../core/remote/RemoteTheatre';
14
+
15
+ // Debug tools
16
+ if (IS_DEV) {
17
+ studio.initialize({ __experimental_rafDriver: RemoteTheatre.getRafDriver() });
18
+ if (app.isApp) studio.ui.hide();
19
+ RemoteController(app);
20
+ }
21
+
22
+ // React
23
+
24
+ ReactDOM.createRoot(document.getElementById('root')!).render(
25
+ <>
26
+ {IS_DEV ? (
27
+ <>
28
+ <App />
29
+ {app.editor ? <CustomEditor /> : null}
30
+ </>
31
+ ) : (
32
+ <React.StrictMode>
33
+ <App />
34
+ </React.StrictMode>
35
+ )}
36
+ </>,
37
+ );
@@ -0,0 +1,42 @@
1
+ import { Clock, Object3D, PerspectiveCamera, Scene, WebGLRenderer } from 'three';
2
+ import { app } from '../constants';
3
+
4
+ export default class BaseScene extends Scene {
5
+ camera: PerspectiveCamera;
6
+ renderer: WebGLRenderer;
7
+ clock: Clock;
8
+
9
+ constructor(renderer: WebGLRenderer) {
10
+ super();
11
+ this.renderer = renderer;
12
+ this.clock = new Clock();
13
+ this.clock.start();
14
+
15
+ const cameras = new Object3D();
16
+ cameras.name = 'cameras';
17
+ this.add(cameras);
18
+
19
+ this.camera = new PerspectiveCamera(90, 1, 10, 1000);
20
+ this.camera.name = 'Main';
21
+ this.camera.position.set(0, 100, 125);
22
+ this.camera.lookAt(0, 50, 0);
23
+ cameras.add(this.camera);
24
+ }
25
+
26
+ dispose() {
27
+ app.theatre.clearSheetObjects(this.name);
28
+ }
29
+
30
+ resize(width: number, height: number) {
31
+ this.camera.aspect = width / height;
32
+ this.camera.updateProjectionMatrix();
33
+ }
34
+
35
+ update() {
36
+ //
37
+ }
38
+
39
+ draw() {
40
+ this.renderer.render(this, this.camera);
41
+ }
42
+ }
@@ -0,0 +1,72 @@
1
+ import { Color, Matrix3, Matrix4, ShaderMaterial, Texture, Vector3 } from 'three';
2
+ import { textureFromSrc } from '../../editor/sidePanel/utils';
3
+
4
+ const vertex = `varying vec2 vUv;
5
+
6
+ void main() {
7
+ vUv = uv;
8
+ gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
9
+ }`;
10
+
11
+ const fragment = `uniform float time;
12
+ uniform float opacity;
13
+ uniform vec3 diffuse;
14
+ uniform vec3 mouse;
15
+ uniform sampler2D map;
16
+ varying vec2 vUv;
17
+
18
+ #define MIN_ALPHA 2.0 / 255.0
19
+
20
+ void main() {
21
+ if (opacity < MIN_ALPHA) discard;
22
+ vec2 imageUV = vUv * 10.0 + (mouse.xy * mouse.z);
23
+ vec3 image = texture2D(map, imageUV).rgb;
24
+ vec3 col = image * diffuse;
25
+ col += (sin(time * 0.1) * 0.5 + 0.5) * 0.2;
26
+ gl_FragColor = vec4(col, opacity);
27
+ }`;
28
+
29
+ const smile = `data:image/png;base64,R0lGODlhCAAIAIAAAP///wAAACH/C1hNUCBEYXRhWE1QPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgOS4xLWMwMDEgNzkuMTQ2Mjg5OTc3NywgMjAyMy8wNi8yNS0yMzo1NzoxNCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIDI1LjIgKE1hY2ludG9zaCkiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6NjVENjQyODM4QTYzMTFFRUJFQTBFOTJFNjA1OTQ5N0YiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6NjVENjQyODQ4QTYzMTFFRUJFQTBFOTJFNjA1OTQ5N0YiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDo2NUQ2NDI4MThBNjMxMUVFQkVBMEU5MkU2MDU5NDk3RiIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDo2NUQ2NDI4MjhBNjMxMUVFQkVBMEU5MkU2MDU5NDk3RiIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PgH//v38+/r5+Pf29fTz8vHw7+7t7Ovq6ejn5uXk4+Lh4N/e3dzb2tnY19bV1NPS0dDPzs3My8rJyMfGxcTDwsHAv769vLu6ubi3trW0s7KxsK+urayrqqmop6alpKOioaCfnp2cm5qZmJeWlZSTkpGQj46NjIuKiYiHhoWEg4KBgH9+fXx7enl4d3Z1dHNycXBvbm1sa2ppaGdmZWRjYmFgX15dXFtaWVhXVlVUU1JRUE9OTUxLSklIR0ZFRENCQUA/Pj08Ozo5ODc2NTQzMjEwLy4tLCsqKSgnJiUkIyIhIB8eHRwbGhkYFxYVFBMSERAPDg0MCwoJCAcGBQQDAgEAACH5BAAAAAAALAAAAAAIAAgAAAIOjH8AprzRkHyspqSoNAUAOw==`;
30
+
31
+ export default class CustomMaterial extends ShaderMaterial {
32
+ constructor() {
33
+ super({
34
+ vertexShader: vertex,
35
+ fragmentShader: fragment,
36
+ name: 'ExampleScene/SimpleShader',
37
+ transparent: true,
38
+ uniforms: {
39
+ diffuse: {
40
+ value: new Color(0xffffff)
41
+ },
42
+ opacity: {
43
+ value: 1,
44
+ },
45
+ time: {
46
+ value: 0,
47
+ },
48
+ map: {
49
+ value: null,
50
+ },
51
+ mouse: {
52
+ value: new Vector3()
53
+ },
54
+ testM3: {
55
+ value: new Matrix3()
56
+ },
57
+ testM4: {
58
+ value: new Matrix4()
59
+ },
60
+ },
61
+ });
62
+
63
+ textureFromSrc(smile).then((texture: Texture) => {
64
+ this.uniforms.map.value = texture;
65
+ });
66
+ }
67
+
68
+ update(delta: number) {
69
+ this.uniforms.opacity.value = this.opacity;
70
+ this.uniforms.time.value += delta;
71
+ }
72
+ }
@@ -0,0 +1,26 @@
1
+ import { AnimationMixer, Object3D } from 'three';
2
+ import { clone } from 'three/examples/jsm/utils/SkeletonUtils';
3
+ import { models } from './loader';
4
+
5
+ export default class FBXAnimation extends Object3D {
6
+ private mixer: AnimationMixer;
7
+
8
+ constructor(name: string) {
9
+ super();
10
+ this.name = name;
11
+ this.scale.setScalar(0.5);
12
+
13
+ const model = models.get(name)!;
14
+ const modelInstance = clone(model);
15
+ this.add(modelInstance);
16
+
17
+ this.mixer = new AnimationMixer(modelInstance);
18
+ modelInstance['mixer'] = this.mixer;
19
+ const action = this.mixer.clipAction(modelInstance.animations[0]);
20
+ action.play();
21
+ }
22
+
23
+ update(delta: number) {
24
+ this.mixer.update(delta);
25
+ }
26
+ }