@tresjs/core 5.9.2 → 5.10.0
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/dist/index-CbIRI9tI.d.ts +1270 -0
- package/dist/src-DL__Foe9.js +2946 -0
- package/dist/template-compiler-options.d.mts +1 -1
- package/dist/template-compiler-options.mjs +1 -1
- package/dist/tres.d.ts +3 -1217
- package/dist/tres.js +3 -2571
- package/dist/webgpu.d.ts +193 -0
- package/dist/webgpu.js +361 -0
- package/package.json +12 -3
|
@@ -0,0 +1,2946 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* name: @tresjs/core
|
|
3
|
+
* version: v5.10.0
|
|
4
|
+
* (c) 2026
|
|
5
|
+
* description: Declarative ThreeJS using Vue Components
|
|
6
|
+
* author: Alvaro Saburido <hola@alvarosaburido.dev> (https://github.com/alvarosabu/)
|
|
7
|
+
*/
|
|
8
|
+
import { Fragment, Teleport, computed, createBlock, createCommentVNode, createElementBlock, createRenderer, defineComponent, getCurrentInstance, h, isRef, mergeDefaults, mergeProps, nextTick, normalizeClass, normalizeStyle, onBeforeUnmount, onMounted, onUnmounted, openBlock, provide, reactive, readonly, ref, renderSlot, shallowRef, toHandlers, toValue, triggerRef, unref, useSlots, watch, watchEffect, withCtx } from "vue";
|
|
9
|
+
import { createEventHook, createInjectionState, promiseTimeout, refDebounced, tryOnScopeDispose, unrefElement, useAsyncState, useDevicePixelRatio, useElementSize, useFps, useIntervalFn, useMemory, useRafFn, useTimeout, useWindowSize, whenever } from "@vueuse/core";
|
|
10
|
+
import * as THREE from "three";
|
|
11
|
+
import { ACESFilmicToneMapping, ArrowHelper, BufferAttribute, Color, DirectionalLightHelper, DoubleSide, HemisphereLightHelper, Layers, Material, MathUtils, Mesh, MeshBasicMaterial, PCFShadowMap, PCFSoftShadowMap, PerspectiveCamera, PointLightHelper, REVISION, Scene, SpotLightHelper, Vector3 } from "three";
|
|
12
|
+
import { camel, isEqual, isFunction, isNumber, isObject, isString, isUndefined } from "radashi";
|
|
13
|
+
import { forwardHtmlEvents, getVoidObject } from "@pmndrs/pointer-events";
|
|
14
|
+
import { setupDevtoolsPlugin } from "@vue/devtools-api";
|
|
15
|
+
import { RectAreaLightHelper } from "three/examples/jsm/helpers/RectAreaLightHelper.js";
|
|
16
|
+
//#region src/utils/makeMap.ts
|
|
17
|
+
/**
|
|
18
|
+
* Make a map and return a function for checking if a key
|
|
19
|
+
* is in that map.
|
|
20
|
+
* IMPORTANT: all calls of this function must be prefixed with
|
|
21
|
+
* \/\*#\_\_PURE\_\_\*\/
|
|
22
|
+
* So that rollup can tree-shake them if necessary.
|
|
23
|
+
*/
|
|
24
|
+
/*! #__NO_SIDE_EFFECTS__ */
|
|
25
|
+
function makeMap(str) {
|
|
26
|
+
const map = Object.create(null);
|
|
27
|
+
for (const key of str.split(",")) map[key] = 1;
|
|
28
|
+
return (val) => val in map;
|
|
29
|
+
}
|
|
30
|
+
const isHTMLTag = /* #__PURE__ */ makeMap("html,body,base,head,link,meta,style,title,address,article,aside,footer,header,hgroup,h1,h2,h3,h4,h5,h6,nav,section,div,dd,dl,dt,figcaption,figure,picture,hr,img,li,main,ol,p,pre,ul,a,b,abbr,bdi,bdo,br,cite,code,data,dfn,em,i,kbd,mark,q,rp,rt,ruby,s,samp,small,span,strong,sub,sup,time,u,var,wbr,area,audio,map,track,video,embed,object,param,source,canvas,script,noscript,del,ins,caption,col,colgroup,table,thead,tbody,td,th,tr,button,datalist,fieldset,form,input,label,legend,meter,optgroup,option,output,progress,select,textarea,details,dialog,menu,summary,template,blockquote,iframe,tfoot");
|
|
31
|
+
//#endregion
|
|
32
|
+
//#region src/utils/is/util.ts
|
|
33
|
+
const createTypeGuard = (property) => (value) => isObject(value) && property in value && !!value[property];
|
|
34
|
+
//#endregion
|
|
35
|
+
//#region src/utils/is/three.ts
|
|
36
|
+
/**
|
|
37
|
+
* Type guard to check if a value is a Three.js Object3D
|
|
38
|
+
* @param value - The value to check
|
|
39
|
+
* @returns True if the value is a Three.js Object3D instance, false otherwise
|
|
40
|
+
* @example
|
|
41
|
+
* ```ts
|
|
42
|
+
* const value = new THREE.Object3D()
|
|
43
|
+
* if (isObject3D(value)) {
|
|
44
|
+
* // TypeScript knows value is Object3D here
|
|
45
|
+
* value.position // OK
|
|
46
|
+
* value.rotation // OK
|
|
47
|
+
* value.scale // OK
|
|
48
|
+
* }
|
|
49
|
+
* ```
|
|
50
|
+
*/
|
|
51
|
+
const isObject3D = createTypeGuard("isObject3D");
|
|
52
|
+
/**
|
|
53
|
+
* Type guard to check if a value is a Three.js Mesh
|
|
54
|
+
* @param value - The value to check
|
|
55
|
+
* @returns True if the value is a Three.js Mesh instance, false otherwise
|
|
56
|
+
* @example
|
|
57
|
+
* ```ts
|
|
58
|
+
* const value = new THREE.Mesh()
|
|
59
|
+
* if (isMesh(value)) {
|
|
60
|
+
* // TypeScript knows value is Mesh here
|
|
61
|
+
* value.geometry // OK
|
|
62
|
+
*/
|
|
63
|
+
const isMesh = createTypeGuard("isMesh");
|
|
64
|
+
/**
|
|
65
|
+
* Type guard to check if a value is a Three.js Camera
|
|
66
|
+
* @param value - The value to check
|
|
67
|
+
* @returns True if the value is a Three.js Camera instance, false otherwise
|
|
68
|
+
* @example
|
|
69
|
+
* ```ts
|
|
70
|
+
* const value = new THREE.PerspectiveCamera()
|
|
71
|
+
* if (isCamera(value)) {
|
|
72
|
+
* // TypeScript knows value is Camera here
|
|
73
|
+
* value.fov // OK
|
|
74
|
+
* value.near // OK
|
|
75
|
+
* value.far // OK
|
|
76
|
+
* }
|
|
77
|
+
* ```
|
|
78
|
+
*/
|
|
79
|
+
const isCamera = createTypeGuard("isCamera");
|
|
80
|
+
/**
|
|
81
|
+
* Type guard to check if a value is a Three.js OrthographicCamera
|
|
82
|
+
* @param value - The value to check
|
|
83
|
+
* @returns True if the value is a Three.js OrthographicCamera instance, false otherwise
|
|
84
|
+
*/
|
|
85
|
+
const isOrthographicCamera = createTypeGuard("isOrthographicCamera");
|
|
86
|
+
/**
|
|
87
|
+
* Type guard to check if a value is a Three.js PerspectiveCamera
|
|
88
|
+
* @param value - The value to check
|
|
89
|
+
* @returns True if the value is a Three.js PerspectiveCamera instance, false otherwise
|
|
90
|
+
*/
|
|
91
|
+
const isPerspectiveCamera = createTypeGuard("isPerspectiveCamera");
|
|
92
|
+
/**
|
|
93
|
+
* Type guard to check if a value is a Three.js Color
|
|
94
|
+
* @param value - The value to check
|
|
95
|
+
* @returns True if the value is a Three.js Color instance, false otherwise
|
|
96
|
+
*/
|
|
97
|
+
const isColor = createTypeGuard("isColor");
|
|
98
|
+
/**
|
|
99
|
+
* Type guard to check if a value is a Three.js ColorRepresentation
|
|
100
|
+
* @param value - The value to check
|
|
101
|
+
* @returns True if the value is a Three.js ColorRepresentation instance, false otherwise
|
|
102
|
+
*/
|
|
103
|
+
const isColorRepresentation = (value) => isString(value) || isNumber(value) || isColor(value);
|
|
104
|
+
/**
|
|
105
|
+
* Type guard to check if a value is a Three.js Layers
|
|
106
|
+
* @param value - The value to check
|
|
107
|
+
* @returns True if the value is a Three.js Layers instance, false otherwise
|
|
108
|
+
*/
|
|
109
|
+
const isLayers = (value) => value instanceof Layers;
|
|
110
|
+
/**
|
|
111
|
+
* Type guard to check if a value is a Three.js BufferGeometry
|
|
112
|
+
* @param value - The value to check
|
|
113
|
+
* @returns True if the value is a Three.js BufferGeometry instance, false otherwise
|
|
114
|
+
* @example
|
|
115
|
+
* ```ts
|
|
116
|
+
* const value = new THREE.BufferGeometry()
|
|
117
|
+
* if (isBufferGeometry(value)) {
|
|
118
|
+
* // TypeScript knows value is BufferGeometry here
|
|
119
|
+
* value.attributes // OK
|
|
120
|
+
* value.index // OK
|
|
121
|
+
* value.computeVertexNormals() // OK
|
|
122
|
+
* }
|
|
123
|
+
* ```
|
|
124
|
+
*/
|
|
125
|
+
const isBufferGeometry = createTypeGuard("isBufferGeometry");
|
|
126
|
+
/**
|
|
127
|
+
* Type guard to check if a value is a Three.js Material
|
|
128
|
+
* @param value - The value to check
|
|
129
|
+
* @returns True if the value is a Three.js Material instance, false otherwise
|
|
130
|
+
* @example
|
|
131
|
+
* ```ts
|
|
132
|
+
* const value = new THREE.MeshStandardMaterial()
|
|
133
|
+
* if (isMaterial(value)) {
|
|
134
|
+
* // TypeScript knows value is Material here
|
|
135
|
+
* value.color // OK
|
|
136
|
+
* value.metalness // OK
|
|
137
|
+
* value.roughness // OK
|
|
138
|
+
* }
|
|
139
|
+
* ```
|
|
140
|
+
*/
|
|
141
|
+
const isMaterial = createTypeGuard("isMaterial");
|
|
142
|
+
/**
|
|
143
|
+
* Type guard to check if a value is a Three.js NodeMaterial
|
|
144
|
+
* @param value - The value to check
|
|
145
|
+
* @returns True if the value is a Three.js NodeMaterial instance, false otherwise
|
|
146
|
+
*/
|
|
147
|
+
const isNodeMaterial = createTypeGuard("isNodeMaterial");
|
|
148
|
+
/**
|
|
149
|
+
* Type guard to check if a value is a Three.js TSL Node
|
|
150
|
+
* @param value - The value to check
|
|
151
|
+
* @returns True if the value is a Three.js Node instance, false otherwise
|
|
152
|
+
*/
|
|
153
|
+
const isNode = createTypeGuard("isNode");
|
|
154
|
+
/**
|
|
155
|
+
* Type guard to check if a value is a Three.js Light
|
|
156
|
+
* @param value - The value to check
|
|
157
|
+
* @returns True if the value is a Three.js Light instance, false otherwise
|
|
158
|
+
* @example
|
|
159
|
+
* ```ts
|
|
160
|
+
* const value = new THREE.DirectionalLight()
|
|
161
|
+
* if (isLight(value)) {
|
|
162
|
+
* // TypeScript knows value is Light here
|
|
163
|
+
* value.intensity // OK
|
|
164
|
+
* value.color // OK
|
|
165
|
+
* value.position // OK
|
|
166
|
+
* }
|
|
167
|
+
* ```
|
|
168
|
+
*/
|
|
169
|
+
const isLight = createTypeGuard("isLight");
|
|
170
|
+
/**
|
|
171
|
+
* Type guard to check if a value is a Three.js Fog
|
|
172
|
+
* @param value - The value to check
|
|
173
|
+
* @returns True if the value is a Three.js Fog instance, false otherwise
|
|
174
|
+
* @example
|
|
175
|
+
* ```ts
|
|
176
|
+
* const value = new THREE.Fog(0x000000, 1, 1000)
|
|
177
|
+
* if (isFog(value)) {
|
|
178
|
+
* // TypeScript knows value is Fog here
|
|
179
|
+
* value.color // OK
|
|
180
|
+
* value.near // OK
|
|
181
|
+
* value.far // OK
|
|
182
|
+
* }
|
|
183
|
+
* ```
|
|
184
|
+
*/
|
|
185
|
+
const isFog = createTypeGuard("isFog");
|
|
186
|
+
/**
|
|
187
|
+
* Type guard to check if a value is a Three.js Scene
|
|
188
|
+
* @param value - The value to check
|
|
189
|
+
* @returns True if the value is a Three.js Scene instance, false otherwise
|
|
190
|
+
* @example
|
|
191
|
+
* ```ts
|
|
192
|
+
* const value = new THREE.Scene()
|
|
193
|
+
* if (isScene(value)) {
|
|
194
|
+
* // TypeScript knows value is Scene here
|
|
195
|
+
* value.children // OK
|
|
196
|
+
* value.add(new THREE.Object3D()) // OK
|
|
197
|
+
* value.remove(new THREE.Object3D()) // OK
|
|
198
|
+
* }
|
|
199
|
+
* ```
|
|
200
|
+
*/
|
|
201
|
+
const isScene = createTypeGuard("isScene");
|
|
202
|
+
/**
|
|
203
|
+
* Type guard to check if a value is a Three.js Group
|
|
204
|
+
* @param value - The value to check
|
|
205
|
+
* @returns True if the value is a Three.js Group instance, false otherwise
|
|
206
|
+
* ```
|
|
207
|
+
*/
|
|
208
|
+
const isGroup = createTypeGuard("isGroup");
|
|
209
|
+
//#endregion
|
|
210
|
+
//#region src/utils/is/tres.ts
|
|
211
|
+
const isVectorLike = (value) => value !== null && typeof value === "object" && "set" in value && typeof value.set === "function";
|
|
212
|
+
const isCopyable = (value) => isVectorLike(value) && "copy" in value && typeof value.copy === "function";
|
|
213
|
+
const isClassInstance = (object) => !!object?.constructor;
|
|
214
|
+
/**
|
|
215
|
+
* Type guard to check if a value is a TresCamera
|
|
216
|
+
* @param value - The value to check
|
|
217
|
+
* @returns True if the value is a TresCamera instance, false otherwise
|
|
218
|
+
*/
|
|
219
|
+
const isTresCamera = (value) => isCamera(value) || isOrthographicCamera(value) || isPerspectiveCamera(value);
|
|
220
|
+
/**
|
|
221
|
+
* Type guard to check if a value is a TresObject
|
|
222
|
+
* @param value - The value to check
|
|
223
|
+
* @returns True if the value is a TresObject (Object3D | BufferGeometry | Material | Fog), false otherwise
|
|
224
|
+
* @example
|
|
225
|
+
* ```ts
|
|
226
|
+
* const value = new THREE.Mesh()
|
|
227
|
+
* if (isTresObject(value)) {
|
|
228
|
+
* // TypeScript knows value is TresObject here
|
|
229
|
+
* // You can use common properties and methods shared by all TresObjects
|
|
230
|
+
* }
|
|
231
|
+
* ```
|
|
232
|
+
* @remarks
|
|
233
|
+
* TresObject is a union type that represents the core Three.js objects that can be used in TresJS.
|
|
234
|
+
* This includes Object3D, BufferGeometry, Material, and Fog instances.
|
|
235
|
+
*/
|
|
236
|
+
const isTresObject = (value) => isObject3D(value) || isBufferGeometry(value) || isMaterial(value) || isFog(value);
|
|
237
|
+
/**
|
|
238
|
+
* Type guard to check if a value is a TresPrimitive
|
|
239
|
+
* @param value - The value to check
|
|
240
|
+
* @returns True if the value is a TresPrimitive instance, false otherwise
|
|
241
|
+
* @example
|
|
242
|
+
* ```ts
|
|
243
|
+
* const value = { isPrimitive: true }
|
|
244
|
+
* if (isTresPrimitive(value)) {
|
|
245
|
+
* // TypeScript knows value is TresPrimitive here
|
|
246
|
+
* // You can use properties and methods specific to TresPrimitives
|
|
247
|
+
* }
|
|
248
|
+
* ```
|
|
249
|
+
* @remarks
|
|
250
|
+
* TresPrimitive is a special type in TresJS that represents primitive objects
|
|
251
|
+
* that can be used directly in the scene without needing to be wrapped in a Three.js object.
|
|
252
|
+
*/
|
|
253
|
+
const isTresPrimitive = createTypeGuard("isPrimitive");
|
|
254
|
+
/**
|
|
255
|
+
* Type guard to check if a value is a TresInstance (has __tres property)
|
|
256
|
+
* @param value - The value to check
|
|
257
|
+
* @returns True if the value is a TresInstance (has __tres property), false otherwise
|
|
258
|
+
* @example
|
|
259
|
+
* ```ts
|
|
260
|
+
* const value = new THREE.Mesh()
|
|
261
|
+
* if (isTresInstance(value)) {
|
|
262
|
+
* // TypeScript knows value is TresInstance here
|
|
263
|
+
* // You can safely access value.__tres
|
|
264
|
+
* }
|
|
265
|
+
* ```
|
|
266
|
+
*/
|
|
267
|
+
const isTresInstance = (value) => isTresObject(value) && "__tres" in value;
|
|
268
|
+
/**
|
|
269
|
+
* Type guard to check if a value is a Three.js WebGPU `Renderer`.
|
|
270
|
+
*
|
|
271
|
+
* Identifies the renderer *instance*, not the active backend: a `WebGPURenderer`
|
|
272
|
+
* can run on a WebGPU **or** a WebGL2 fallback backend, and this returns `true`
|
|
273
|
+
* for both. That is the right granularity for branching on capabilities —
|
|
274
|
+
* `NodeMaterial` works on either backend, while a GLSL `ShaderMaterial` works on
|
|
275
|
+
* neither under the WebGPU renderer.
|
|
276
|
+
* @param value - The value to check
|
|
277
|
+
* @returns True if the value is a WebGPU `Renderer` instance, false otherwise
|
|
278
|
+
* @example
|
|
279
|
+
* ```ts
|
|
280
|
+
* const { renderer } = useTresContext()
|
|
281
|
+
* if (isWebGPURenderer(renderer.instance)) {
|
|
282
|
+
* // TypeScript knows renderer.instance is a WebGPU Renderer here
|
|
283
|
+
* }
|
|
284
|
+
* ```
|
|
285
|
+
*/
|
|
286
|
+
const isWebGPURenderer = createTypeGuard("isRenderer");
|
|
287
|
+
/**
|
|
288
|
+
* Type guard to check if a value is a Three.js `WebGLRenderer`.
|
|
289
|
+
* @param value - The value to check
|
|
290
|
+
* @returns True if the value is a `WebGLRenderer` instance, false otherwise
|
|
291
|
+
* @see {@link isWebGPURenderer}
|
|
292
|
+
*/
|
|
293
|
+
const isWebGLRenderer = createTypeGuard("isWebGLRenderer");
|
|
294
|
+
//#endregion
|
|
295
|
+
//#region src/composables/useRenderer/pixelRatio.ts
|
|
296
|
+
const setPixelRatio = (renderer, systemDpr, userDpr) => {
|
|
297
|
+
if (!isFunction(renderer.setPixelRatio)) return;
|
|
298
|
+
let newDpr = 0;
|
|
299
|
+
if (userDpr && Array.isArray(userDpr) && userDpr.length >= 2) {
|
|
300
|
+
const [min, max] = userDpr;
|
|
301
|
+
newDpr = MathUtils.clamp(systemDpr, min, max);
|
|
302
|
+
} else if (isNumber(userDpr)) newDpr = userDpr;
|
|
303
|
+
else newDpr = systemDpr;
|
|
304
|
+
if (newDpr !== renderer.getPixelRatio?.()) renderer.setPixelRatio(newDpr);
|
|
305
|
+
};
|
|
306
|
+
//#endregion
|
|
307
|
+
//#region src/utils/logger.ts
|
|
308
|
+
/**
|
|
309
|
+
* Logger utility for TresJS
|
|
310
|
+
* @module logger
|
|
311
|
+
*/
|
|
312
|
+
function resolveRuntimeMode() {
|
|
313
|
+
try {
|
|
314
|
+
const modeFromImportMeta = import.meta.env?.MODE;
|
|
315
|
+
if (modeFromImportMeta) return modeFromImportMeta;
|
|
316
|
+
} catch {}
|
|
317
|
+
return typeof process !== "undefined" && process.env && process.env.NODE_ENV ? process.env.NODE_ENV : "production";
|
|
318
|
+
}
|
|
319
|
+
const isProd = resolveRuntimeMode() === "production";
|
|
320
|
+
const logPrefix = "[TresJS ▲ ■ ●] ";
|
|
321
|
+
/**
|
|
322
|
+
* Logs an error message with the TresJS prefix
|
|
323
|
+
* @param args - Arguments to log
|
|
324
|
+
*/
|
|
325
|
+
function logError(...args) {
|
|
326
|
+
if (typeof args[0] === "string") args[0] = logPrefix + args[0];
|
|
327
|
+
else args.unshift(logPrefix);
|
|
328
|
+
console.error(...args);
|
|
329
|
+
}
|
|
330
|
+
/**
|
|
331
|
+
* Logs a warning message with the TresJS prefix
|
|
332
|
+
* @param args - Arguments to log
|
|
333
|
+
*/
|
|
334
|
+
function logWarning(...args) {
|
|
335
|
+
if (typeof args[0] === "string") args[0] = logPrefix + args[0];
|
|
336
|
+
else args.unshift(logPrefix);
|
|
337
|
+
console.warn(...args);
|
|
338
|
+
}
|
|
339
|
+
/**
|
|
340
|
+
* Logs a message with the TresJS prefix (only in development mode)
|
|
341
|
+
* @param name - Name of the message
|
|
342
|
+
* @param value - Value to log
|
|
343
|
+
*/
|
|
344
|
+
function logMessage(name, value) {
|
|
345
|
+
if (!isProd) console.log(`${logPrefix} - ${name}:`, value);
|
|
346
|
+
}
|
|
347
|
+
//#endregion
|
|
348
|
+
//#region src/core/revision.ts
|
|
349
|
+
const revision = Number.parseInt(REVISION.replace("dev", ""));
|
|
350
|
+
//#endregion
|
|
351
|
+
//#region src/utils/createTimer.ts
|
|
352
|
+
/**
|
|
353
|
+
* Minimum Three.js revision that ships the `Timer` class.
|
|
354
|
+
*/
|
|
355
|
+
const TIMER_MIN_REVISION = 179;
|
|
356
|
+
/**
|
|
357
|
+
* Creates a unified timer abstraction that uses `Timer` on Three.js r179+
|
|
358
|
+
* and falls back to `Clock` on older versions.
|
|
359
|
+
*
|
|
360
|
+
* - On r179+: uses `Timer` with its Page Visibility API support (`connect`/`disconnect`).
|
|
361
|
+
* - On older versions: uses `Clock` with `start`/`stop`.
|
|
362
|
+
*
|
|
363
|
+
*/
|
|
364
|
+
function createTimer() {
|
|
365
|
+
if (revision >= TIMER_MIN_REVISION) {
|
|
366
|
+
const timer = new THREE.Timer();
|
|
367
|
+
return {
|
|
368
|
+
getDelta: () => timer.getDelta(),
|
|
369
|
+
getElapsed: () => timer.getElapsed(),
|
|
370
|
+
update: () => timer.update(),
|
|
371
|
+
start: () => {
|
|
372
|
+
if (typeof document !== "undefined") timer.connect(document);
|
|
373
|
+
},
|
|
374
|
+
stop: () => timer.disconnect()
|
|
375
|
+
};
|
|
376
|
+
} else {
|
|
377
|
+
const clock = new THREE.Clock();
|
|
378
|
+
return {
|
|
379
|
+
getDelta: () => clock.getDelta(),
|
|
380
|
+
getElapsed: () => clock.elapsedTime,
|
|
381
|
+
update: () => {},
|
|
382
|
+
start: () => clock.start(),
|
|
383
|
+
stop: () => clock.stop()
|
|
384
|
+
};
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
//#endregion
|
|
388
|
+
//#region src/composables/useCreateRafLoop/index.ts
|
|
389
|
+
/**
|
|
390
|
+
* @param cycleFn the function that is called before the after event hook is triggered and after the before event hook is triggered.
|
|
391
|
+
*/
|
|
392
|
+
const useCreateRafLoop = (cycleFn, { fpsLimit } = {}) => {
|
|
393
|
+
const timer = createTimer();
|
|
394
|
+
const eventHooks = {
|
|
395
|
+
before: createEventHook(),
|
|
396
|
+
after: createEventHook()
|
|
397
|
+
};
|
|
398
|
+
const { pause, resume, isActive } = useRafFn(() => {
|
|
399
|
+
timer.update();
|
|
400
|
+
const context = {
|
|
401
|
+
delta: timer.getDelta(),
|
|
402
|
+
elapsed: timer.getElapsed()
|
|
403
|
+
};
|
|
404
|
+
eventHooks.before.trigger(context);
|
|
405
|
+
cycleFn();
|
|
406
|
+
eventHooks.after.trigger(context);
|
|
407
|
+
}, {
|
|
408
|
+
immediate: false,
|
|
409
|
+
fpsLimit
|
|
410
|
+
});
|
|
411
|
+
const start = () => {
|
|
412
|
+
timer.start();
|
|
413
|
+
resume();
|
|
414
|
+
};
|
|
415
|
+
const stop = () => {
|
|
416
|
+
timer.stop();
|
|
417
|
+
pause();
|
|
418
|
+
};
|
|
419
|
+
return {
|
|
420
|
+
start,
|
|
421
|
+
stop,
|
|
422
|
+
isActive,
|
|
423
|
+
onBeforeLoop: eventHooks.before.on,
|
|
424
|
+
onLoop: eventHooks.after.on
|
|
425
|
+
};
|
|
426
|
+
};
|
|
427
|
+
//#endregion
|
|
428
|
+
//#region src/utils/error.ts
|
|
429
|
+
var TresRendererError = class extends Error {
|
|
430
|
+
code;
|
|
431
|
+
name = "TresRendererError";
|
|
432
|
+
constructor(message, code, options) {
|
|
433
|
+
super(message, options);
|
|
434
|
+
this.code = code;
|
|
435
|
+
}
|
|
436
|
+
};
|
|
437
|
+
//#endregion
|
|
438
|
+
//#region src/composables/useRenderer/useRendererManager.ts
|
|
439
|
+
/**
|
|
440
|
+
* Three.js revision where `WebGPURenderer` removed `PCFSoftShadowMap`.
|
|
441
|
+
*/
|
|
442
|
+
const WEBGPU_PCF_SOFT_REMOVED_REVISION = 186;
|
|
443
|
+
function useBaseRendererManager({ scene, canvas, options, fpsLimit, contextParts: { sizes, camera } }, createDefaultRenderer) {
|
|
444
|
+
const setupContext = {
|
|
445
|
+
sizes,
|
|
446
|
+
scene,
|
|
447
|
+
camera,
|
|
448
|
+
canvas
|
|
449
|
+
};
|
|
450
|
+
const getRenderer = () => {
|
|
451
|
+
if (isFunction(options.renderer)) return options.renderer(setupContext);
|
|
452
|
+
if (createDefaultRenderer) return createDefaultRenderer(setupContext, options);
|
|
453
|
+
throw new Error("No renderer: set the `renderer` option or pass a default renderer factory.");
|
|
454
|
+
};
|
|
455
|
+
const renderer = getRenderer();
|
|
456
|
+
const frames = ref(toValue(options.renderMode) === "manual" ? 0 : 1);
|
|
457
|
+
const maxFrames = 60;
|
|
458
|
+
const canBeInvalidated = computed(() => toValue(options.renderMode) === "on-demand" && frames.value === 0);
|
|
459
|
+
const forceMaterialUpdate = () => scene.value.traverse((child) => {
|
|
460
|
+
if (child instanceof Mesh && child.material instanceof Material) child.material.needsUpdate = true;
|
|
461
|
+
});
|
|
462
|
+
/**
|
|
463
|
+
* Invalidates the current frame when in on-demand render mode.
|
|
464
|
+
*/
|
|
465
|
+
const invalidate = (amountOfFramesToInvalidate = 1) => {
|
|
466
|
+
if (!canBeInvalidated.value) return;
|
|
467
|
+
frames.value = Math.min(maxFrames, frames.value + amountOfFramesToInvalidate);
|
|
468
|
+
};
|
|
469
|
+
/**
|
|
470
|
+
* Advances one frame when in manual render mode.
|
|
471
|
+
*/
|
|
472
|
+
const advance = () => {
|
|
473
|
+
if (toValue(options.renderMode) !== "manual") throw new Error("advance can only be called in manual render mode.");
|
|
474
|
+
frames.value = 1;
|
|
475
|
+
};
|
|
476
|
+
const invalidateOnDemand = () => {
|
|
477
|
+
if (toValue(options.renderMode) === "on-demand") invalidate();
|
|
478
|
+
};
|
|
479
|
+
const isModeAlways = computed(() => toValue(options.renderMode) === "always");
|
|
480
|
+
const readyEventHook = createEventHook();
|
|
481
|
+
const errorEventHook = createEventHook();
|
|
482
|
+
let hasTriggeredReady = false;
|
|
483
|
+
const isInitialized = ref(false);
|
|
484
|
+
const error = ref(null);
|
|
485
|
+
const initializeRenderer = async () => {
|
|
486
|
+
try {
|
|
487
|
+
if (isWebGPURenderer(renderer)) await renderer.init();
|
|
488
|
+
isInitialized.value = true;
|
|
489
|
+
} catch (e) {
|
|
490
|
+
const rendererError = new TresRendererError(e instanceof Error ? e.message : "Unknown error", "INITIALIZATION_FAILED", { cause: e instanceof Error ? e : void 0 });
|
|
491
|
+
error.value = rendererError;
|
|
492
|
+
console.error(`[TresJS] Renderer initialization failed. This may occur if:
|
|
493
|
+
- WebGPU is not supported by your browser
|
|
494
|
+
- GPU is not available or lacks required features
|
|
495
|
+
- GPU drivers are outdated
|
|
496
|
+
Error details: ${rendererError.message}`, rendererError);
|
|
497
|
+
errorEventHook.trigger(rendererError);
|
|
498
|
+
}
|
|
499
|
+
};
|
|
500
|
+
const renderEventHook = createEventHook();
|
|
501
|
+
const notifyFrameRendered = () => {
|
|
502
|
+
frames.value = isModeAlways.value ? 1 : Math.max(0, frames.value - 1);
|
|
503
|
+
renderEventHook.trigger(renderer);
|
|
504
|
+
};
|
|
505
|
+
let renderFunction = (_notifyFrameRendered) => {
|
|
506
|
+
if (camera.activeCamera.value) {
|
|
507
|
+
renderer.render(scene.value, camera.activeCamera.value);
|
|
508
|
+
_notifyFrameRendered();
|
|
509
|
+
}
|
|
510
|
+
};
|
|
511
|
+
const replaceRenderFunction = (fn) => {
|
|
512
|
+
renderFunction = fn;
|
|
513
|
+
};
|
|
514
|
+
const loop = useCreateRafLoop(() => {
|
|
515
|
+
if (frames.value) renderFunction(notifyFrameRendered);
|
|
516
|
+
}, { fpsLimit });
|
|
517
|
+
readyEventHook.on(() => {
|
|
518
|
+
if (isInitialized.value) loop.start();
|
|
519
|
+
});
|
|
520
|
+
watch([
|
|
521
|
+
sizes.width,
|
|
522
|
+
sizes.height,
|
|
523
|
+
isInitialized
|
|
524
|
+
], () => {
|
|
525
|
+
if (!isInitialized.value) return;
|
|
526
|
+
renderer.setSize(sizes.width.value, sizes.height.value);
|
|
527
|
+
if (!hasTriggeredReady && renderer.domElement.width && renderer.domElement.height) {
|
|
528
|
+
hasTriggeredReady = true;
|
|
529
|
+
nextTick(() => {
|
|
530
|
+
readyEventHook.trigger(renderer);
|
|
531
|
+
});
|
|
532
|
+
}
|
|
533
|
+
invalidateOnDemand();
|
|
534
|
+
}, { immediate: true });
|
|
535
|
+
watchEffect(() => {
|
|
536
|
+
if (!isInitialized.value) return;
|
|
537
|
+
setPixelRatio(renderer, sizes.pixelRatio.value, toValue(options.dpr));
|
|
538
|
+
});
|
|
539
|
+
initializeRenderer();
|
|
540
|
+
if (toValue(options.renderMode) === "on-demand") invalidate();
|
|
541
|
+
if (toValue(options.renderMode) === "manual") useTimeout(100, { callback: advance });
|
|
542
|
+
const clearColorAndAlpha = computed(() => {
|
|
543
|
+
const clearColor = toValue(options.clearColor);
|
|
544
|
+
const clearAlpha = toValue(options.clearAlpha);
|
|
545
|
+
const isClearColorWithAlpha = typeof clearColor === "string" && clearColor.length === 9 && clearColor.startsWith("#");
|
|
546
|
+
if (isClearColorWithAlpha && clearAlpha !== void 0) logWarning(`clearColor with alpha (e.g. ${clearColor}) and clearAlpha cannot both be set, using clearColor as source of truth`);
|
|
547
|
+
if (isClearColorWithAlpha) return {
|
|
548
|
+
alpha: Number.parseInt(clearColor.slice(7, 9), 16) / 255,
|
|
549
|
+
color: clearColor.slice(0, 7)
|
|
550
|
+
};
|
|
551
|
+
return {
|
|
552
|
+
alpha: clearAlpha,
|
|
553
|
+
color: clearColor
|
|
554
|
+
};
|
|
555
|
+
});
|
|
556
|
+
watchEffect(() => {
|
|
557
|
+
if (!isInitialized.value) return;
|
|
558
|
+
const value = clearColorAndAlpha.value;
|
|
559
|
+
if (value.color === void 0 || value.alpha === void 0) return;
|
|
560
|
+
renderer.setClearColor(value.color, value.alpha);
|
|
561
|
+
});
|
|
562
|
+
watchEffect(() => {
|
|
563
|
+
if (!isInitialized.value) return;
|
|
564
|
+
const value = options.toneMapping;
|
|
565
|
+
if (value) renderer.toneMapping = value;
|
|
566
|
+
});
|
|
567
|
+
watchEffect(() => {
|
|
568
|
+
if (!isInitialized.value) return;
|
|
569
|
+
const value = options.toneMappingExposure;
|
|
570
|
+
if (value) renderer.toneMappingExposure = value;
|
|
571
|
+
});
|
|
572
|
+
watchEffect(() => {
|
|
573
|
+
if (!isInitialized.value) return;
|
|
574
|
+
const value = options.outputColorSpace;
|
|
575
|
+
if (value) renderer.outputColorSpace = value;
|
|
576
|
+
});
|
|
577
|
+
watchEffect(() => {
|
|
578
|
+
if (!isInitialized.value) return;
|
|
579
|
+
const value = options.shadows;
|
|
580
|
+
if (value === void 0) return;
|
|
581
|
+
renderer.shadowMap.enabled = value;
|
|
582
|
+
forceMaterialUpdate();
|
|
583
|
+
});
|
|
584
|
+
watchEffect(() => {
|
|
585
|
+
if (!isInitialized.value) return;
|
|
586
|
+
const fallback = isWebGPURenderer(renderer) && revision < WEBGPU_PCF_SOFT_REMOVED_REVISION ? PCFSoftShadowMap : PCFShadowMap;
|
|
587
|
+
renderer.shadowMap.type = options.shadowMapType ?? fallback;
|
|
588
|
+
forceMaterialUpdate();
|
|
589
|
+
});
|
|
590
|
+
onUnmounted(() => {
|
|
591
|
+
renderer.dispose();
|
|
592
|
+
if ("forceContextLoss" in renderer) renderer.forceContextLoss();
|
|
593
|
+
});
|
|
594
|
+
return {
|
|
595
|
+
loop,
|
|
596
|
+
instance: renderer,
|
|
597
|
+
advance,
|
|
598
|
+
onReady: readyEventHook.on,
|
|
599
|
+
onRender: renderEventHook.on,
|
|
600
|
+
onError: errorEventHook.on,
|
|
601
|
+
invalidate,
|
|
602
|
+
canBeInvalidated,
|
|
603
|
+
mode: toValue(options.renderMode),
|
|
604
|
+
replaceRenderFunction,
|
|
605
|
+
isInitialized,
|
|
606
|
+
error
|
|
607
|
+
};
|
|
608
|
+
}
|
|
609
|
+
//#endregion
|
|
610
|
+
//#region src/core/catalogue.ts
|
|
611
|
+
const catalogue = ref({});
|
|
612
|
+
const extend = (objects) => Object.assign(catalogue.value, objects);
|
|
613
|
+
//#endregion
|
|
614
|
+
//#region src/composables/useCamera/index.ts
|
|
615
|
+
/**
|
|
616
|
+
* Composable for managing cameras in a Three.js scene
|
|
617
|
+
* @param params - The parameters for the composable
|
|
618
|
+
* @param params.sizes - The sizes object containing window dimensions
|
|
619
|
+
* @returns The camera management functions and state
|
|
620
|
+
*/
|
|
621
|
+
const useCameraManager = ({ sizes }) => {
|
|
622
|
+
const cameras = shallowRef([]);
|
|
623
|
+
const activeCamera = computed(() => cameras.value[0]);
|
|
624
|
+
/**
|
|
625
|
+
* Set the active camera
|
|
626
|
+
* @param cameraOrUuid - The camera or its UUID to set as active
|
|
627
|
+
*/
|
|
628
|
+
const setActiveCamera = (cameraOrUuid) => {
|
|
629
|
+
const camera = isCamera(cameraOrUuid) ? cameraOrUuid : cameras.value.find((camera) => camera.uuid === cameraOrUuid);
|
|
630
|
+
if (!camera) return;
|
|
631
|
+
const otherCameras = cameras.value.filter(({ uuid }) => uuid !== camera.uuid);
|
|
632
|
+
cameras.value = [camera, ...otherCameras];
|
|
633
|
+
};
|
|
634
|
+
/**
|
|
635
|
+
* Register a camera
|
|
636
|
+
* @param camera - The camera to register
|
|
637
|
+
* @param active - Whether to set the camera as active
|
|
638
|
+
*/
|
|
639
|
+
const registerCamera = (camera, active = false) => {
|
|
640
|
+
if (cameras.value.some(({ uuid }) => uuid === camera.uuid)) return;
|
|
641
|
+
cameras.value.push(camera);
|
|
642
|
+
triggerRef(cameras);
|
|
643
|
+
if (active) setActiveCamera(camera.uuid);
|
|
644
|
+
};
|
|
645
|
+
/**
|
|
646
|
+
* Deregister a camera
|
|
647
|
+
* @param camera - The camera to deregister
|
|
648
|
+
*/
|
|
649
|
+
const deregisterCamera = (camera) => {
|
|
650
|
+
cameras.value = cameras.value.filter(({ uuid }) => uuid !== camera.uuid);
|
|
651
|
+
};
|
|
652
|
+
/**
|
|
653
|
+
* Update camera aspect ratios when the window size changes
|
|
654
|
+
*/
|
|
655
|
+
watchEffect(() => {
|
|
656
|
+
if (sizes.aspectRatio.value) cameras.value.forEach((camera) => {
|
|
657
|
+
if (isPerspectiveCamera(camera)) {
|
|
658
|
+
camera.aspect = sizes.aspectRatio.value;
|
|
659
|
+
camera.updateProjectionMatrix();
|
|
660
|
+
}
|
|
661
|
+
});
|
|
662
|
+
});
|
|
663
|
+
return {
|
|
664
|
+
activeCamera,
|
|
665
|
+
cameras,
|
|
666
|
+
registerCamera,
|
|
667
|
+
deregisterCamera,
|
|
668
|
+
setActiveCamera
|
|
669
|
+
};
|
|
670
|
+
};
|
|
671
|
+
//#endregion
|
|
672
|
+
//#region src/composables/useSizes/index.ts
|
|
673
|
+
function useSizes(windowSize, canvas, debounceMs = 10) {
|
|
674
|
+
const { pixelRatio } = useDevicePixelRatio();
|
|
675
|
+
const reactiveSize = toValue(windowSize) ? useWindowSize() : useElementSize(computed(() => toValue(canvas).parentElement));
|
|
676
|
+
const debouncedReactiveWidth = readonly(refDebounced(reactiveSize.width, debounceMs));
|
|
677
|
+
const debouncedReactiveHeight = readonly(refDebounced(reactiveSize.height, debounceMs));
|
|
678
|
+
return {
|
|
679
|
+
width: debouncedReactiveWidth,
|
|
680
|
+
height: debouncedReactiveHeight,
|
|
681
|
+
pixelRatio,
|
|
682
|
+
aspectRatio: computed(() => debouncedReactiveWidth.value / debouncedReactiveHeight.value)
|
|
683
|
+
};
|
|
684
|
+
}
|
|
685
|
+
//#endregion
|
|
686
|
+
//#region src/composables/useEventManager/index.ts
|
|
687
|
+
function useEventManager({ canvas, contextParts: { scene, camera, renderer } }) {
|
|
688
|
+
const { update, destroy } = forwardHtmlEvents(toValue(canvas), () => toValue(camera.activeCamera), scene.value);
|
|
689
|
+
const { off } = renderer.loop.onLoop(update);
|
|
690
|
+
onUnmounted(destroy);
|
|
691
|
+
onUnmounted(off);
|
|
692
|
+
const voidObject = getVoidObject(scene.value);
|
|
693
|
+
const pointerMissedEventHook = createEventHook();
|
|
694
|
+
voidObject.addEventListener("click", pointerMissedEventHook.trigger);
|
|
695
|
+
return { onPointerMissed: pointerMissedEventHook.on };
|
|
696
|
+
}
|
|
697
|
+
//#endregion
|
|
698
|
+
//#region src/composables/useTresContextProvider/index.ts
|
|
699
|
+
const INJECTION_KEY = "useTres";
|
|
700
|
+
const [useBaseTresContextProvider, _useTresContext] = createInjectionState(({ scene, canvas, fpsLimit, windowSize, rendererOptions }, createDefaultRenderer) => {
|
|
701
|
+
const localScene = shallowRef(scene);
|
|
702
|
+
const sizes = useSizes(windowSize, canvas);
|
|
703
|
+
const camera = useCameraManager({ sizes });
|
|
704
|
+
const renderer = useBaseRendererManager({
|
|
705
|
+
scene: localScene,
|
|
706
|
+
canvas,
|
|
707
|
+
options: rendererOptions,
|
|
708
|
+
fpsLimit,
|
|
709
|
+
contextParts: {
|
|
710
|
+
sizes,
|
|
711
|
+
camera
|
|
712
|
+
}
|
|
713
|
+
}, createDefaultRenderer);
|
|
714
|
+
const events = useEventManager({
|
|
715
|
+
canvas,
|
|
716
|
+
contextParts: {
|
|
717
|
+
scene: localScene,
|
|
718
|
+
camera,
|
|
719
|
+
renderer
|
|
720
|
+
}
|
|
721
|
+
});
|
|
722
|
+
const ctx = {
|
|
723
|
+
sizes,
|
|
724
|
+
scene: localScene,
|
|
725
|
+
camera,
|
|
726
|
+
renderer,
|
|
727
|
+
isWebGPU: computed(() => isWebGPURenderer(renderer.instance)),
|
|
728
|
+
controls: ref(null),
|
|
729
|
+
extend,
|
|
730
|
+
events
|
|
731
|
+
};
|
|
732
|
+
ctx.scene.value.__tres = {
|
|
733
|
+
root: ctx,
|
|
734
|
+
objects: [],
|
|
735
|
+
isUnmounting: false
|
|
736
|
+
};
|
|
737
|
+
return ctx;
|
|
738
|
+
}, { injectionKey: "useTres" });
|
|
739
|
+
const useTresContext = () => {
|
|
740
|
+
const ctx = _useTresContext();
|
|
741
|
+
if (!ctx) throw new Error("useTresContext must be used together with useTresContextProvider.\n You probably tried to use it above or on the same level as a TresCanvas component.\n It should be used in child components of a TresCanvas instance.");
|
|
742
|
+
return ctx;
|
|
743
|
+
};
|
|
744
|
+
//#endregion
|
|
745
|
+
//#region src/webgl/renderer.ts
|
|
746
|
+
const extendWithThree = () => extend(THREE);
|
|
747
|
+
const createWebGLRenderer = (ctx, options) => new THREE.WebGLRenderer({
|
|
748
|
+
...options,
|
|
749
|
+
canvas: unrefElement(ctx.canvas)
|
|
750
|
+
});
|
|
751
|
+
const useRendererManager = (options) => useBaseRendererManager(options, createWebGLRenderer);
|
|
752
|
+
const useTresContextProvider = (options) => useBaseTresContextProvider(options, createWebGLRenderer);
|
|
753
|
+
//#endregion
|
|
754
|
+
//#region package.json
|
|
755
|
+
var version = "5.10.0";
|
|
756
|
+
//#endregion
|
|
757
|
+
//#region src/utils/array.ts
|
|
758
|
+
/**
|
|
759
|
+
* Like Array.filter, but modifies the array in place.
|
|
760
|
+
* @param array - Array to modify
|
|
761
|
+
* @param callbackFn - A function called for each element of the array. It should return a truthy value to keep the element in the array.
|
|
762
|
+
*/
|
|
763
|
+
const filterInPlace = (array, callbackFn) => {
|
|
764
|
+
let i = 0;
|
|
765
|
+
for (let ii = 0; ii < array.length; ii++) if (callbackFn(array[ii], ii)) {
|
|
766
|
+
array[i] = array[ii];
|
|
767
|
+
i++;
|
|
768
|
+
}
|
|
769
|
+
array.length = i;
|
|
770
|
+
return array;
|
|
771
|
+
};
|
|
772
|
+
//#endregion
|
|
773
|
+
//#region src/utils/index.ts
|
|
774
|
+
function disposeMaterial(material) {
|
|
775
|
+
const hasMap = (material) => "map" in material && !!material.map;
|
|
776
|
+
if (hasMap(material)) material.map.dispose();
|
|
777
|
+
material.dispose();
|
|
778
|
+
}
|
|
779
|
+
function disposeObject3D(object) {
|
|
780
|
+
if (object.parent) object.removeFromParent?.();
|
|
781
|
+
delete object.__tres;
|
|
782
|
+
[...object.children].forEach((child) => disposeObject3D(child));
|
|
783
|
+
if (object instanceof Scene) {} else {
|
|
784
|
+
const mesh = object;
|
|
785
|
+
if (object) object.dispose?.();
|
|
786
|
+
if (mesh.geometry) mesh.geometry.dispose();
|
|
787
|
+
if (Array.isArray(mesh.material)) mesh.material.forEach((material) => disposeMaterial(material));
|
|
788
|
+
else if (mesh.material) disposeMaterial(mesh.material);
|
|
789
|
+
}
|
|
790
|
+
}
|
|
791
|
+
function resolve(obj, key) {
|
|
792
|
+
let target = obj;
|
|
793
|
+
if (key.includes("-")) {
|
|
794
|
+
const entries = key.split("-");
|
|
795
|
+
let currKey = entries.shift();
|
|
796
|
+
while (target && entries.length) if (!(currKey in target)) currKey = joinAsCamelCase(currKey, entries.shift());
|
|
797
|
+
else {
|
|
798
|
+
target = target[currKey];
|
|
799
|
+
currKey = entries.shift();
|
|
800
|
+
}
|
|
801
|
+
return {
|
|
802
|
+
target,
|
|
803
|
+
key: joinAsCamelCase(currKey, ...entries)
|
|
804
|
+
};
|
|
805
|
+
} else return {
|
|
806
|
+
target,
|
|
807
|
+
key
|
|
808
|
+
};
|
|
809
|
+
}
|
|
810
|
+
function joinAsCamelCase(...strings) {
|
|
811
|
+
return strings.map((s, i) => i === 0 ? s : s.charAt(0).toUpperCase() + s.slice(1)).join("");
|
|
812
|
+
}
|
|
813
|
+
function attach(parent, child, type) {
|
|
814
|
+
const INDEX_REGEX = /-\d+$/;
|
|
815
|
+
if (isString(type)) {
|
|
816
|
+
if (INDEX_REGEX.test(type)) {
|
|
817
|
+
const { target, key } = resolve(parent, type.replace(INDEX_REGEX, ""));
|
|
818
|
+
if (!Array.isArray(target[key])) {
|
|
819
|
+
const previousAttach = target[key];
|
|
820
|
+
const augmentedArray = [];
|
|
821
|
+
augmentedArray.__tresDetach = () => {
|
|
822
|
+
if (augmentedArray.every((v) => isUndefined(v))) target[key] = previousAttach;
|
|
823
|
+
};
|
|
824
|
+
target[key] = augmentedArray;
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
const { target, key } = resolve(parent, type);
|
|
828
|
+
child.__tres.previousAttach = target[key];
|
|
829
|
+
target[key] = unboxTresPrimitive(child);
|
|
830
|
+
} else child.__tres.previousAttach = type(parent, child);
|
|
831
|
+
}
|
|
832
|
+
function detach(parent, child, type) {
|
|
833
|
+
if (isString(type)) {
|
|
834
|
+
const { target, key } = resolve(parent, type);
|
|
835
|
+
const previous = child.__tres.previousAttach;
|
|
836
|
+
if (previous === void 0) delete target[key];
|
|
837
|
+
else target[key] = previous;
|
|
838
|
+
if ("__tresDetach" in target) target.__tresDetach();
|
|
839
|
+
} else child.__tres?.previousAttach?.(parent, child);
|
|
840
|
+
delete child.__tres?.previousAttach;
|
|
841
|
+
}
|
|
842
|
+
function prepareTresInstance(obj, state, context) {
|
|
843
|
+
const instance = obj;
|
|
844
|
+
instance.__tres = {
|
|
845
|
+
type: "unknown",
|
|
846
|
+
root: context,
|
|
847
|
+
memoizedProps: {},
|
|
848
|
+
objects: [],
|
|
849
|
+
parent: null,
|
|
850
|
+
previousAttach: null,
|
|
851
|
+
...state
|
|
852
|
+
};
|
|
853
|
+
if (!instance.__tres.attach) {
|
|
854
|
+
if (isMaterial(instance)) instance.__tres.attach = "material";
|
|
855
|
+
else if (isBufferGeometry(instance)) instance.__tres.attach = "geometry";
|
|
856
|
+
else if (isFog(instance)) instance.__tres.attach = "fog";
|
|
857
|
+
}
|
|
858
|
+
return instance;
|
|
859
|
+
}
|
|
860
|
+
function invalidateInstance(instance) {
|
|
861
|
+
const ctx = instance?.__tres?.root;
|
|
862
|
+
if (!ctx?.renderer) return;
|
|
863
|
+
if (ctx.renderer.canBeInvalidated.value) ctx.renderer.invalidate();
|
|
864
|
+
}
|
|
865
|
+
function setPrimitiveObject(newObject, primitive, setTarget, nodeOpsFns, context) {
|
|
866
|
+
const objectsToAttach = [...primitive.__tres.objects];
|
|
867
|
+
const oldObject = unboxTresPrimitive(primitive);
|
|
868
|
+
newObject = unboxTresPrimitive(newObject);
|
|
869
|
+
if (oldObject === newObject) return true;
|
|
870
|
+
const newInstance = prepareTresInstance(newObject, primitive.__tres ?? {}, context);
|
|
871
|
+
const parent = primitive.parent ?? primitive.__tres.parent ?? null;
|
|
872
|
+
const propsToPatch = { ...primitive.__tres.memoizedProps };
|
|
873
|
+
delete propsToPatch.object;
|
|
874
|
+
for (const obj of objectsToAttach) {
|
|
875
|
+
doRemoveDetach(obj, context);
|
|
876
|
+
doRemoveDeregister(obj, context);
|
|
877
|
+
}
|
|
878
|
+
oldObject.__tres.objects = [];
|
|
879
|
+
nodeOpsFns.remove(primitive);
|
|
880
|
+
for (const [key, value] of Object.entries(propsToPatch)) nodeOpsFns.patchProp(newInstance, key, newInstance[key], value);
|
|
881
|
+
setTarget(newObject);
|
|
882
|
+
nodeOpsFns.insert(primitive, parent);
|
|
883
|
+
for (const obj of objectsToAttach) nodeOpsFns.insert(obj, primitive);
|
|
884
|
+
return true;
|
|
885
|
+
}
|
|
886
|
+
function unboxTresPrimitive(maybePrimitive) {
|
|
887
|
+
if (isTresPrimitive(maybePrimitive)) {
|
|
888
|
+
const primitive = maybePrimitive;
|
|
889
|
+
primitive.object.__tres = primitive.__tres;
|
|
890
|
+
return primitive.object;
|
|
891
|
+
} else return maybePrimitive;
|
|
892
|
+
}
|
|
893
|
+
function doRemoveDetach(node, context) {
|
|
894
|
+
const parent = node.__tres?.parent || context.scene.value;
|
|
895
|
+
if (node.__tres) node.__tres.parent = null;
|
|
896
|
+
if (parent && parent.__tres && "objects" in parent.__tres) filterInPlace(parent.__tres.objects, (obj) => obj !== node);
|
|
897
|
+
if (node.__tres?.attach) detach(parent, node, node.__tres.attach);
|
|
898
|
+
else {
|
|
899
|
+
node.parent?.remove?.(unboxTresPrimitive(node));
|
|
900
|
+
node.parent = null;
|
|
901
|
+
}
|
|
902
|
+
}
|
|
903
|
+
function doRemoveDeregister(node, context) {
|
|
904
|
+
node.traverse?.((child) => {
|
|
905
|
+
if (isTresCamera(child)) context.camera.deregisterCamera(child);
|
|
906
|
+
});
|
|
907
|
+
if (isTresCamera(node)) context.camera.deregisterCamera(node);
|
|
908
|
+
invalidateInstance(node);
|
|
909
|
+
}
|
|
910
|
+
//#endregion
|
|
911
|
+
//#region src/utils/primitive/createRetargetingProxy.ts
|
|
912
|
+
function createRetargetingProxy(target, getters = {}, setters = {}) {
|
|
913
|
+
let _target = target;
|
|
914
|
+
const setTarget = (newTarget) => {
|
|
915
|
+
_target = newTarget;
|
|
916
|
+
};
|
|
917
|
+
let proxy = new Proxy({}, {});
|
|
918
|
+
proxy = new Proxy({}, {
|
|
919
|
+
has(_, key) {
|
|
920
|
+
return key in getters || key in _target;
|
|
921
|
+
},
|
|
922
|
+
get(_, prop, __) {
|
|
923
|
+
if (prop in getters) return getters[prop](_target);
|
|
924
|
+
return _target[prop];
|
|
925
|
+
},
|
|
926
|
+
set(_, prop, val) {
|
|
927
|
+
if (setters[prop]) setters[prop](val, _target, proxy, setTarget);
|
|
928
|
+
else _target[prop] = val;
|
|
929
|
+
return true;
|
|
930
|
+
}
|
|
931
|
+
});
|
|
932
|
+
return proxy;
|
|
933
|
+
}
|
|
934
|
+
//#endregion
|
|
935
|
+
//#region src/utils/pointerEvents.ts
|
|
936
|
+
const supportedPointerEvents = [
|
|
937
|
+
"onClick",
|
|
938
|
+
"onContextmenu",
|
|
939
|
+
"onPointermove",
|
|
940
|
+
"onPointerenter",
|
|
941
|
+
"onPointerleave",
|
|
942
|
+
"onPointerover",
|
|
943
|
+
"onPointerout",
|
|
944
|
+
"onDblclick",
|
|
945
|
+
"onPointerdown",
|
|
946
|
+
"onPointerup",
|
|
947
|
+
"onPointercancel",
|
|
948
|
+
"onLostpointercapture",
|
|
949
|
+
"onWheel"
|
|
950
|
+
];
|
|
951
|
+
const pointerEventsMapVueToThree = {
|
|
952
|
+
onClick: "click",
|
|
953
|
+
onContextmenu: "contextmenu",
|
|
954
|
+
onPointermove: "pointermove",
|
|
955
|
+
onPointerenter: "pointerenter",
|
|
956
|
+
onPointerleave: "pointerleave",
|
|
957
|
+
onPointerover: "pointerover",
|
|
958
|
+
onPointerout: "pointerout",
|
|
959
|
+
onDblclick: "dblclick",
|
|
960
|
+
onPointerdown: "pointerdown",
|
|
961
|
+
onPointerup: "pointerup",
|
|
962
|
+
onPointercancel: "pointercancel",
|
|
963
|
+
onLostpointercapture: "lostpointercapture",
|
|
964
|
+
onWheel: "wheel"
|
|
965
|
+
};
|
|
966
|
+
const isSupportedPointerEvent = (event) => supportedPointerEvents.includes(event);
|
|
967
|
+
//#endregion
|
|
968
|
+
//#region src/core/nodeOps.ts
|
|
969
|
+
const textNodeSymbol = Symbol("tresTextNode");
|
|
970
|
+
const commentNodeSymbol = Symbol("tresComment");
|
|
971
|
+
const nodeOps = ({ context, options = { primitivePrefix: "" } }) => {
|
|
972
|
+
const scene = context.scene.value;
|
|
973
|
+
const createTextNode = () => {
|
|
974
|
+
if (scene?.__tres?.isUnmounting) return;
|
|
975
|
+
return {
|
|
976
|
+
[textNodeSymbol]: true,
|
|
977
|
+
__tres: { parent: null }
|
|
978
|
+
};
|
|
979
|
+
};
|
|
980
|
+
const createCommentNode = () => ({
|
|
981
|
+
[commentNodeSymbol]: true,
|
|
982
|
+
__tres: { parent: null }
|
|
983
|
+
});
|
|
984
|
+
const isTextNode = (node) => !!node && typeof node === "object" && textNodeSymbol in node;
|
|
985
|
+
const isCommentNode = (node) => !!node && typeof node === "object" && commentNodeSymbol in node;
|
|
986
|
+
function createElement(tag, _namespace, _isCustomizedBuiltIn, props) {
|
|
987
|
+
if (!props) props = {};
|
|
988
|
+
if (!props.args) props.args = [];
|
|
989
|
+
if (isHTMLTag(tag)) return null;
|
|
990
|
+
if (tag.includes("-")) tag = tag.replace(/-([a-z])/g, (_, c) => c.toUpperCase()).replace(/^[a-z]/, (c) => c.toUpperCase());
|
|
991
|
+
let name = tag.replace("Tres", "");
|
|
992
|
+
let obj;
|
|
993
|
+
if (tag === `${options?.primitivePrefix ?? ""}primitive`) {
|
|
994
|
+
if (!isObject(props.object) || isRef(props.object)) logError("Tres primitives need an 'object' prop, whose value is an object or shallowRef<object>");
|
|
995
|
+
name = props.object.type;
|
|
996
|
+
const __tres = {};
|
|
997
|
+
obj = createRetargetingProxy(props.object, {
|
|
998
|
+
object: (t) => t,
|
|
999
|
+
isPrimitive: () => true,
|
|
1000
|
+
__tres: () => __tres
|
|
1001
|
+
}, {
|
|
1002
|
+
object: (object, _, primitive, setTarget) => {
|
|
1003
|
+
setPrimitiveObject(object, primitive, setTarget, {
|
|
1004
|
+
patchProp,
|
|
1005
|
+
remove,
|
|
1006
|
+
insert
|
|
1007
|
+
}, context);
|
|
1008
|
+
},
|
|
1009
|
+
__tres: (t) => {
|
|
1010
|
+
Object.assign(__tres, t);
|
|
1011
|
+
}
|
|
1012
|
+
});
|
|
1013
|
+
} else {
|
|
1014
|
+
const target = catalogue.value[name];
|
|
1015
|
+
if (!target) logError(`${name} is not defined on the THREE namespace. Use extend to add it to the catalog.`);
|
|
1016
|
+
obj = new target(...props.args);
|
|
1017
|
+
}
|
|
1018
|
+
if (!obj) return null;
|
|
1019
|
+
if (isTresCamera(obj)) {
|
|
1020
|
+
if (!props?.position) obj.position.set(3, 3, 3);
|
|
1021
|
+
if (!props?.lookAt) obj.lookAt(0, 0, 0);
|
|
1022
|
+
}
|
|
1023
|
+
obj = prepareTresInstance(obj, {
|
|
1024
|
+
...isTresInstance(obj) ? obj.__tres : {},
|
|
1025
|
+
type: name,
|
|
1026
|
+
memoizedProps: props,
|
|
1027
|
+
primitive: tag === "primitive",
|
|
1028
|
+
attach: props.attach
|
|
1029
|
+
}, context);
|
|
1030
|
+
return obj;
|
|
1031
|
+
}
|
|
1032
|
+
function insert(child, parent, anchor) {
|
|
1033
|
+
if (!child) return;
|
|
1034
|
+
parent = parent || scene;
|
|
1035
|
+
if (isTextNode(child) || isCommentNode(child)) {
|
|
1036
|
+
const parentInstance = parent.__tres ? parent : prepareTresInstance(parent, {}, context);
|
|
1037
|
+
child.__tres.parent = parentInstance;
|
|
1038
|
+
if (parentInstance.__tres.objects) {
|
|
1039
|
+
const insertIndex = anchor ? parentInstance.__tres.objects.findIndex((obj) => obj === anchor) : -1;
|
|
1040
|
+
if (insertIndex >= 0) parentInstance.__tres.objects.splice(insertIndex, 0, child);
|
|
1041
|
+
else parentInstance.__tres.objects.push(child);
|
|
1042
|
+
}
|
|
1043
|
+
return;
|
|
1044
|
+
}
|
|
1045
|
+
const childInstance = child.__tres ? child : prepareTresInstance(child, {}, context);
|
|
1046
|
+
const parentInstance = parent.__tres ? parent : prepareTresInstance(parent, {}, context);
|
|
1047
|
+
child = unboxTresPrimitive(childInstance);
|
|
1048
|
+
parent = unboxTresPrimitive(parentInstance);
|
|
1049
|
+
if (isTresCamera(child)) context.camera?.registerCamera(child);
|
|
1050
|
+
if (childInstance.__tres.attach) attach(parentInstance, childInstance, childInstance.__tres.attach);
|
|
1051
|
+
else if (isObject3D(child) && isObject3D(parentInstance)) {
|
|
1052
|
+
parentInstance.add(child);
|
|
1053
|
+
child.dispatchEvent({ type: "added" });
|
|
1054
|
+
}
|
|
1055
|
+
childInstance.__tres.parent = parentInstance;
|
|
1056
|
+
if (parentInstance.__tres.objects && !parentInstance.__tres.objects.includes(childInstance)) {
|
|
1057
|
+
const insertIndex = anchor ? parentInstance.__tres.objects.findIndex((obj) => obj === anchor) : -1;
|
|
1058
|
+
if (~insertIndex) parentInstance.__tres.objects.splice(insertIndex, 0, childInstance);
|
|
1059
|
+
else parentInstance.__tres.objects.push(childInstance);
|
|
1060
|
+
}
|
|
1061
|
+
}
|
|
1062
|
+
/**
|
|
1063
|
+
* @param node – the node root to remove
|
|
1064
|
+
* @param dispose – the disposal type
|
|
1065
|
+
*/
|
|
1066
|
+
function remove(node, dispose) {
|
|
1067
|
+
if (!node) return;
|
|
1068
|
+
if (isTextNode(node) || isCommentNode(node)) {
|
|
1069
|
+
const parent = node.__tres.parent;
|
|
1070
|
+
if (parent?.__tres?.objects) filterInPlace(parent.__tres.objects, (obj) => obj !== node);
|
|
1071
|
+
node.__tres.parent = null;
|
|
1072
|
+
return;
|
|
1073
|
+
}
|
|
1074
|
+
dispose = isUndefined(dispose) ? "default" : dispose;
|
|
1075
|
+
const userDispose = node.__tres?.dispose;
|
|
1076
|
+
if (!isUndefined(userDispose)) {
|
|
1077
|
+
if (userDispose === null) dispose = false;
|
|
1078
|
+
else dispose = userDispose;
|
|
1079
|
+
}
|
|
1080
|
+
const isPrimitive = node.__tres?.primitive;
|
|
1081
|
+
const shouldDispose = dispose === "default" ? !isPrimitive : !!dispose;
|
|
1082
|
+
if (node.__tres && "objects" in node.__tres) [...node.__tres.objects].forEach((obj) => remove(obj, dispose));
|
|
1083
|
+
if (shouldDispose) {
|
|
1084
|
+
if (node.children) [...node.children].forEach((child) => remove(child, dispose));
|
|
1085
|
+
}
|
|
1086
|
+
doRemoveDetach(node, context);
|
|
1087
|
+
doRemoveDeregister(node, context);
|
|
1088
|
+
if (shouldDispose && !isScene(node)) {
|
|
1089
|
+
if (isFunction(dispose)) dispose(node);
|
|
1090
|
+
else if (isFunction(node.dispose)) try {
|
|
1091
|
+
node.dispose();
|
|
1092
|
+
} catch (e) {}
|
|
1093
|
+
}
|
|
1094
|
+
if ("__tres" in node) delete node.__tres;
|
|
1095
|
+
}
|
|
1096
|
+
function markNodeMaterialForRecompile(target, prevValue, nextValue) {
|
|
1097
|
+
if (isNodeMaterial(target) && (isNode(prevValue) || isNode(nextValue))) target.needsUpdate = true;
|
|
1098
|
+
}
|
|
1099
|
+
function patchProp(node, prop, prevValue, nextValue) {
|
|
1100
|
+
if (!node) return;
|
|
1101
|
+
let root = node;
|
|
1102
|
+
const key = prop;
|
|
1103
|
+
if (node.__tres) node.__tres.memoizedProps[prop] = nextValue;
|
|
1104
|
+
if (prop === "attach") {
|
|
1105
|
+
const maybeParent = node.__tres?.parent || node.parent;
|
|
1106
|
+
remove(node);
|
|
1107
|
+
prepareTresInstance(node, { attach: nextValue }, context);
|
|
1108
|
+
if (maybeParent) insert(node, maybeParent);
|
|
1109
|
+
return;
|
|
1110
|
+
}
|
|
1111
|
+
if (prop === "dispose") {
|
|
1112
|
+
if (!node.__tres) node = prepareTresInstance(node, {}, context);
|
|
1113
|
+
node.__tres.dispose = nextValue;
|
|
1114
|
+
return;
|
|
1115
|
+
}
|
|
1116
|
+
if (isSupportedPointerEvent(prop) && isFunction(nextValue)) node.addEventListener(pointerEventsMapVueToThree[prop], nextValue);
|
|
1117
|
+
let finalKey = camel(key);
|
|
1118
|
+
let target = root?.[finalKey];
|
|
1119
|
+
if (key === "args") {
|
|
1120
|
+
const prevNode = node;
|
|
1121
|
+
const prevArgs = prevValue ?? [];
|
|
1122
|
+
const args = nextValue ?? [];
|
|
1123
|
+
const instanceName = node.__tres?.type || node.type;
|
|
1124
|
+
if (instanceName && prevArgs.length && !isEqual(prevArgs, args)) {
|
|
1125
|
+
const newInstance = new catalogue.value[instanceName](...nextValue);
|
|
1126
|
+
const descriptors = Object.getOwnPropertyDescriptors(newInstance);
|
|
1127
|
+
Object.entries(descriptors).forEach(([key, descriptor]) => {
|
|
1128
|
+
if (!descriptor.writable && !descriptor.set) return;
|
|
1129
|
+
if (key in prevNode) try {
|
|
1130
|
+
prevNode[key] = newInstance[key];
|
|
1131
|
+
} catch (e) {
|
|
1132
|
+
console.warn(`Could not set property ${key} on ${instanceName}:`, e);
|
|
1133
|
+
}
|
|
1134
|
+
});
|
|
1135
|
+
root = prevNode;
|
|
1136
|
+
}
|
|
1137
|
+
return;
|
|
1138
|
+
}
|
|
1139
|
+
if (root.type === "BufferGeometry") {
|
|
1140
|
+
if (key === "args") return;
|
|
1141
|
+
root.setAttribute(camel(key), new BufferAttribute(...nextValue));
|
|
1142
|
+
return;
|
|
1143
|
+
}
|
|
1144
|
+
if (key.includes("-") && target === void 0) {
|
|
1145
|
+
const resolved = resolve(root, key);
|
|
1146
|
+
target = resolved.target;
|
|
1147
|
+
root = resolved.target;
|
|
1148
|
+
finalKey = resolved.key;
|
|
1149
|
+
if (target && finalKey) {
|
|
1150
|
+
target[finalKey] = nextValue;
|
|
1151
|
+
markNodeMaterialForRecompile(target, prevValue, nextValue);
|
|
1152
|
+
if (isTresCamera(node)) node.updateProjectionMatrix();
|
|
1153
|
+
invalidateInstance(node);
|
|
1154
|
+
return;
|
|
1155
|
+
}
|
|
1156
|
+
}
|
|
1157
|
+
let value = nextValue;
|
|
1158
|
+
if (value === "") value = true;
|
|
1159
|
+
if (isFunction(target)) {
|
|
1160
|
+
if (!isSupportedPointerEvent(prop)) {
|
|
1161
|
+
if (Array.isArray(value)) node[finalKey](...value);
|
|
1162
|
+
else node[finalKey](value);
|
|
1163
|
+
}
|
|
1164
|
+
if (finalKey.startsWith("on") && isFunction(value)) root[finalKey] = value;
|
|
1165
|
+
return;
|
|
1166
|
+
}
|
|
1167
|
+
if (isLayers(target) && isLayers(value)) target.mask = value.mask;
|
|
1168
|
+
else if (isColor(target) && isColorRepresentation(value)) target.set(value);
|
|
1169
|
+
else if (isCopyable(target) && isClassInstance(value) && target.constructor === value.constructor) target.copy(value);
|
|
1170
|
+
else if (isVectorLike(target) && Array.isArray(value)) {
|
|
1171
|
+
if ("fromArray" in target && typeof target.fromArray === "function") target.fromArray(value);
|
|
1172
|
+
else target.set(...value);
|
|
1173
|
+
} else if (isVectorLike(target) && typeof value === "number") {
|
|
1174
|
+
if ("setScalar" in target && typeof target.setScalar === "function") target.setScalar(value);
|
|
1175
|
+
else target.set(value);
|
|
1176
|
+
} else root[finalKey] = value;
|
|
1177
|
+
markNodeMaterialForRecompile(root, prevValue, value);
|
|
1178
|
+
if (isTresCamera(node)) node.updateProjectionMatrix();
|
|
1179
|
+
invalidateInstance(node);
|
|
1180
|
+
}
|
|
1181
|
+
function parentNode(node) {
|
|
1182
|
+
return node?.__tres?.parent || null;
|
|
1183
|
+
}
|
|
1184
|
+
function nextSibling(node) {
|
|
1185
|
+
if (!node || !("__tres" in node)) return null;
|
|
1186
|
+
const siblings = parentNode(node)?.__tres?.objects || [];
|
|
1187
|
+
const index = siblings.findIndex((sibling) => sibling === node);
|
|
1188
|
+
if (index < 0 || index >= siblings.length - 1) return null;
|
|
1189
|
+
return siblings[index + 1];
|
|
1190
|
+
}
|
|
1191
|
+
const noop = () => {};
|
|
1192
|
+
return {
|
|
1193
|
+
insert,
|
|
1194
|
+
remove,
|
|
1195
|
+
createElement,
|
|
1196
|
+
patchProp,
|
|
1197
|
+
parentNode,
|
|
1198
|
+
createText: createTextNode,
|
|
1199
|
+
createComment: createCommentNode,
|
|
1200
|
+
setText: noop,
|
|
1201
|
+
setElementText: noop,
|
|
1202
|
+
nextSibling,
|
|
1203
|
+
querySelector: noop,
|
|
1204
|
+
setScopeId: noop,
|
|
1205
|
+
cloneNode: noop,
|
|
1206
|
+
insertStaticContent: noop
|
|
1207
|
+
};
|
|
1208
|
+
};
|
|
1209
|
+
//#endregion
|
|
1210
|
+
//#region src/core/roots.ts
|
|
1211
|
+
const _roots = /* @__PURE__ */ new Map();
|
|
1212
|
+
const getRoot = (canvas) => _roots.get(canvas);
|
|
1213
|
+
const setRoot = (canvas, root) => {
|
|
1214
|
+
_roots.set(canvas, root);
|
|
1215
|
+
};
|
|
1216
|
+
const deleteRoot = (canvas) => _roots.delete(canvas);
|
|
1217
|
+
//#endregion
|
|
1218
|
+
//#region src/devtools/DevtoolsMessenger.ts
|
|
1219
|
+
const QUEUEABLE_MESSAGE_TYPES = ["asset-load"];
|
|
1220
|
+
/**
|
|
1221
|
+
* Messenger class for communicating with Tres DevTools
|
|
1222
|
+
* This class will be attached to window.__TRES__DEVTOOLS__
|
|
1223
|
+
*/
|
|
1224
|
+
var DevtoolsMessenger = class {
|
|
1225
|
+
subscribers = /* @__PURE__ */ new Set();
|
|
1226
|
+
messageQueue = [];
|
|
1227
|
+
maxQueueSize = 100;
|
|
1228
|
+
/**
|
|
1229
|
+
* Send a message to devtools subscribers
|
|
1230
|
+
* If no subscribers are available, only queueable message types are queued
|
|
1231
|
+
*/
|
|
1232
|
+
send(type, data) {
|
|
1233
|
+
const message = {
|
|
1234
|
+
type,
|
|
1235
|
+
data,
|
|
1236
|
+
timestamp: Date.now()
|
|
1237
|
+
};
|
|
1238
|
+
if (this.subscribers.size > 0) this.subscribers.forEach((subscriber) => subscriber(message));
|
|
1239
|
+
else if (QUEUEABLE_MESSAGE_TYPES.includes(type)) this.queueMessage(message);
|
|
1240
|
+
}
|
|
1241
|
+
/**
|
|
1242
|
+
* Queue a message for later delivery
|
|
1243
|
+
*/
|
|
1244
|
+
queueMessage(message) {
|
|
1245
|
+
this.messageQueue.push(message);
|
|
1246
|
+
if (this.messageQueue.length > this.maxQueueSize) this.messageQueue.shift();
|
|
1247
|
+
}
|
|
1248
|
+
/**
|
|
1249
|
+
* Flush all queued messages to current subscribers
|
|
1250
|
+
*/
|
|
1251
|
+
flushQueue() {
|
|
1252
|
+
if (this.messageQueue.length === 0 || this.subscribers.size === 0) return;
|
|
1253
|
+
this.messageQueue.forEach((message) => {
|
|
1254
|
+
this.subscribers.forEach((subscriber) => subscriber(message));
|
|
1255
|
+
});
|
|
1256
|
+
this.messageQueue = [];
|
|
1257
|
+
}
|
|
1258
|
+
/**
|
|
1259
|
+
* Subscribe to devtools messages
|
|
1260
|
+
* When a new subscriber is added, all queued messages (asset-load events) are immediately delivered
|
|
1261
|
+
*/
|
|
1262
|
+
subscribe(subscriber) {
|
|
1263
|
+
this.subscribers.add(subscriber);
|
|
1264
|
+
this.flushQueue();
|
|
1265
|
+
return () => {
|
|
1266
|
+
this.subscribers.delete(subscriber);
|
|
1267
|
+
};
|
|
1268
|
+
}
|
|
1269
|
+
/**
|
|
1270
|
+
* Check if there are any subscribers
|
|
1271
|
+
*/
|
|
1272
|
+
get hasSubscribers() {
|
|
1273
|
+
return this.subscribers.size > 0;
|
|
1274
|
+
}
|
|
1275
|
+
/**
|
|
1276
|
+
* Get the current queue size
|
|
1277
|
+
*/
|
|
1278
|
+
get queueSize() {
|
|
1279
|
+
return this.messageQueue.length;
|
|
1280
|
+
}
|
|
1281
|
+
/**
|
|
1282
|
+
* Clear all queued messages
|
|
1283
|
+
*/
|
|
1284
|
+
clearQueue() {
|
|
1285
|
+
this.messageQueue = [];
|
|
1286
|
+
}
|
|
1287
|
+
};
|
|
1288
|
+
//#endregion
|
|
1289
|
+
//#region src/devtools/utils.ts
|
|
1290
|
+
/**
|
|
1291
|
+
* Shows a toast or console.log
|
|
1292
|
+
*
|
|
1293
|
+
* @param message - message to log
|
|
1294
|
+
* @param type - different color of the tooltip
|
|
1295
|
+
*/
|
|
1296
|
+
function toastMessage(message, type) {
|
|
1297
|
+
const tresMessage = `▲ ■ ●${message}`;
|
|
1298
|
+
if (typeof __VUE_DEVTOOLS_TOAST__ === "function") __VUE_DEVTOOLS_TOAST__(tresMessage, type);
|
|
1299
|
+
else if (type === "error") console.error(tresMessage);
|
|
1300
|
+
else if (type === "warn") console.warn(tresMessage);
|
|
1301
|
+
else console.log(tresMessage);
|
|
1302
|
+
}
|
|
1303
|
+
function __VUE_DEVTOOLS_TOAST__(tresMessage, type) {
|
|
1304
|
+
throw new Error(tresMessage + type);
|
|
1305
|
+
}
|
|
1306
|
+
//#endregion
|
|
1307
|
+
//#region src/utils/perf.ts
|
|
1308
|
+
function calculateMemoryUsage(object) {
|
|
1309
|
+
let totalMemory = 0;
|
|
1310
|
+
object.traverse((node) => {
|
|
1311
|
+
if (isMesh(node) && node.type !== "HightlightMesh") {
|
|
1312
|
+
const geometry = node.geometry;
|
|
1313
|
+
const verticesMemory = geometry.attributes.position.count * 3 * Float32Array.BYTES_PER_ELEMENT;
|
|
1314
|
+
const facesMemory = geometry.index ? geometry.index.count * Uint32Array.BYTES_PER_ELEMENT : 0;
|
|
1315
|
+
const normalsMemory = geometry.attributes.normal ? geometry.attributes.normal.count * 3 * Float32Array.BYTES_PER_ELEMENT : 0;
|
|
1316
|
+
const uvsMemory = geometry.attributes.uv ? geometry.attributes.uv.count * 2 * Float32Array.BYTES_PER_ELEMENT : 0;
|
|
1317
|
+
const geometryMemory = verticesMemory + facesMemory + normalsMemory + uvsMemory;
|
|
1318
|
+
totalMemory += geometryMemory;
|
|
1319
|
+
}
|
|
1320
|
+
});
|
|
1321
|
+
return totalMemory;
|
|
1322
|
+
}
|
|
1323
|
+
function boundedPush(arr, value, max) {
|
|
1324
|
+
arr.push(value);
|
|
1325
|
+
if (arr.length > max) arr.shift();
|
|
1326
|
+
}
|
|
1327
|
+
function bytesToKB(bytes) {
|
|
1328
|
+
return (bytes / 1024).toFixed(2);
|
|
1329
|
+
}
|
|
1330
|
+
//#endregion
|
|
1331
|
+
//#region src/devtools/setupDevtools.ts
|
|
1332
|
+
function setupTresDevtools(ctx) {
|
|
1333
|
+
if (!ctx) return;
|
|
1334
|
+
if (typeof window !== "undefined" && !window.__TRES__DEVTOOLS__) window.__TRES__DEVTOOLS__ = new DevtoolsMessenger();
|
|
1335
|
+
const performanceState = {
|
|
1336
|
+
maxFrames: 160,
|
|
1337
|
+
fps: {
|
|
1338
|
+
value: 0,
|
|
1339
|
+
accumulator: []
|
|
1340
|
+
},
|
|
1341
|
+
memory: {
|
|
1342
|
+
currentMem: 0,
|
|
1343
|
+
allocatedMem: 0,
|
|
1344
|
+
accumulator: []
|
|
1345
|
+
}
|
|
1346
|
+
};
|
|
1347
|
+
const updateInterval = 100;
|
|
1348
|
+
const fps = useFps({ every: updateInterval });
|
|
1349
|
+
const { isSupported, memory } = useMemory({ scheduler: (cb) => useIntervalFn(cb, updateInterval) });
|
|
1350
|
+
const maxFrames = 160;
|
|
1351
|
+
let lastUpdateTime = performance.now();
|
|
1352
|
+
let accumulatedTime = 0;
|
|
1353
|
+
const interval = 1;
|
|
1354
|
+
const updatePerformanceData = ({ timestamp }) => {
|
|
1355
|
+
if (ctx.scene.value) performanceState.memory.allocatedMem = calculateMemoryUsage(ctx.scene.value);
|
|
1356
|
+
if (timestamp - lastUpdateTime >= updateInterval) {
|
|
1357
|
+
lastUpdateTime = timestamp;
|
|
1358
|
+
boundedPush(performanceState.fps.accumulator, fps.value, maxFrames);
|
|
1359
|
+
performanceState.fps.value = fps.value;
|
|
1360
|
+
if (isSupported.value && memory.value?.usedJSHeapSize) {
|
|
1361
|
+
boundedPush(performanceState.memory.accumulator, memory.value.usedJSHeapSize / 1024 / 1024, maxFrames);
|
|
1362
|
+
if (performanceState.memory.accumulator.length > 0) performanceState.memory.currentMem = performanceState.memory.accumulator.reduce((a, b) => a + b, 0) / performanceState.memory.accumulator.length;
|
|
1363
|
+
}
|
|
1364
|
+
}
|
|
1365
|
+
};
|
|
1366
|
+
const { pause } = useRafFn(({ delta }) => {
|
|
1367
|
+
if (!window.__TRES__DEVTOOLS__) return;
|
|
1368
|
+
updatePerformanceData({ timestamp: performance.now() });
|
|
1369
|
+
accumulatedTime += delta;
|
|
1370
|
+
if (accumulatedTime >= interval) {
|
|
1371
|
+
window.__TRES__DEVTOOLS__.send("context", ctx);
|
|
1372
|
+
window.__TRES__DEVTOOLS__.send("performance", performanceState);
|
|
1373
|
+
accumulatedTime = 0;
|
|
1374
|
+
}
|
|
1375
|
+
}, { immediate: true });
|
|
1376
|
+
onUnmounted(() => {
|
|
1377
|
+
pause();
|
|
1378
|
+
});
|
|
1379
|
+
}
|
|
1380
|
+
//#endregion
|
|
1381
|
+
//#region src/utils/three.ts
|
|
1382
|
+
/**
|
|
1383
|
+
* Recursively searches for an Object3D with the specified UUID within a Three.js scene graph.
|
|
1384
|
+
*
|
|
1385
|
+
* @param node - The root Object3D to start searching from
|
|
1386
|
+
* @param uuid - The unique identifier of the object to find
|
|
1387
|
+
* @returns The Object3D with the matching UUID, or undefined if not found
|
|
1388
|
+
*/
|
|
1389
|
+
const getObjectByUuid = (node, uuid) => {
|
|
1390
|
+
if (node.uuid === uuid) return node;
|
|
1391
|
+
for (const child of node.children) {
|
|
1392
|
+
const found = getObjectByUuid(child, uuid);
|
|
1393
|
+
if (found) return found;
|
|
1394
|
+
}
|
|
1395
|
+
};
|
|
1396
|
+
//#endregion
|
|
1397
|
+
//#region src/devtools/highlight.ts
|
|
1398
|
+
var HightlightMesh = class extends THREE.Mesh {
|
|
1399
|
+
type = "HightlightMesh";
|
|
1400
|
+
createTime;
|
|
1401
|
+
constructor(...args) {
|
|
1402
|
+
super(...args);
|
|
1403
|
+
this.createTime = Date.now();
|
|
1404
|
+
}
|
|
1405
|
+
onBeforeRender() {
|
|
1406
|
+
const time = (Date.now() - this.createTime) / 1e3;
|
|
1407
|
+
const scaleFactor = 1 + .07 * Math.sin(2.5 * time);
|
|
1408
|
+
this.scale.set(scaleFactor, scaleFactor, scaleFactor);
|
|
1409
|
+
}
|
|
1410
|
+
};
|
|
1411
|
+
//#endregion
|
|
1412
|
+
//#region src/devtools/inspectorHandlers.ts
|
|
1413
|
+
/**
|
|
1414
|
+
* Creates a node representation of a Three.js object for the inspector tree
|
|
1415
|
+
* @param object - The Three.js object to create a node for
|
|
1416
|
+
* @returns A SceneGraphObject representing the Three.js object with relevant metadata
|
|
1417
|
+
*/
|
|
1418
|
+
const createNode = (object) => {
|
|
1419
|
+
const node = {
|
|
1420
|
+
id: `scene-${object.uuid}`,
|
|
1421
|
+
label: object.type,
|
|
1422
|
+
children: [],
|
|
1423
|
+
tags: []
|
|
1424
|
+
};
|
|
1425
|
+
if (object.name !== "") node.tags.push({
|
|
1426
|
+
label: object.name,
|
|
1427
|
+
textColor: 5750629,
|
|
1428
|
+
backgroundColor: 15793395
|
|
1429
|
+
});
|
|
1430
|
+
const memory = calculateMemoryUsage(object);
|
|
1431
|
+
if (memory > 0) node.tags.push({
|
|
1432
|
+
label: `${bytesToKB(memory)} KB`,
|
|
1433
|
+
textColor: 15707189,
|
|
1434
|
+
backgroundColor: 16775644,
|
|
1435
|
+
tooltip: "Memory usage"
|
|
1436
|
+
});
|
|
1437
|
+
if (object.type.includes("Light")) {
|
|
1438
|
+
if (isLight(object)) node.tags.push({
|
|
1439
|
+
label: `${object.intensity}`,
|
|
1440
|
+
textColor: 9738662,
|
|
1441
|
+
backgroundColor: 16316922,
|
|
1442
|
+
tooltip: "Intensity"
|
|
1443
|
+
});
|
|
1444
|
+
node.tags.push({
|
|
1445
|
+
label: `#${new Color(object.color).getHexString()}`,
|
|
1446
|
+
textColor: 9738662,
|
|
1447
|
+
backgroundColor: 16316922,
|
|
1448
|
+
tooltip: "Color"
|
|
1449
|
+
});
|
|
1450
|
+
}
|
|
1451
|
+
if (object.type.includes("Camera")) {
|
|
1452
|
+
node.tags.push({
|
|
1453
|
+
label: `${object.fov}°`,
|
|
1454
|
+
textColor: 9738662,
|
|
1455
|
+
backgroundColor: 16316922,
|
|
1456
|
+
tooltip: "Field of view"
|
|
1457
|
+
});
|
|
1458
|
+
node.tags.push({
|
|
1459
|
+
label: `x: ${Math.round(object.position.x)} y: ${Math.round(object.position.y)} z: ${Math.round(object.position.z)}`,
|
|
1460
|
+
textColor: 9738662,
|
|
1461
|
+
backgroundColor: 16316922,
|
|
1462
|
+
tooltip: "Position"
|
|
1463
|
+
});
|
|
1464
|
+
}
|
|
1465
|
+
return node;
|
|
1466
|
+
};
|
|
1467
|
+
/**
|
|
1468
|
+
* Creates a context node for the inspector tree
|
|
1469
|
+
* @param key - The key identifier for the context node
|
|
1470
|
+
* @param uuid - The unique identifier for the context
|
|
1471
|
+
* @param parentKey - Optional parent key for nested context nodes
|
|
1472
|
+
* @returns A SceneGraphObject representing the context node
|
|
1473
|
+
*/
|
|
1474
|
+
function createContextNode(key, uuid, parentKey = "") {
|
|
1475
|
+
return {
|
|
1476
|
+
id: `context-${uuid}-${parentKey ? `${parentKey}.${key}` : key}`,
|
|
1477
|
+
label: key,
|
|
1478
|
+
children: [],
|
|
1479
|
+
tags: []
|
|
1480
|
+
};
|
|
1481
|
+
}
|
|
1482
|
+
/**
|
|
1483
|
+
* Recursively builds a graph representation of Three.js objects for the inspector
|
|
1484
|
+
* @param object - The root Three.js object to build the graph from
|
|
1485
|
+
* @param node - The current node in the graph being built
|
|
1486
|
+
* @param filter - Optional filter string to filter objects by type or name
|
|
1487
|
+
*/
|
|
1488
|
+
function buildGraph$1(object, node, filter = "") {
|
|
1489
|
+
object.children.forEach((child) => {
|
|
1490
|
+
if (child.type === "HightlightMesh") return;
|
|
1491
|
+
if (filter && !child.type.includes(filter) && !child.name.includes(filter)) return;
|
|
1492
|
+
const childNode = createNode(child);
|
|
1493
|
+
node.children.push(childNode);
|
|
1494
|
+
buildGraph$1(child, childNode, filter);
|
|
1495
|
+
});
|
|
1496
|
+
}
|
|
1497
|
+
/**
|
|
1498
|
+
* Recursively builds a graph representation of context objects for the inspector
|
|
1499
|
+
* @param object - The root object to build the context graph from
|
|
1500
|
+
* @param node - The current node in the graph being built
|
|
1501
|
+
* @param visited - WeakSet to track visited objects and prevent circular references
|
|
1502
|
+
* @param depth - Current depth in the object tree
|
|
1503
|
+
* @param maxDepth - Maximum depth to traverse
|
|
1504
|
+
* @param contextUuid - Optional UUID for the context
|
|
1505
|
+
* @param parentKey - Optional parent key for nested objects
|
|
1506
|
+
*/
|
|
1507
|
+
function buildContextGraph(object, node, visited = /* @__PURE__ */ new WeakSet(), depth = 0, maxDepth = 4, contextUuid, parentKey = "") {
|
|
1508
|
+
if (depth >= maxDepth || !object || visited.has(object)) return;
|
|
1509
|
+
const uuid = depth === 0 ? object?.scene?.value?.uuid || Math.random().toString(36).slice(2, 11) : contextUuid;
|
|
1510
|
+
visited.add(object);
|
|
1511
|
+
Object.entries(object).forEach(([key, value]) => {
|
|
1512
|
+
if (key.startsWith("_") || typeof value === "function") return;
|
|
1513
|
+
const chainedKey = parentKey ? `${parentKey}.${key}` : key;
|
|
1514
|
+
const childNode = createContextNode(key, uuid, parentKey);
|
|
1515
|
+
if (key === "scene") return;
|
|
1516
|
+
if (isRef(value)) {
|
|
1517
|
+
childNode.tags.push({
|
|
1518
|
+
label: `Ref<${typeof value.value}>`,
|
|
1519
|
+
textColor: 4372611,
|
|
1520
|
+
backgroundColor: 15793395
|
|
1521
|
+
});
|
|
1522
|
+
if (value.value && typeof value.value === "object") buildContextGraph(value.value, childNode, visited, depth + 1, maxDepth, uuid, chainedKey);
|
|
1523
|
+
else childNode.label = `${key}: ${JSON.stringify(value.value)}`;
|
|
1524
|
+
} else if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
1525
|
+
if (Object.keys(value).length > 0) {
|
|
1526
|
+
if (visited.has(value)) childNode.tags.push({
|
|
1527
|
+
label: "Circular",
|
|
1528
|
+
textColor: 16711680,
|
|
1529
|
+
backgroundColor: 16773360
|
|
1530
|
+
});
|
|
1531
|
+
else buildContextGraph(value, childNode, visited, depth + 1, maxDepth, uuid, chainedKey);
|
|
1532
|
+
} else childNode.label = `${key}: {}`;
|
|
1533
|
+
} else if (Array.isArray(value)) {
|
|
1534
|
+
childNode.label = `${key}: Array(${value.length})`;
|
|
1535
|
+
childNode.tags.push({
|
|
1536
|
+
label: `length: ${value.length}`,
|
|
1537
|
+
textColor: 9738662,
|
|
1538
|
+
backgroundColor: 16316922
|
|
1539
|
+
});
|
|
1540
|
+
} else childNode.label = `${key}: ${JSON.stringify(value)}`;
|
|
1541
|
+
node.children.push(childNode);
|
|
1542
|
+
});
|
|
1543
|
+
}
|
|
1544
|
+
/**
|
|
1545
|
+
* Handler for inspector tree updates
|
|
1546
|
+
* @param tres - The TresContext instance
|
|
1547
|
+
* @returns A function that handles inspector tree payload updates
|
|
1548
|
+
*/
|
|
1549
|
+
const inspectorTreeHandler = (tres) => (payload) => {
|
|
1550
|
+
if (payload.inspectorId === "tres:inspector") {
|
|
1551
|
+
const root = createNode(tres.scene.value);
|
|
1552
|
+
buildGraph$1(tres.scene.value, root, payload.filter);
|
|
1553
|
+
const rootContext = {
|
|
1554
|
+
id: "context-root",
|
|
1555
|
+
label: "Context",
|
|
1556
|
+
children: [],
|
|
1557
|
+
tags: []
|
|
1558
|
+
};
|
|
1559
|
+
buildContextGraph(tres, rootContext);
|
|
1560
|
+
payload.rootNodes = [root, rootContext];
|
|
1561
|
+
}
|
|
1562
|
+
};
|
|
1563
|
+
/**
|
|
1564
|
+
* Handler for inspector state updates
|
|
1565
|
+
* @param tres - The TresContext instance
|
|
1566
|
+
* @param options - Options for the handler
|
|
1567
|
+
* @param options.highlightMesh - The currently highlighted mesh
|
|
1568
|
+
* @param options.prevInstance - The previously selected instance
|
|
1569
|
+
* @returns A function that handles inspector state payload updates
|
|
1570
|
+
*/
|
|
1571
|
+
const inspectorStateHandler = (tres, { highlightMesh, prevInstance }) => (payload) => {
|
|
1572
|
+
if (payload.inspectorId !== "tres:inspector") return;
|
|
1573
|
+
const highlightMaterial = new MeshBasicMaterial({
|
|
1574
|
+
color: 11003607,
|
|
1575
|
+
transparent: true,
|
|
1576
|
+
opacity: .2,
|
|
1577
|
+
depthTest: false,
|
|
1578
|
+
side: DoubleSide
|
|
1579
|
+
});
|
|
1580
|
+
if (payload.nodeId.includes("scene")) {
|
|
1581
|
+
const match = payload.nodeId.match(/^scene-(.+)$/);
|
|
1582
|
+
const uuid = match ? match[1] : null;
|
|
1583
|
+
if (!uuid) return;
|
|
1584
|
+
const [instance] = tres.scene.value.getObjectsByProperty("uuid", uuid);
|
|
1585
|
+
if (!instance) return;
|
|
1586
|
+
if (prevInstance && highlightMesh && highlightMesh.parent) prevInstance.remove(highlightMesh);
|
|
1587
|
+
if (isMesh(instance)) {
|
|
1588
|
+
const newHighlightMesh = new HightlightMesh(instance.geometry.clone(), highlightMaterial);
|
|
1589
|
+
instance.add(newHighlightMesh);
|
|
1590
|
+
highlightMesh = newHighlightMesh;
|
|
1591
|
+
prevInstance = instance;
|
|
1592
|
+
}
|
|
1593
|
+
payload.state = { object: Object.entries(instance).map(([key, value]) => {
|
|
1594
|
+
if (key === "children") return {
|
|
1595
|
+
key,
|
|
1596
|
+
value: value.filter((child) => child.type !== "HightlightMesh")
|
|
1597
|
+
};
|
|
1598
|
+
return {
|
|
1599
|
+
key,
|
|
1600
|
+
value,
|
|
1601
|
+
editable: true
|
|
1602
|
+
};
|
|
1603
|
+
}).filter(({ key }) => {
|
|
1604
|
+
return key !== "parent";
|
|
1605
|
+
}) };
|
|
1606
|
+
if (isScene(instance)) {
|
|
1607
|
+
const sceneState = {
|
|
1608
|
+
...payload.state,
|
|
1609
|
+
state: [{
|
|
1610
|
+
key: "Scene Info",
|
|
1611
|
+
value: {
|
|
1612
|
+
objects: instance.children.length,
|
|
1613
|
+
memory: calculateMemoryUsage(instance),
|
|
1614
|
+
calls: tres.renderer.instance.info.render.calls,
|
|
1615
|
+
triangles: tres.renderer.instance.info.render.triangles,
|
|
1616
|
+
points: tres.renderer.instance.info.render.points,
|
|
1617
|
+
lines: tres.renderer.instance.info.render.lines
|
|
1618
|
+
}
|
|
1619
|
+
}]
|
|
1620
|
+
};
|
|
1621
|
+
if ("programs" in tres.renderer.instance.info) sceneState.state.push({
|
|
1622
|
+
key: "Programs",
|
|
1623
|
+
value: tres.renderer.instance.info.programs?.map((program) => ({
|
|
1624
|
+
...program,
|
|
1625
|
+
programName: program.name
|
|
1626
|
+
}))
|
|
1627
|
+
});
|
|
1628
|
+
payload.state = sceneState;
|
|
1629
|
+
}
|
|
1630
|
+
} else if (payload.nodeId.includes("context")) {
|
|
1631
|
+
const match = payload.nodeId.match(/^context-([^-]+(?:-[^-]+)*)-(.+)$/);
|
|
1632
|
+
const chainedKey = match ? match[2] : "context";
|
|
1633
|
+
if (!chainedKey || chainedKey === "context") {
|
|
1634
|
+
payload.state = { object: Object.entries(tres).filter(([key]) => !key.startsWith("_") && key !== "parent").map(([key, value]) => ({
|
|
1635
|
+
key,
|
|
1636
|
+
value: isRef(value) ? value.value : value,
|
|
1637
|
+
editable: false
|
|
1638
|
+
})) };
|
|
1639
|
+
return;
|
|
1640
|
+
}
|
|
1641
|
+
const parts = chainedKey.split(".");
|
|
1642
|
+
let value = tres;
|
|
1643
|
+
for (const part of parts) {
|
|
1644
|
+
if (!value || typeof value !== "object") break;
|
|
1645
|
+
value = isRef(value[part]) ? value[part].value : value[part];
|
|
1646
|
+
}
|
|
1647
|
+
if (value !== void 0) payload.state = { object: Object.entries(value).filter(([key]) => !key.startsWith("_") && key !== "parent").map(([key, val]) => {
|
|
1648
|
+
if (isRef(val)) return {
|
|
1649
|
+
key,
|
|
1650
|
+
value: val.value,
|
|
1651
|
+
editable: false
|
|
1652
|
+
};
|
|
1653
|
+
if (typeof val === "function") return {
|
|
1654
|
+
key,
|
|
1655
|
+
value: "ƒ()",
|
|
1656
|
+
editable: false
|
|
1657
|
+
};
|
|
1658
|
+
if (val && typeof val === "object") return {
|
|
1659
|
+
key,
|
|
1660
|
+
value: Array.isArray(val) ? `Array(${val.length})` : "Object",
|
|
1661
|
+
editable: false
|
|
1662
|
+
};
|
|
1663
|
+
return {
|
|
1664
|
+
key,
|
|
1665
|
+
value: val,
|
|
1666
|
+
editable: false
|
|
1667
|
+
};
|
|
1668
|
+
}) };
|
|
1669
|
+
}
|
|
1670
|
+
};
|
|
1671
|
+
const editSceneObject = (scene, objectUuid, propertyPath, value) => {
|
|
1672
|
+
const targetObject = getObjectByUuid(scene, objectUuid);
|
|
1673
|
+
if (!targetObject) {
|
|
1674
|
+
console.warn("Object with UUID not found in the scene.");
|
|
1675
|
+
return;
|
|
1676
|
+
}
|
|
1677
|
+
let currentProperty = targetObject;
|
|
1678
|
+
for (let i = 0; i < propertyPath.length - 1; i++) if (currentProperty[propertyPath[i]] !== void 0) currentProperty = currentProperty[propertyPath[i]];
|
|
1679
|
+
else {
|
|
1680
|
+
console.warn(`Property path is not valid: ${propertyPath.join(".")}`);
|
|
1681
|
+
return;
|
|
1682
|
+
}
|
|
1683
|
+
const lastProperty = propertyPath[propertyPath.length - 1];
|
|
1684
|
+
if (currentProperty[lastProperty] !== void 0) currentProperty[lastProperty] = value;
|
|
1685
|
+
else console.warn(`Property path is not valid: ${propertyPath.join(".")}`);
|
|
1686
|
+
};
|
|
1687
|
+
/**
|
|
1688
|
+
* Handler for inspector state edits
|
|
1689
|
+
* @param tres - The TresContext instance
|
|
1690
|
+
* @returns A function that handles inspector state edit payload updates
|
|
1691
|
+
*/
|
|
1692
|
+
const inspectorEditStateHandler = (tres) => (payload) => {
|
|
1693
|
+
if (payload.inspectorId === "tres:inspector") {
|
|
1694
|
+
if (payload.nodeId.includes("scene")) {
|
|
1695
|
+
const match = payload.nodeId.match(/^scene-(.+)$/);
|
|
1696
|
+
const uuid = match ? match[1] : null;
|
|
1697
|
+
if (!uuid) return;
|
|
1698
|
+
editSceneObject(tres.scene.value, uuid, payload.path, payload.state.value);
|
|
1699
|
+
}
|
|
1700
|
+
}
|
|
1701
|
+
};
|
|
1702
|
+
//#endregion
|
|
1703
|
+
//#region src/devtools/plugin.ts
|
|
1704
|
+
const INSPECTOR_ID = "tres:inspector";
|
|
1705
|
+
function registerTresDevtools(app, tres) {
|
|
1706
|
+
const pluginDescriptor = {
|
|
1707
|
+
id: "dev.esm.tres",
|
|
1708
|
+
label: "TresJS 🪐",
|
|
1709
|
+
logo: "https://raw.githubusercontent.com/Tresjs/tres/main/public/favicon.svg",
|
|
1710
|
+
packageName: "tresjs",
|
|
1711
|
+
homepage: "https://docs.tresjs.org",
|
|
1712
|
+
app
|
|
1713
|
+
};
|
|
1714
|
+
const highlightMesh = null;
|
|
1715
|
+
const prevInstance = null;
|
|
1716
|
+
setupTresDevtools(tres);
|
|
1717
|
+
setupDevtoolsPlugin(pluginDescriptor, (api) => {
|
|
1718
|
+
if (typeof api.now !== "function") toastMessage("You seem to be using an outdated version of Vue Devtools. Are you still using the Beta release instead of the stable one? You can find the links at https://devtools.vuejs.org/guide/installation.html.");
|
|
1719
|
+
api.addInspector({
|
|
1720
|
+
id: INSPECTOR_ID,
|
|
1721
|
+
label: "TresJS 🪐",
|
|
1722
|
+
icon: "account_tree",
|
|
1723
|
+
treeFilterPlaceholder: "Search instances"
|
|
1724
|
+
});
|
|
1725
|
+
setInterval(() => {
|
|
1726
|
+
api.sendInspectorTree(INSPECTOR_ID);
|
|
1727
|
+
}, 1e3);
|
|
1728
|
+
setInterval(() => {
|
|
1729
|
+
api.notifyComponentUpdate();
|
|
1730
|
+
}, 5e3);
|
|
1731
|
+
api.on.getInspectorTree(inspectorTreeHandler(tres));
|
|
1732
|
+
api.on.getInspectorState(inspectorStateHandler(tres, {
|
|
1733
|
+
highlightMesh,
|
|
1734
|
+
prevInstance
|
|
1735
|
+
}));
|
|
1736
|
+
api.on.editInspectorState(inspectorEditStateHandler(tres));
|
|
1737
|
+
});
|
|
1738
|
+
}
|
|
1739
|
+
//#endregion
|
|
1740
|
+
//#region src/components/Context.vue
|
|
1741
|
+
var Context_default = /* @__PURE__ */ defineComponent({
|
|
1742
|
+
__name: "Context",
|
|
1743
|
+
props: {
|
|
1744
|
+
camera: {
|
|
1745
|
+
type: null,
|
|
1746
|
+
required: false
|
|
1747
|
+
},
|
|
1748
|
+
windowSize: {
|
|
1749
|
+
type: Boolean,
|
|
1750
|
+
required: false
|
|
1751
|
+
},
|
|
1752
|
+
fpsLimit: {
|
|
1753
|
+
type: Number,
|
|
1754
|
+
required: false
|
|
1755
|
+
},
|
|
1756
|
+
enableProvideBridge: {
|
|
1757
|
+
type: Boolean,
|
|
1758
|
+
required: false
|
|
1759
|
+
},
|
|
1760
|
+
customRendererOptions: {
|
|
1761
|
+
type: Object,
|
|
1762
|
+
required: false
|
|
1763
|
+
},
|
|
1764
|
+
antialias: {
|
|
1765
|
+
type: Boolean,
|
|
1766
|
+
required: false
|
|
1767
|
+
},
|
|
1768
|
+
stencil: {
|
|
1769
|
+
type: Boolean,
|
|
1770
|
+
required: false
|
|
1771
|
+
},
|
|
1772
|
+
depth: {
|
|
1773
|
+
type: Boolean,
|
|
1774
|
+
required: false
|
|
1775
|
+
},
|
|
1776
|
+
precision: {
|
|
1777
|
+
type: String,
|
|
1778
|
+
required: false
|
|
1779
|
+
},
|
|
1780
|
+
logarithmicDepthBuffer: {
|
|
1781
|
+
type: Boolean,
|
|
1782
|
+
required: false
|
|
1783
|
+
},
|
|
1784
|
+
preserveDrawingBuffer: {
|
|
1785
|
+
type: Boolean,
|
|
1786
|
+
required: false
|
|
1787
|
+
},
|
|
1788
|
+
powerPreference: {
|
|
1789
|
+
type: null,
|
|
1790
|
+
required: false
|
|
1791
|
+
},
|
|
1792
|
+
alpha: {
|
|
1793
|
+
type: Boolean,
|
|
1794
|
+
required: false
|
|
1795
|
+
},
|
|
1796
|
+
premultipliedAlpha: {
|
|
1797
|
+
type: Boolean,
|
|
1798
|
+
required: false
|
|
1799
|
+
},
|
|
1800
|
+
failIfMajorPerformanceCaveat: {
|
|
1801
|
+
type: Boolean,
|
|
1802
|
+
required: false
|
|
1803
|
+
},
|
|
1804
|
+
clearColor: {
|
|
1805
|
+
type: [
|
|
1806
|
+
Object,
|
|
1807
|
+
String,
|
|
1808
|
+
Number
|
|
1809
|
+
],
|
|
1810
|
+
required: false
|
|
1811
|
+
},
|
|
1812
|
+
clearAlpha: {
|
|
1813
|
+
type: Number,
|
|
1814
|
+
required: false
|
|
1815
|
+
},
|
|
1816
|
+
shadows: {
|
|
1817
|
+
type: Boolean,
|
|
1818
|
+
required: false
|
|
1819
|
+
},
|
|
1820
|
+
toneMapping: {
|
|
1821
|
+
type: null,
|
|
1822
|
+
required: false
|
|
1823
|
+
},
|
|
1824
|
+
shadowMapType: {
|
|
1825
|
+
type: null,
|
|
1826
|
+
required: false
|
|
1827
|
+
},
|
|
1828
|
+
useLegacyLights: {
|
|
1829
|
+
type: Boolean,
|
|
1830
|
+
required: false
|
|
1831
|
+
},
|
|
1832
|
+
outputColorSpace: {
|
|
1833
|
+
type: null,
|
|
1834
|
+
required: false
|
|
1835
|
+
},
|
|
1836
|
+
toneMappingExposure: {
|
|
1837
|
+
type: Number,
|
|
1838
|
+
required: false
|
|
1839
|
+
},
|
|
1840
|
+
renderMode: {
|
|
1841
|
+
type: String,
|
|
1842
|
+
required: false
|
|
1843
|
+
},
|
|
1844
|
+
dpr: {
|
|
1845
|
+
type: [Number, Array],
|
|
1846
|
+
required: false
|
|
1847
|
+
},
|
|
1848
|
+
renderer: {
|
|
1849
|
+
type: Function,
|
|
1850
|
+
required: false
|
|
1851
|
+
},
|
|
1852
|
+
canvas: {
|
|
1853
|
+
type: null,
|
|
1854
|
+
required: true
|
|
1855
|
+
}
|
|
1856
|
+
},
|
|
1857
|
+
emits: [
|
|
1858
|
+
"ready",
|
|
1859
|
+
"error",
|
|
1860
|
+
"pointermissed",
|
|
1861
|
+
"render",
|
|
1862
|
+
"beforeLoop",
|
|
1863
|
+
"loop",
|
|
1864
|
+
"click",
|
|
1865
|
+
"contextmenu",
|
|
1866
|
+
"pointermove",
|
|
1867
|
+
"pointerenter",
|
|
1868
|
+
"pointerleave",
|
|
1869
|
+
"pointerover",
|
|
1870
|
+
"pointerout",
|
|
1871
|
+
"dblclick",
|
|
1872
|
+
"pointerdown",
|
|
1873
|
+
"pointerup",
|
|
1874
|
+
"pointercancel",
|
|
1875
|
+
"lostpointercapture",
|
|
1876
|
+
"wheel"
|
|
1877
|
+
],
|
|
1878
|
+
setup(__props, { expose: __expose, emit: __emit }) {
|
|
1879
|
+
const props = __props;
|
|
1880
|
+
const emit = __emit;
|
|
1881
|
+
const slots = useSlots();
|
|
1882
|
+
const scene = shallowRef(new Scene());
|
|
1883
|
+
const instance = getCurrentInstance();
|
|
1884
|
+
const createInternalComponent = (context, hmrTick) => defineComponent({ setup() {
|
|
1885
|
+
const ctx = getCurrentInstance()?.appContext;
|
|
1886
|
+
if (ctx) ctx.app = instance?.appContext.app;
|
|
1887
|
+
const provides = {};
|
|
1888
|
+
function mergeProvides(currentInstance) {
|
|
1889
|
+
if (!currentInstance) return;
|
|
1890
|
+
if (currentInstance.parent) mergeProvides(currentInstance.parent);
|
|
1891
|
+
if (currentInstance.provides) Object.assign(provides, currentInstance.provides);
|
|
1892
|
+
}
|
|
1893
|
+
if (instance?.parent && props.enableProvideBridge) {
|
|
1894
|
+
mergeProvides(instance.parent);
|
|
1895
|
+
Reflect.ownKeys(provides).forEach((key) => {
|
|
1896
|
+
provide(key, provides[key]);
|
|
1897
|
+
});
|
|
1898
|
+
}
|
|
1899
|
+
provide(INJECTION_KEY, context);
|
|
1900
|
+
provide("extend", extend);
|
|
1901
|
+
if (typeof window !== "undefined" && ctx?.app) registerTresDevtools(ctx?.app, context);
|
|
1902
|
+
return () => {
|
|
1903
|
+
hmrTick.value;
|
|
1904
|
+
return h(Fragment, null, slots.default?.() ?? []);
|
|
1905
|
+
};
|
|
1906
|
+
} });
|
|
1907
|
+
const mountCustomRenderer = (context) => {
|
|
1908
|
+
const canvas = props.canvas;
|
|
1909
|
+
if (getRoot(canvas)) return;
|
|
1910
|
+
const hmrTick = ref(0);
|
|
1911
|
+
const internalComponent = createInternalComponent(context, hmrTick);
|
|
1912
|
+
const renderer = createRenderer(nodeOps({
|
|
1913
|
+
context,
|
|
1914
|
+
options: props.customRendererOptions
|
|
1915
|
+
}));
|
|
1916
|
+
renderer.render(h(internalComponent), scene.value);
|
|
1917
|
+
setRoot(canvas, {
|
|
1918
|
+
renderer,
|
|
1919
|
+
internalComponent,
|
|
1920
|
+
hmrTick,
|
|
1921
|
+
context
|
|
1922
|
+
});
|
|
1923
|
+
};
|
|
1924
|
+
const dispose = (context, force = false) => {
|
|
1925
|
+
disposeObject3D(context.scene.value);
|
|
1926
|
+
if (force) {
|
|
1927
|
+
context.renderer.instance.dispose();
|
|
1928
|
+
if (isWebGLRenderer(context.renderer.instance)) {
|
|
1929
|
+
context.renderer.instance.renderLists.dispose();
|
|
1930
|
+
context.renderer.instance.forceContextLoss();
|
|
1931
|
+
}
|
|
1932
|
+
}
|
|
1933
|
+
scene.value.__tres = {
|
|
1934
|
+
root: context,
|
|
1935
|
+
objects: [],
|
|
1936
|
+
isUnmounting: true
|
|
1937
|
+
};
|
|
1938
|
+
};
|
|
1939
|
+
const context = shallowRef(useBaseTresContextProvider({
|
|
1940
|
+
scene: scene.value,
|
|
1941
|
+
canvas: props.canvas,
|
|
1942
|
+
fpsLimit: () => props.fpsLimit ?? Infinity,
|
|
1943
|
+
windowSize: props.windowSize ?? false,
|
|
1944
|
+
rendererOptions: props
|
|
1945
|
+
}));
|
|
1946
|
+
__expose({
|
|
1947
|
+
context,
|
|
1948
|
+
dispose: () => dispose(context.value, true)
|
|
1949
|
+
});
|
|
1950
|
+
const handleHMR = () => {
|
|
1951
|
+
const root = getRoot(props.canvas);
|
|
1952
|
+
if (root) root.hmrTick.value++;
|
|
1953
|
+
};
|
|
1954
|
+
const unmountCanvas = () => {
|
|
1955
|
+
const root = getRoot(props.canvas);
|
|
1956
|
+
if (!root) return;
|
|
1957
|
+
const isTresScene = (value) => isScene(value) && "__tres" in value;
|
|
1958
|
+
if (isTresScene(scene.value)) scene.value.__tres.isUnmounting = true;
|
|
1959
|
+
root.renderer.render(null, scene.value);
|
|
1960
|
+
dispose(context.value);
|
|
1961
|
+
deleteRoot(props.canvas);
|
|
1962
|
+
};
|
|
1963
|
+
const { camera, renderer } = context.value;
|
|
1964
|
+
const { registerCamera, cameras, activeCamera, deregisterCamera } = camera;
|
|
1965
|
+
const addDefaultCamera = () => {
|
|
1966
|
+
const camera = new PerspectiveCamera(45, window.innerWidth / window.innerHeight, .1, 1e3);
|
|
1967
|
+
camera.position.set(3, 3, 3);
|
|
1968
|
+
camera.lookAt(0, 0, 0);
|
|
1969
|
+
registerCamera(camera);
|
|
1970
|
+
const unwatch = watchEffect(() => {
|
|
1971
|
+
if (cameras.value.length >= 2) {
|
|
1972
|
+
camera.removeFromParent();
|
|
1973
|
+
deregisterCamera(camera);
|
|
1974
|
+
unwatch?.();
|
|
1975
|
+
}
|
|
1976
|
+
});
|
|
1977
|
+
};
|
|
1978
|
+
context.value.events.onPointerMissed((event) => {
|
|
1979
|
+
emit("pointermissed", event);
|
|
1980
|
+
});
|
|
1981
|
+
watch(() => props.camera, (newCamera, oldCamera) => {
|
|
1982
|
+
if (newCamera) registerCamera(toValue(newCamera), true);
|
|
1983
|
+
if (oldCamera) {
|
|
1984
|
+
toValue(oldCamera).removeFromParent();
|
|
1985
|
+
deregisterCamera(toValue(oldCamera));
|
|
1986
|
+
}
|
|
1987
|
+
}, { immediate: true });
|
|
1988
|
+
renderer.onRender(() => {
|
|
1989
|
+
if (context.value) emit("render", context.value);
|
|
1990
|
+
});
|
|
1991
|
+
renderer.loop.onLoop((loopContext) => {
|
|
1992
|
+
if (context.value) emit("loop", {
|
|
1993
|
+
...context.value,
|
|
1994
|
+
...loopContext
|
|
1995
|
+
});
|
|
1996
|
+
});
|
|
1997
|
+
renderer.loop.onBeforeLoop((loopContext) => {
|
|
1998
|
+
if (context.value) emit("beforeLoop", {
|
|
1999
|
+
...context.value,
|
|
2000
|
+
...loopContext
|
|
2001
|
+
});
|
|
2002
|
+
});
|
|
2003
|
+
renderer.onReady(() => {
|
|
2004
|
+
mountCustomRenderer(context.value);
|
|
2005
|
+
emit("ready", context.value);
|
|
2006
|
+
if (!activeCamera.value) addDefaultCamera();
|
|
2007
|
+
});
|
|
2008
|
+
renderer.onError((error) => {
|
|
2009
|
+
emit("error", error);
|
|
2010
|
+
});
|
|
2011
|
+
if (import.meta.hot) {
|
|
2012
|
+
const hmrHandler = () => handleHMR();
|
|
2013
|
+
import.meta.hot.on("vite:afterUpdate", hmrHandler);
|
|
2014
|
+
onBeforeUnmount(() => {
|
|
2015
|
+
import.meta.hot?.off?.("vite:afterUpdate", hmrHandler);
|
|
2016
|
+
});
|
|
2017
|
+
}
|
|
2018
|
+
onMounted(async () => {
|
|
2019
|
+
await promiseTimeout(3e3);
|
|
2020
|
+
if (!context.value.sizes.width || !context.value.sizes.height.value) console.warn(`TresCanvas: The canvas has no area, so nothing can be rendered. Set it manually on the parent element or use the prop windowSize.`);
|
|
2021
|
+
});
|
|
2022
|
+
onUnmounted(unmountCanvas);
|
|
2023
|
+
return () => {};
|
|
2024
|
+
}
|
|
2025
|
+
});
|
|
2026
|
+
//#endregion
|
|
2027
|
+
//#region src/components/forwardContextEmits.ts
|
|
2028
|
+
const contextEvents = {
|
|
2029
|
+
ready: true,
|
|
2030
|
+
error: true,
|
|
2031
|
+
pointermissed: true,
|
|
2032
|
+
render: true,
|
|
2033
|
+
beforeLoop: true,
|
|
2034
|
+
loop: true,
|
|
2035
|
+
click: true,
|
|
2036
|
+
contextmenu: true,
|
|
2037
|
+
pointermove: true,
|
|
2038
|
+
pointerenter: true,
|
|
2039
|
+
pointerleave: true,
|
|
2040
|
+
pointerover: true,
|
|
2041
|
+
pointerout: true,
|
|
2042
|
+
dblclick: true,
|
|
2043
|
+
pointerdown: true,
|
|
2044
|
+
pointerup: true,
|
|
2045
|
+
pointercancel: true,
|
|
2046
|
+
lostpointercapture: true,
|
|
2047
|
+
wheel: true
|
|
2048
|
+
};
|
|
2049
|
+
/**
|
|
2050
|
+
* Listeners for `v-on` that re-emit every `ContextEmits` event through `emit`.
|
|
2051
|
+
* Both `TresCanvas` components declare these emits for typed handlers, and a declared emit
|
|
2052
|
+
* does not fall through `$attrs`, so each wrapper must forward them.
|
|
2053
|
+
*/
|
|
2054
|
+
const forwardContextEmits = (emit) => Object.fromEntries(Object.keys(contextEvents).map((event) => [event, (...args) => emit(event, ...args)]));
|
|
2055
|
+
const forwardContext = (emit, child) => {
|
|
2056
|
+
return {
|
|
2057
|
+
listeners: forwardContextEmits(emit),
|
|
2058
|
+
exposed: {
|
|
2059
|
+
get context() {
|
|
2060
|
+
return child.value?.context;
|
|
2061
|
+
},
|
|
2062
|
+
dispose: () => child.value?.dispose()
|
|
2063
|
+
}
|
|
2064
|
+
};
|
|
2065
|
+
};
|
|
2066
|
+
//#endregion
|
|
2067
|
+
//#region src/components/tresCanvasDefaults.ts
|
|
2068
|
+
/**
|
|
2069
|
+
* Prop defaults shared by the root and the WebGPU `TresCanvas`.
|
|
2070
|
+
* Boolean props default to `undefined`, else Vue casts an absent boolean prop to `false`
|
|
2071
|
+
* and it overrides the renderer's own default.
|
|
2072
|
+
*/
|
|
2073
|
+
const tresCanvasDefaults = {
|
|
2074
|
+
alpha: void 0,
|
|
2075
|
+
depth: void 0,
|
|
2076
|
+
shadows: void 0,
|
|
2077
|
+
stencil: void 0,
|
|
2078
|
+
antialias: true,
|
|
2079
|
+
windowSize: void 0,
|
|
2080
|
+
useLegacyLights: void 0,
|
|
2081
|
+
preserveDrawingBuffer: void 0,
|
|
2082
|
+
logarithmicDepthBuffer: void 0,
|
|
2083
|
+
failIfMajorPerformanceCaveat: void 0,
|
|
2084
|
+
renderMode: "always",
|
|
2085
|
+
clearColor: "#000000",
|
|
2086
|
+
clearAlpha: 1,
|
|
2087
|
+
enableProvideBridge: true,
|
|
2088
|
+
toneMapping: ACESFilmicToneMapping,
|
|
2089
|
+
shadowMapType: void 0,
|
|
2090
|
+
customRendererOptions: () => ({ primitivePrefix: "" })
|
|
2091
|
+
};
|
|
2092
|
+
//#endregion
|
|
2093
|
+
//#region src/components/BaseCanvas.vue?vue&type=script&setup=true&lang.ts
|
|
2094
|
+
const _hoisted_1 = ["data-scene", "data-tres"];
|
|
2095
|
+
//#endregion
|
|
2096
|
+
//#region src/components/BaseCanvas.vue
|
|
2097
|
+
var BaseCanvas_default = /* @__PURE__ */ defineComponent({
|
|
2098
|
+
__name: "BaseCanvas",
|
|
2099
|
+
props: /*@__PURE__*/ mergeDefaults({
|
|
2100
|
+
camera: {
|
|
2101
|
+
type: null,
|
|
2102
|
+
required: false
|
|
2103
|
+
},
|
|
2104
|
+
windowSize: {
|
|
2105
|
+
type: Boolean,
|
|
2106
|
+
required: false
|
|
2107
|
+
},
|
|
2108
|
+
fpsLimit: {
|
|
2109
|
+
type: Number,
|
|
2110
|
+
required: false
|
|
2111
|
+
},
|
|
2112
|
+
enableProvideBridge: {
|
|
2113
|
+
type: Boolean,
|
|
2114
|
+
required: false
|
|
2115
|
+
},
|
|
2116
|
+
customRendererOptions: {
|
|
2117
|
+
type: Object,
|
|
2118
|
+
required: false
|
|
2119
|
+
},
|
|
2120
|
+
antialias: {
|
|
2121
|
+
type: Boolean,
|
|
2122
|
+
required: false
|
|
2123
|
+
},
|
|
2124
|
+
stencil: {
|
|
2125
|
+
type: Boolean,
|
|
2126
|
+
required: false
|
|
2127
|
+
},
|
|
2128
|
+
depth: {
|
|
2129
|
+
type: Boolean,
|
|
2130
|
+
required: false
|
|
2131
|
+
},
|
|
2132
|
+
precision: {
|
|
2133
|
+
type: String,
|
|
2134
|
+
required: false
|
|
2135
|
+
},
|
|
2136
|
+
logarithmicDepthBuffer: {
|
|
2137
|
+
type: Boolean,
|
|
2138
|
+
required: false
|
|
2139
|
+
},
|
|
2140
|
+
preserveDrawingBuffer: {
|
|
2141
|
+
type: Boolean,
|
|
2142
|
+
required: false
|
|
2143
|
+
},
|
|
2144
|
+
powerPreference: {
|
|
2145
|
+
type: null,
|
|
2146
|
+
required: false
|
|
2147
|
+
},
|
|
2148
|
+
alpha: {
|
|
2149
|
+
type: Boolean,
|
|
2150
|
+
required: false
|
|
2151
|
+
},
|
|
2152
|
+
premultipliedAlpha: {
|
|
2153
|
+
type: Boolean,
|
|
2154
|
+
required: false
|
|
2155
|
+
},
|
|
2156
|
+
failIfMajorPerformanceCaveat: {
|
|
2157
|
+
type: Boolean,
|
|
2158
|
+
required: false
|
|
2159
|
+
},
|
|
2160
|
+
clearColor: {
|
|
2161
|
+
type: [
|
|
2162
|
+
Object,
|
|
2163
|
+
String,
|
|
2164
|
+
Number
|
|
2165
|
+
],
|
|
2166
|
+
required: false
|
|
2167
|
+
},
|
|
2168
|
+
clearAlpha: {
|
|
2169
|
+
type: Number,
|
|
2170
|
+
required: false
|
|
2171
|
+
},
|
|
2172
|
+
shadows: {
|
|
2173
|
+
type: Boolean,
|
|
2174
|
+
required: false
|
|
2175
|
+
},
|
|
2176
|
+
toneMapping: {
|
|
2177
|
+
type: null,
|
|
2178
|
+
required: false
|
|
2179
|
+
},
|
|
2180
|
+
shadowMapType: {
|
|
2181
|
+
type: null,
|
|
2182
|
+
required: false
|
|
2183
|
+
},
|
|
2184
|
+
useLegacyLights: {
|
|
2185
|
+
type: Boolean,
|
|
2186
|
+
required: false
|
|
2187
|
+
},
|
|
2188
|
+
outputColorSpace: {
|
|
2189
|
+
type: null,
|
|
2190
|
+
required: false
|
|
2191
|
+
},
|
|
2192
|
+
toneMappingExposure: {
|
|
2193
|
+
type: Number,
|
|
2194
|
+
required: false
|
|
2195
|
+
},
|
|
2196
|
+
renderMode: {
|
|
2197
|
+
type: String,
|
|
2198
|
+
required: false
|
|
2199
|
+
},
|
|
2200
|
+
dpr: {
|
|
2201
|
+
type: [Number, Array],
|
|
2202
|
+
required: false
|
|
2203
|
+
},
|
|
2204
|
+
renderer: {
|
|
2205
|
+
type: Function,
|
|
2206
|
+
required: false
|
|
2207
|
+
}
|
|
2208
|
+
}, tresCanvasDefaults),
|
|
2209
|
+
emits: [
|
|
2210
|
+
"ready",
|
|
2211
|
+
"error",
|
|
2212
|
+
"pointermissed",
|
|
2213
|
+
"render",
|
|
2214
|
+
"beforeLoop",
|
|
2215
|
+
"loop",
|
|
2216
|
+
"click",
|
|
2217
|
+
"contextmenu",
|
|
2218
|
+
"pointermove",
|
|
2219
|
+
"pointerenter",
|
|
2220
|
+
"pointerleave",
|
|
2221
|
+
"pointerover",
|
|
2222
|
+
"pointerout",
|
|
2223
|
+
"dblclick",
|
|
2224
|
+
"pointerdown",
|
|
2225
|
+
"pointerup",
|
|
2226
|
+
"pointercancel",
|
|
2227
|
+
"lostpointercapture",
|
|
2228
|
+
"wheel"
|
|
2229
|
+
],
|
|
2230
|
+
setup(__props, { expose: __expose, emit: __emit }) {
|
|
2231
|
+
const props = __props;
|
|
2232
|
+
const contextListeners = forwardContextEmits(__emit);
|
|
2233
|
+
const canvasRef = ref();
|
|
2234
|
+
const contextRef = shallowRef();
|
|
2235
|
+
__expose({
|
|
2236
|
+
get context() {
|
|
2237
|
+
return contextRef.value?.context;
|
|
2238
|
+
},
|
|
2239
|
+
dispose: () => contextRef.value?.dispose()
|
|
2240
|
+
});
|
|
2241
|
+
return (_ctx, _cache) => {
|
|
2242
|
+
return openBlock(), createElementBlock("canvas", {
|
|
2243
|
+
ref_key: "canvasRef",
|
|
2244
|
+
ref: canvasRef,
|
|
2245
|
+
"data-scene": contextRef.value?.context?.scene.value.uuid,
|
|
2246
|
+
class: normalizeClass(_ctx.$attrs.class),
|
|
2247
|
+
"data-tres": `tresjs ${unref(version)}`,
|
|
2248
|
+
style: normalizeStyle({
|
|
2249
|
+
display: "block",
|
|
2250
|
+
width: "100%",
|
|
2251
|
+
height: "100%",
|
|
2252
|
+
position: __props.windowSize ? "fixed" : "relative",
|
|
2253
|
+
top: 0,
|
|
2254
|
+
left: 0,
|
|
2255
|
+
pointerEvents: "auto",
|
|
2256
|
+
touchAction: "none",
|
|
2257
|
+
..._ctx.$attrs.style
|
|
2258
|
+
})
|
|
2259
|
+
}, [canvasRef.value ? (openBlock(), createBlock(Context_default, mergeProps({
|
|
2260
|
+
key: 0,
|
|
2261
|
+
ref_key: "contextRef",
|
|
2262
|
+
ref: contextRef,
|
|
2263
|
+
canvas: canvasRef.value
|
|
2264
|
+
}, props, toHandlers(unref(contextListeners))), {
|
|
2265
|
+
default: withCtx(() => [renderSlot(_ctx.$slots, "default")]),
|
|
2266
|
+
_: 3
|
|
2267
|
+
}, 16, ["canvas"])) : createCommentVNode("v-if", true)], 14, _hoisted_1);
|
|
2268
|
+
};
|
|
2269
|
+
}
|
|
2270
|
+
});
|
|
2271
|
+
//#endregion
|
|
2272
|
+
//#region src/components/TresCanvas.vue
|
|
2273
|
+
var TresCanvas_default = /* @__PURE__ */ defineComponent({
|
|
2274
|
+
__name: "TresCanvas",
|
|
2275
|
+
props: /*@__PURE__*/ mergeDefaults({
|
|
2276
|
+
camera: {
|
|
2277
|
+
type: null,
|
|
2278
|
+
required: false
|
|
2279
|
+
},
|
|
2280
|
+
windowSize: {
|
|
2281
|
+
type: Boolean,
|
|
2282
|
+
required: false
|
|
2283
|
+
},
|
|
2284
|
+
fpsLimit: {
|
|
2285
|
+
type: Number,
|
|
2286
|
+
required: false
|
|
2287
|
+
},
|
|
2288
|
+
enableProvideBridge: {
|
|
2289
|
+
type: Boolean,
|
|
2290
|
+
required: false
|
|
2291
|
+
},
|
|
2292
|
+
customRendererOptions: {
|
|
2293
|
+
type: Object,
|
|
2294
|
+
required: false
|
|
2295
|
+
},
|
|
2296
|
+
antialias: {
|
|
2297
|
+
type: Boolean,
|
|
2298
|
+
required: false
|
|
2299
|
+
},
|
|
2300
|
+
stencil: {
|
|
2301
|
+
type: Boolean,
|
|
2302
|
+
required: false
|
|
2303
|
+
},
|
|
2304
|
+
depth: {
|
|
2305
|
+
type: Boolean,
|
|
2306
|
+
required: false
|
|
2307
|
+
},
|
|
2308
|
+
precision: {
|
|
2309
|
+
type: String,
|
|
2310
|
+
required: false
|
|
2311
|
+
},
|
|
2312
|
+
logarithmicDepthBuffer: {
|
|
2313
|
+
type: Boolean,
|
|
2314
|
+
required: false
|
|
2315
|
+
},
|
|
2316
|
+
preserveDrawingBuffer: {
|
|
2317
|
+
type: Boolean,
|
|
2318
|
+
required: false
|
|
2319
|
+
},
|
|
2320
|
+
powerPreference: {
|
|
2321
|
+
type: null,
|
|
2322
|
+
required: false
|
|
2323
|
+
},
|
|
2324
|
+
alpha: {
|
|
2325
|
+
type: Boolean,
|
|
2326
|
+
required: false
|
|
2327
|
+
},
|
|
2328
|
+
premultipliedAlpha: {
|
|
2329
|
+
type: Boolean,
|
|
2330
|
+
required: false
|
|
2331
|
+
},
|
|
2332
|
+
failIfMajorPerformanceCaveat: {
|
|
2333
|
+
type: Boolean,
|
|
2334
|
+
required: false
|
|
2335
|
+
},
|
|
2336
|
+
clearColor: {
|
|
2337
|
+
type: [
|
|
2338
|
+
Object,
|
|
2339
|
+
String,
|
|
2340
|
+
Number
|
|
2341
|
+
],
|
|
2342
|
+
required: false
|
|
2343
|
+
},
|
|
2344
|
+
clearAlpha: {
|
|
2345
|
+
type: Number,
|
|
2346
|
+
required: false
|
|
2347
|
+
},
|
|
2348
|
+
shadows: {
|
|
2349
|
+
type: Boolean,
|
|
2350
|
+
required: false
|
|
2351
|
+
},
|
|
2352
|
+
toneMapping: {
|
|
2353
|
+
type: null,
|
|
2354
|
+
required: false
|
|
2355
|
+
},
|
|
2356
|
+
shadowMapType: {
|
|
2357
|
+
type: null,
|
|
2358
|
+
required: false
|
|
2359
|
+
},
|
|
2360
|
+
useLegacyLights: {
|
|
2361
|
+
type: Boolean,
|
|
2362
|
+
required: false
|
|
2363
|
+
},
|
|
2364
|
+
outputColorSpace: {
|
|
2365
|
+
type: null,
|
|
2366
|
+
required: false
|
|
2367
|
+
},
|
|
2368
|
+
toneMappingExposure: {
|
|
2369
|
+
type: Number,
|
|
2370
|
+
required: false
|
|
2371
|
+
},
|
|
2372
|
+
renderMode: {
|
|
2373
|
+
type: String,
|
|
2374
|
+
required: false
|
|
2375
|
+
},
|
|
2376
|
+
dpr: {
|
|
2377
|
+
type: [Number, Array],
|
|
2378
|
+
required: false
|
|
2379
|
+
},
|
|
2380
|
+
renderer: {
|
|
2381
|
+
type: Function,
|
|
2382
|
+
required: false
|
|
2383
|
+
}
|
|
2384
|
+
}, tresCanvasDefaults),
|
|
2385
|
+
emits: [
|
|
2386
|
+
"ready",
|
|
2387
|
+
"error",
|
|
2388
|
+
"pointermissed",
|
|
2389
|
+
"render",
|
|
2390
|
+
"beforeLoop",
|
|
2391
|
+
"loop",
|
|
2392
|
+
"click",
|
|
2393
|
+
"contextmenu",
|
|
2394
|
+
"pointermove",
|
|
2395
|
+
"pointerenter",
|
|
2396
|
+
"pointerleave",
|
|
2397
|
+
"pointerover",
|
|
2398
|
+
"pointerout",
|
|
2399
|
+
"dblclick",
|
|
2400
|
+
"pointerdown",
|
|
2401
|
+
"pointerup",
|
|
2402
|
+
"pointercancel",
|
|
2403
|
+
"lostpointercapture",
|
|
2404
|
+
"wheel"
|
|
2405
|
+
],
|
|
2406
|
+
setup(__props, { expose: __expose, emit: __emit }) {
|
|
2407
|
+
const props = __props;
|
|
2408
|
+
const emit = __emit;
|
|
2409
|
+
const child = shallowRef();
|
|
2410
|
+
const { listeners, exposed } = forwardContext(emit, child);
|
|
2411
|
+
__expose(exposed);
|
|
2412
|
+
extendWithThree();
|
|
2413
|
+
const createDefaultRenderer = (ctx) => createWebGLRenderer(ctx, props);
|
|
2414
|
+
return (_ctx, _cache) => {
|
|
2415
|
+
return openBlock(), createBlock(BaseCanvas_default, mergeProps({
|
|
2416
|
+
ref_key: "child",
|
|
2417
|
+
ref: child
|
|
2418
|
+
}, props, { renderer: props.renderer ?? createDefaultRenderer }, toHandlers(unref(listeners))), {
|
|
2419
|
+
default: withCtx(() => [renderSlot(_ctx.$slots, "default")]),
|
|
2420
|
+
_: 3
|
|
2421
|
+
}, 16, ["renderer"]);
|
|
2422
|
+
};
|
|
2423
|
+
}
|
|
2424
|
+
});
|
|
2425
|
+
//#endregion
|
|
2426
|
+
//#region src/utils/normalize.ts
|
|
2427
|
+
function normalizeVectorFlexibleParam(value) {
|
|
2428
|
+
if (typeof value === "number") return [
|
|
2429
|
+
value,
|
|
2430
|
+
value,
|
|
2431
|
+
value
|
|
2432
|
+
];
|
|
2433
|
+
if (value instanceof Vector3) return [
|
|
2434
|
+
value.x,
|
|
2435
|
+
value.y,
|
|
2436
|
+
value.z
|
|
2437
|
+
];
|
|
2438
|
+
return value;
|
|
2439
|
+
}
|
|
2440
|
+
function normalizeColor(value) {
|
|
2441
|
+
if (value instanceof Color) return value;
|
|
2442
|
+
if (Array.isArray(value)) return new Color(...value);
|
|
2443
|
+
return new Color(value);
|
|
2444
|
+
}
|
|
2445
|
+
//#endregion
|
|
2446
|
+
//#region src/utils/template-compiler-options.ts
|
|
2447
|
+
const whitelist = [
|
|
2448
|
+
"TresCanvas",
|
|
2449
|
+
"TresCanvasContext",
|
|
2450
|
+
"TresLeches",
|
|
2451
|
+
"TresScene",
|
|
2452
|
+
"TresPortal"
|
|
2453
|
+
];
|
|
2454
|
+
const templateCompilerOptions = { template: { compilerOptions: { isCustomElement: (tag) => (/^Tres[A-Z]/.test(tag) || tag.startsWith("tres-")) && !whitelist.includes(tag) || tag === "primitive" } } };
|
|
2455
|
+
//#endregion
|
|
2456
|
+
//#region src/components/WebGLContext.vue
|
|
2457
|
+
var WebGLContext_default = /* @__PURE__ */ defineComponent({
|
|
2458
|
+
__name: "WebGLContext",
|
|
2459
|
+
props: {
|
|
2460
|
+
camera: {
|
|
2461
|
+
type: null,
|
|
2462
|
+
required: false
|
|
2463
|
+
},
|
|
2464
|
+
windowSize: {
|
|
2465
|
+
type: Boolean,
|
|
2466
|
+
required: false
|
|
2467
|
+
},
|
|
2468
|
+
fpsLimit: {
|
|
2469
|
+
type: Number,
|
|
2470
|
+
required: false
|
|
2471
|
+
},
|
|
2472
|
+
enableProvideBridge: {
|
|
2473
|
+
type: Boolean,
|
|
2474
|
+
required: false
|
|
2475
|
+
},
|
|
2476
|
+
customRendererOptions: {
|
|
2477
|
+
type: Object,
|
|
2478
|
+
required: false
|
|
2479
|
+
},
|
|
2480
|
+
antialias: {
|
|
2481
|
+
type: Boolean,
|
|
2482
|
+
required: false
|
|
2483
|
+
},
|
|
2484
|
+
stencil: {
|
|
2485
|
+
type: Boolean,
|
|
2486
|
+
required: false
|
|
2487
|
+
},
|
|
2488
|
+
depth: {
|
|
2489
|
+
type: Boolean,
|
|
2490
|
+
required: false
|
|
2491
|
+
},
|
|
2492
|
+
precision: {
|
|
2493
|
+
type: String,
|
|
2494
|
+
required: false
|
|
2495
|
+
},
|
|
2496
|
+
logarithmicDepthBuffer: {
|
|
2497
|
+
type: Boolean,
|
|
2498
|
+
required: false
|
|
2499
|
+
},
|
|
2500
|
+
preserveDrawingBuffer: {
|
|
2501
|
+
type: Boolean,
|
|
2502
|
+
required: false
|
|
2503
|
+
},
|
|
2504
|
+
powerPreference: {
|
|
2505
|
+
type: null,
|
|
2506
|
+
required: false
|
|
2507
|
+
},
|
|
2508
|
+
alpha: {
|
|
2509
|
+
type: Boolean,
|
|
2510
|
+
required: false
|
|
2511
|
+
},
|
|
2512
|
+
premultipliedAlpha: {
|
|
2513
|
+
type: Boolean,
|
|
2514
|
+
required: false
|
|
2515
|
+
},
|
|
2516
|
+
failIfMajorPerformanceCaveat: {
|
|
2517
|
+
type: Boolean,
|
|
2518
|
+
required: false
|
|
2519
|
+
},
|
|
2520
|
+
clearColor: {
|
|
2521
|
+
type: [
|
|
2522
|
+
Object,
|
|
2523
|
+
String,
|
|
2524
|
+
Number
|
|
2525
|
+
],
|
|
2526
|
+
required: false
|
|
2527
|
+
},
|
|
2528
|
+
clearAlpha: {
|
|
2529
|
+
type: Number,
|
|
2530
|
+
required: false
|
|
2531
|
+
},
|
|
2532
|
+
shadows: {
|
|
2533
|
+
type: Boolean,
|
|
2534
|
+
required: false
|
|
2535
|
+
},
|
|
2536
|
+
toneMapping: {
|
|
2537
|
+
type: null,
|
|
2538
|
+
required: false
|
|
2539
|
+
},
|
|
2540
|
+
shadowMapType: {
|
|
2541
|
+
type: null,
|
|
2542
|
+
required: false
|
|
2543
|
+
},
|
|
2544
|
+
useLegacyLights: {
|
|
2545
|
+
type: Boolean,
|
|
2546
|
+
required: false
|
|
2547
|
+
},
|
|
2548
|
+
outputColorSpace: {
|
|
2549
|
+
type: null,
|
|
2550
|
+
required: false
|
|
2551
|
+
},
|
|
2552
|
+
toneMappingExposure: {
|
|
2553
|
+
type: Number,
|
|
2554
|
+
required: false
|
|
2555
|
+
},
|
|
2556
|
+
renderMode: {
|
|
2557
|
+
type: String,
|
|
2558
|
+
required: false
|
|
2559
|
+
},
|
|
2560
|
+
dpr: {
|
|
2561
|
+
type: [Number, Array],
|
|
2562
|
+
required: false
|
|
2563
|
+
},
|
|
2564
|
+
renderer: {
|
|
2565
|
+
type: Function,
|
|
2566
|
+
required: false
|
|
2567
|
+
},
|
|
2568
|
+
canvas: {
|
|
2569
|
+
type: null,
|
|
2570
|
+
required: true
|
|
2571
|
+
}
|
|
2572
|
+
},
|
|
2573
|
+
emits: [
|
|
2574
|
+
"ready",
|
|
2575
|
+
"error",
|
|
2576
|
+
"pointermissed",
|
|
2577
|
+
"render",
|
|
2578
|
+
"beforeLoop",
|
|
2579
|
+
"loop",
|
|
2580
|
+
"click",
|
|
2581
|
+
"contextmenu",
|
|
2582
|
+
"pointermove",
|
|
2583
|
+
"pointerenter",
|
|
2584
|
+
"pointerleave",
|
|
2585
|
+
"pointerover",
|
|
2586
|
+
"pointerout",
|
|
2587
|
+
"dblclick",
|
|
2588
|
+
"pointerdown",
|
|
2589
|
+
"pointerup",
|
|
2590
|
+
"pointercancel",
|
|
2591
|
+
"lostpointercapture",
|
|
2592
|
+
"wheel"
|
|
2593
|
+
],
|
|
2594
|
+
setup(__props, { expose: __expose, emit: __emit }) {
|
|
2595
|
+
const props = __props;
|
|
2596
|
+
const emit = __emit;
|
|
2597
|
+
const child = shallowRef();
|
|
2598
|
+
const { listeners, exposed } = forwardContext(emit, child);
|
|
2599
|
+
__expose(exposed);
|
|
2600
|
+
extendWithThree();
|
|
2601
|
+
const createDefaultRenderer = (ctx) => createWebGLRenderer(ctx, props);
|
|
2602
|
+
return (_ctx, _cache) => {
|
|
2603
|
+
return openBlock(), createBlock(Context_default, mergeProps({
|
|
2604
|
+
ref_key: "child",
|
|
2605
|
+
ref: child
|
|
2606
|
+
}, props, { renderer: props.renderer ?? createDefaultRenderer }, toHandlers(unref(listeners))), {
|
|
2607
|
+
default: withCtx(() => [renderSlot(_ctx.$slots, "default")]),
|
|
2608
|
+
_: 3
|
|
2609
|
+
}, 16, ["renderer"]);
|
|
2610
|
+
};
|
|
2611
|
+
}
|
|
2612
|
+
});
|
|
2613
|
+
//#endregion
|
|
2614
|
+
//#region src/components/TresPortal.vue
|
|
2615
|
+
var TresPortal_default = /* @__PURE__ */ defineComponent({
|
|
2616
|
+
__name: "TresPortal",
|
|
2617
|
+
props: {
|
|
2618
|
+
to: {
|
|
2619
|
+
type: Object,
|
|
2620
|
+
required: true
|
|
2621
|
+
},
|
|
2622
|
+
disabled: {
|
|
2623
|
+
type: Boolean,
|
|
2624
|
+
required: false
|
|
2625
|
+
}
|
|
2626
|
+
},
|
|
2627
|
+
setup(__props) {
|
|
2628
|
+
return (_ctx, _cache) => {
|
|
2629
|
+
return openBlock(), createBlock(Teleport, {
|
|
2630
|
+
to: __props.to,
|
|
2631
|
+
disabled: __props.disabled
|
|
2632
|
+
}, [renderSlot(_ctx.$slots, "default")], 8, ["to", "disabled"]);
|
|
2633
|
+
};
|
|
2634
|
+
}
|
|
2635
|
+
});
|
|
2636
|
+
//#endregion
|
|
2637
|
+
//#region src/composables/useLoader/index.ts
|
|
2638
|
+
/**
|
|
2639
|
+
* Vue composable for loading 3D models using Three.js loaders
|
|
2640
|
+
* @param Loader - The Three.js loader constructor
|
|
2641
|
+
* @param path - The path to the model file
|
|
2642
|
+
* @param options - Optional configuration for the loader
|
|
2643
|
+
* @returns UseAsyncState composable with the loaded model
|
|
2644
|
+
*/
|
|
2645
|
+
function useLoader(Loader, path, options) {
|
|
2646
|
+
const proto = new Loader(options?.manager);
|
|
2647
|
+
const progress = reactive({
|
|
2648
|
+
loaded: 0,
|
|
2649
|
+
total: 0,
|
|
2650
|
+
percentage: 0
|
|
2651
|
+
});
|
|
2652
|
+
if (options?.extensions) options.extensions(proto);
|
|
2653
|
+
const initialPath = toValue(path);
|
|
2654
|
+
const result = useAsyncState((path) => new Promise((resolve, reject) => {
|
|
2655
|
+
const assetPath = path || initialPath || "";
|
|
2656
|
+
proto.load(assetPath, (result) => {
|
|
2657
|
+
resolve(result);
|
|
2658
|
+
}, (event) => {
|
|
2659
|
+
progress.loaded = event.loaded;
|
|
2660
|
+
progress.total = event.total;
|
|
2661
|
+
progress.percentage = progress.loaded / progress.total * 100;
|
|
2662
|
+
}, (err) => {
|
|
2663
|
+
reject(err);
|
|
2664
|
+
});
|
|
2665
|
+
}), options?.initialValue ?? null, {
|
|
2666
|
+
...options?.asyncOptions,
|
|
2667
|
+
immediate: options?.asyncOptions?.immediate ?? true
|
|
2668
|
+
});
|
|
2669
|
+
const unsub = watch(() => toValue(path), (newPath) => {
|
|
2670
|
+
if (newPath) {
|
|
2671
|
+
const value = result.state.value;
|
|
2672
|
+
if (value && typeof value === "object" && "scene" in value && value.scene) disposeObject3D(value.scene);
|
|
2673
|
+
result.execute(0, newPath);
|
|
2674
|
+
}
|
|
2675
|
+
});
|
|
2676
|
+
onUnmounted(() => {
|
|
2677
|
+
unsub();
|
|
2678
|
+
const value = result.state.value;
|
|
2679
|
+
if (value && typeof value === "object" && "scene" in value && value.scene) disposeObject3D(value.scene);
|
|
2680
|
+
});
|
|
2681
|
+
return {
|
|
2682
|
+
...result,
|
|
2683
|
+
load: (path) => {
|
|
2684
|
+
result.execute(0, path);
|
|
2685
|
+
},
|
|
2686
|
+
progress
|
|
2687
|
+
};
|
|
2688
|
+
}
|
|
2689
|
+
//#endregion
|
|
2690
|
+
//#region src/composables/useLoader/component.vue
|
|
2691
|
+
var component_default = /* @__PURE__ */ defineComponent({
|
|
2692
|
+
__name: "component",
|
|
2693
|
+
props: {
|
|
2694
|
+
loader: {
|
|
2695
|
+
type: null,
|
|
2696
|
+
required: true
|
|
2697
|
+
},
|
|
2698
|
+
path: {
|
|
2699
|
+
type: String,
|
|
2700
|
+
required: true
|
|
2701
|
+
},
|
|
2702
|
+
manager: {
|
|
2703
|
+
type: Object,
|
|
2704
|
+
required: false
|
|
2705
|
+
},
|
|
2706
|
+
extensions: {
|
|
2707
|
+
type: Function,
|
|
2708
|
+
required: false
|
|
2709
|
+
}
|
|
2710
|
+
},
|
|
2711
|
+
emits: ["loaded", "error"],
|
|
2712
|
+
setup(__props, { emit: __emit }) {
|
|
2713
|
+
const props = __props;
|
|
2714
|
+
const emit = __emit;
|
|
2715
|
+
const { state, isLoading, error } = useLoader(props.loader, props.path, {
|
|
2716
|
+
manager: props.manager,
|
|
2717
|
+
extensions: props.extensions
|
|
2718
|
+
});
|
|
2719
|
+
whenever(error, (err) => {
|
|
2720
|
+
if (err) emit("error", err);
|
|
2721
|
+
});
|
|
2722
|
+
whenever(state, (value) => {
|
|
2723
|
+
if (value) emit("loaded", value);
|
|
2724
|
+
});
|
|
2725
|
+
return (_ctx, _cache) => {
|
|
2726
|
+
return renderSlot(_ctx.$slots, "default", {
|
|
2727
|
+
state: unref(state),
|
|
2728
|
+
isLoading: unref(isLoading),
|
|
2729
|
+
error: unref(error)
|
|
2730
|
+
});
|
|
2731
|
+
};
|
|
2732
|
+
}
|
|
2733
|
+
});
|
|
2734
|
+
//#endregion
|
|
2735
|
+
//#region src/utils/graph.ts
|
|
2736
|
+
function buildGraph(object) {
|
|
2737
|
+
const data = {
|
|
2738
|
+
nodes: {},
|
|
2739
|
+
materials: {},
|
|
2740
|
+
meshes: {}
|
|
2741
|
+
};
|
|
2742
|
+
if (object) object.traverse((obj) => {
|
|
2743
|
+
if (obj.name) data.nodes[obj.name] = obj;
|
|
2744
|
+
if (isMesh(obj)) {
|
|
2745
|
+
if (!data.meshes[obj.name]) data.meshes[obj.name] = obj;
|
|
2746
|
+
(Array.isArray(obj.material) ? obj.material : [obj.material]).forEach((material) => {
|
|
2747
|
+
if (material.name && !data.materials[material.name]) data.materials[material.name] = material;
|
|
2748
|
+
});
|
|
2749
|
+
}
|
|
2750
|
+
});
|
|
2751
|
+
return data;
|
|
2752
|
+
}
|
|
2753
|
+
//#endregion
|
|
2754
|
+
//#region src/composables/useGraph/index.ts
|
|
2755
|
+
const useGraph = (object) => {
|
|
2756
|
+
return computed(() => {
|
|
2757
|
+
const obj = toValue(object);
|
|
2758
|
+
if (!obj) return;
|
|
2759
|
+
return buildGraph(obj);
|
|
2760
|
+
});
|
|
2761
|
+
};
|
|
2762
|
+
//#endregion
|
|
2763
|
+
//#region src/utils/createPriorityEventHook.ts
|
|
2764
|
+
function createPriorityEventHook() {
|
|
2765
|
+
const eventToPriority = /* @__PURE__ */ new Map();
|
|
2766
|
+
const ascending = /* @__PURE__ */ new Set();
|
|
2767
|
+
let ADD_COUNT = 0;
|
|
2768
|
+
let dirty = false;
|
|
2769
|
+
const sort = () => {
|
|
2770
|
+
const sorted = Array.from(eventToPriority.entries()).sort((a, b) => {
|
|
2771
|
+
const priorityDiff = a[1].priority - b[1].priority;
|
|
2772
|
+
return priorityDiff === 0 ? a[1].addI - b[1].addI : priorityDiff;
|
|
2773
|
+
});
|
|
2774
|
+
ascending.clear();
|
|
2775
|
+
sorted.forEach((entry) => ascending.add(entry[0]));
|
|
2776
|
+
};
|
|
2777
|
+
const off = (fn) => {
|
|
2778
|
+
eventToPriority.delete(fn);
|
|
2779
|
+
ascending.delete(fn);
|
|
2780
|
+
};
|
|
2781
|
+
const on = (fn, priority = 0) => {
|
|
2782
|
+
eventToPriority.set(fn, {
|
|
2783
|
+
priority,
|
|
2784
|
+
addI: ADD_COUNT++
|
|
2785
|
+
});
|
|
2786
|
+
const offFn = () => off(fn);
|
|
2787
|
+
tryOnScopeDispose(offFn);
|
|
2788
|
+
dirty = true;
|
|
2789
|
+
return { off: offFn };
|
|
2790
|
+
};
|
|
2791
|
+
const trigger = (...args) => {
|
|
2792
|
+
if (dirty) {
|
|
2793
|
+
sort();
|
|
2794
|
+
dirty = false;
|
|
2795
|
+
}
|
|
2796
|
+
return Promise.all(Array.from(ascending).map((fn) => fn(...args)));
|
|
2797
|
+
};
|
|
2798
|
+
const dispose = () => {
|
|
2799
|
+
eventToPriority.clear();
|
|
2800
|
+
ascending.clear();
|
|
2801
|
+
};
|
|
2802
|
+
return {
|
|
2803
|
+
on,
|
|
2804
|
+
off,
|
|
2805
|
+
trigger,
|
|
2806
|
+
dispose,
|
|
2807
|
+
get count() {
|
|
2808
|
+
return eventToPriority.size;
|
|
2809
|
+
}
|
|
2810
|
+
};
|
|
2811
|
+
}
|
|
2812
|
+
//#endregion
|
|
2813
|
+
//#region src/composables/useTres/index.ts
|
|
2814
|
+
function useTres() {
|
|
2815
|
+
const { scene, renderer, camera, sizes, controls, extend, events, isWebGPU } = useTresContext();
|
|
2816
|
+
return {
|
|
2817
|
+
scene,
|
|
2818
|
+
renderer: renderer.instance,
|
|
2819
|
+
camera: camera.activeCamera,
|
|
2820
|
+
sizes,
|
|
2821
|
+
controls,
|
|
2822
|
+
extend,
|
|
2823
|
+
events,
|
|
2824
|
+
isWebGPU,
|
|
2825
|
+
invalidate: renderer.invalidate,
|
|
2826
|
+
advance: renderer.advance
|
|
2827
|
+
};
|
|
2828
|
+
}
|
|
2829
|
+
//#endregion
|
|
2830
|
+
//#region src/composables/useLoop/index.ts
|
|
2831
|
+
/**
|
|
2832
|
+
* Composable that provides control over the render loop and animation lifecycle.
|
|
2833
|
+
*/
|
|
2834
|
+
const useLoop = () => {
|
|
2835
|
+
const tresContext = useTres();
|
|
2836
|
+
const { renderer: rendererManager } = useTresContext();
|
|
2837
|
+
const eventHookBeforeRender = createPriorityEventHook();
|
|
2838
|
+
const eventHookAfterRender = createPriorityEventHook();
|
|
2839
|
+
rendererManager.loop.onBeforeLoop((loopContext) => {
|
|
2840
|
+
eventHookBeforeRender.trigger({
|
|
2841
|
+
...tresContext,
|
|
2842
|
+
...loopContext
|
|
2843
|
+
});
|
|
2844
|
+
});
|
|
2845
|
+
rendererManager.loop.onLoop((loopContext) => {
|
|
2846
|
+
eventHookAfterRender.trigger({
|
|
2847
|
+
...tresContext,
|
|
2848
|
+
...loopContext
|
|
2849
|
+
});
|
|
2850
|
+
});
|
|
2851
|
+
const render = rendererManager.replaceRenderFunction;
|
|
2852
|
+
return {
|
|
2853
|
+
stop: rendererManager.loop.stop,
|
|
2854
|
+
start: rendererManager.loop.start,
|
|
2855
|
+
isActive: rendererManager.loop.isActive,
|
|
2856
|
+
onBeforeRender: eventHookBeforeRender.on,
|
|
2857
|
+
onRender: eventHookAfterRender.on,
|
|
2858
|
+
render
|
|
2859
|
+
};
|
|
2860
|
+
};
|
|
2861
|
+
//#endregion
|
|
2862
|
+
//#region src/directives/vDistanceTo.ts
|
|
2863
|
+
let arrowHelper = null;
|
|
2864
|
+
const vDistanceTo = {
|
|
2865
|
+
updated: (el, binding) => {
|
|
2866
|
+
const extractBindingPosition = (binding) => {
|
|
2867
|
+
let observer = binding.value;
|
|
2868
|
+
if (binding.value && isMesh(binding.value)) observer = binding.value.position;
|
|
2869
|
+
if (Array.isArray(binding.value)) observer = new Vector3(...observer);
|
|
2870
|
+
return observer;
|
|
2871
|
+
};
|
|
2872
|
+
const observer = extractBindingPosition(binding);
|
|
2873
|
+
if (!observer) {
|
|
2874
|
+
logWarning(`v-distance-to: problem with binding value: ${binding.value}`);
|
|
2875
|
+
return;
|
|
2876
|
+
}
|
|
2877
|
+
if (arrowHelper) {
|
|
2878
|
+
arrowHelper.dispose();
|
|
2879
|
+
el.parent.remove(arrowHelper);
|
|
2880
|
+
}
|
|
2881
|
+
const dir = observer.clone().sub(el.position);
|
|
2882
|
+
dir.normalize();
|
|
2883
|
+
arrowHelper = new ArrowHelper(dir, el.position, el.position.distanceTo(observer), 16776960);
|
|
2884
|
+
el.parent.add(arrowHelper);
|
|
2885
|
+
console.table([
|
|
2886
|
+
["Distance:", el.position.distanceTo(observer)],
|
|
2887
|
+
[`origin: ${el.name || el.type}`, `x:${el.position.x}, y:${el.position.y}, z:${el.position?.z}`],
|
|
2888
|
+
[`Destiny: ${el.name || el.type}`, `x:${observer.x}, y:${observer.y}, z:${observer?.z}`]
|
|
2889
|
+
]);
|
|
2890
|
+
},
|
|
2891
|
+
unmounted: (el) => {
|
|
2892
|
+
arrowHelper?.dispose();
|
|
2893
|
+
if (el.parent) el.parent.remove(arrowHelper);
|
|
2894
|
+
}
|
|
2895
|
+
};
|
|
2896
|
+
//#endregion
|
|
2897
|
+
//#region src/directives/vLightHelper.ts
|
|
2898
|
+
let CurrentHelper;
|
|
2899
|
+
let currentInstance;
|
|
2900
|
+
const helpers = {
|
|
2901
|
+
DirectionalLight: DirectionalLightHelper,
|
|
2902
|
+
PointLight: PointLightHelper,
|
|
2903
|
+
SpotLight: SpotLightHelper,
|
|
2904
|
+
HemisphereLight: HemisphereLightHelper,
|
|
2905
|
+
RectAreaLight: RectAreaLightHelper
|
|
2906
|
+
};
|
|
2907
|
+
const vLightHelper = {
|
|
2908
|
+
mounted: (el) => {
|
|
2909
|
+
if (!isLight(el)) {
|
|
2910
|
+
logWarning(`${el.type} is not a light`);
|
|
2911
|
+
return;
|
|
2912
|
+
}
|
|
2913
|
+
CurrentHelper = helpers[el.type];
|
|
2914
|
+
el.parent?.add(new CurrentHelper(el, 1, el.color.getHex()));
|
|
2915
|
+
},
|
|
2916
|
+
updated: (el) => {
|
|
2917
|
+
currentInstance = el.parent?.children.find((child) => child instanceof CurrentHelper);
|
|
2918
|
+
if (currentInstance instanceof RectAreaLightHelper) return;
|
|
2919
|
+
currentInstance.update();
|
|
2920
|
+
},
|
|
2921
|
+
unmounted: (el) => {
|
|
2922
|
+
if (!el.isLight) {
|
|
2923
|
+
logWarning(`${el.type} is not a light`);
|
|
2924
|
+
return;
|
|
2925
|
+
}
|
|
2926
|
+
currentInstance = el.parent?.children.find((child) => child instanceof CurrentHelper);
|
|
2927
|
+
if (currentInstance && currentInstance.dispose) currentInstance.dispose();
|
|
2928
|
+
if (el.parent) el.parent.remove(currentInstance);
|
|
2929
|
+
}
|
|
2930
|
+
};
|
|
2931
|
+
//#endregion
|
|
2932
|
+
//#region src/directives/vLog.ts
|
|
2933
|
+
const vLog = { mounted: (el, binding) => {
|
|
2934
|
+
if (binding.arg) {
|
|
2935
|
+
console.log(`v-log:${binding.arg}`, el[binding.arg]);
|
|
2936
|
+
return;
|
|
2937
|
+
}
|
|
2938
|
+
console.log("v-log", el);
|
|
2939
|
+
} };
|
|
2940
|
+
//#endregion
|
|
2941
|
+
//#region src/index.ts
|
|
2942
|
+
const plugin = { install(app) {
|
|
2943
|
+
app.component("TresCanvas", TresCanvas_default);
|
|
2944
|
+
} };
|
|
2945
|
+
//#endregion
|
|
2946
|
+
export { isGroup as $, catalogue as A, isCopyable as B, disposeObject3D as C, useBaseTresContextProvider as D, INJECTION_KEY as E, isProd as F, isVectorLike as G, isTresInstance as H, logError as I, isBufferGeometry as J, isWebGLRenderer as K, logMessage as L, useBaseRendererManager as M, TresRendererError as N, useTresContext as O, createTimer as P, isFog as Q, logWarning as R, DevtoolsMessenger as S, useTresContextProvider as T, isTresObject as U, isTresCamera as V, isTresPrimitive as W, isColor as X, isCamera as Y, isColorRepresentation as Z, BaseCanvas_default as _, useLoop as a, isNodeMaterial as at, Context_default as b, buildGraph as c, isPerspectiveCamera as ct, TresPortal_default as d, isLayers as et, WebGLContext_default as f, TresCanvas_default as g, normalizeVectorFlexibleParam as h, vDistanceTo as i, isNode as it, extend as j, useCameraManager as k, component_default as l, isScene as lt, normalizeColor as m, vLog as n, isMaterial as nt, useTres as o, isObject3D as ot, templateCompilerOptions as p, isWebGPURenderer as q, vLightHelper as r, isMesh as rt, useGraph as s, isOrthographicCamera as st, plugin as t, isLight as tt, useLoader as u, tresCanvasDefaults as v, useRendererManager as w, registerTresDevtools as x, forwardContext as y, isClassInstance as z };
|