@tresjs/core 5.9.3 → 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.
@@ -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 };