@tresjs/core 5.9.3 → 5.10.1

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,2952 @@
1
+ /**
2
+ * name: @tresjs/core
3
+ * version: v5.10.1
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.1";
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
+ queueMicrotask(() => {
864
+ if (ctx.renderer.canBeInvalidated.value) ctx.renderer.invalidate();
865
+ });
866
+ }
867
+ function setPrimitiveObject(newObject, primitive, setTarget, nodeOpsFns, context) {
868
+ const objectsToAttach = [...primitive.__tres.objects];
869
+ const oldObject = unboxTresPrimitive(primitive);
870
+ newObject = unboxTresPrimitive(newObject);
871
+ if (oldObject === newObject) return true;
872
+ const newInstance = prepareTresInstance(newObject, primitive.__tres ?? {}, context);
873
+ const parent = primitive.parent ?? primitive.__tres.parent ?? null;
874
+ const propsToPatch = { ...primitive.__tres.memoizedProps };
875
+ delete propsToPatch.object;
876
+ for (const obj of objectsToAttach) {
877
+ doRemoveDetach(obj, context);
878
+ doRemoveDeregister(obj, context);
879
+ }
880
+ oldObject.__tres.objects = [];
881
+ nodeOpsFns.remove(primitive);
882
+ for (const [key, value] of Object.entries(propsToPatch)) nodeOpsFns.patchProp(newInstance, key, newInstance[key], value);
883
+ setTarget(newObject);
884
+ nodeOpsFns.insert(primitive, parent);
885
+ for (const obj of objectsToAttach) nodeOpsFns.insert(obj, primitive);
886
+ return true;
887
+ }
888
+ function unboxTresPrimitive(maybePrimitive) {
889
+ if (isTresPrimitive(maybePrimitive)) {
890
+ const primitive = maybePrimitive;
891
+ primitive.object.__tres = primitive.__tres;
892
+ return primitive.object;
893
+ } else return maybePrimitive;
894
+ }
895
+ function doRemoveDetach(node, context) {
896
+ const parent = node.__tres?.parent || context.scene.value;
897
+ if (node.__tres) node.__tres.parent = null;
898
+ if (parent && parent.__tres && "objects" in parent.__tres) filterInPlace(parent.__tres.objects, (obj) => obj !== node);
899
+ if (node.__tres?.attach) detach(parent, node, node.__tres.attach);
900
+ else {
901
+ node.parent?.remove?.(unboxTresPrimitive(node));
902
+ node.parent = null;
903
+ }
904
+ }
905
+ function doRemoveDeregister(node, context) {
906
+ node.traverse?.((child) => {
907
+ if (isTresCamera(child)) context.camera.deregisterCamera(child);
908
+ });
909
+ if (isTresCamera(node)) context.camera.deregisterCamera(node);
910
+ invalidateInstance(node);
911
+ }
912
+ //#endregion
913
+ //#region src/utils/primitive/createRetargetingProxy.ts
914
+ function createRetargetingProxy(target, getters = {}, setters = {}) {
915
+ let _target = target;
916
+ const setTarget = (newTarget) => {
917
+ _target = newTarget;
918
+ };
919
+ let proxy = new Proxy({}, {});
920
+ proxy = new Proxy({}, {
921
+ has(_, key) {
922
+ return key in getters || key in _target;
923
+ },
924
+ get(_, prop, __) {
925
+ if (prop in getters) return getters[prop](_target);
926
+ return _target[prop];
927
+ },
928
+ set(_, prop, val) {
929
+ if (setters[prop]) setters[prop](val, _target, proxy, setTarget);
930
+ else _target[prop] = val;
931
+ return true;
932
+ }
933
+ });
934
+ return proxy;
935
+ }
936
+ //#endregion
937
+ //#region src/utils/pointerEvents.ts
938
+ const supportedPointerEvents = [
939
+ "onClick",
940
+ "onContextmenu",
941
+ "onPointermove",
942
+ "onPointerenter",
943
+ "onPointerleave",
944
+ "onPointerover",
945
+ "onPointerout",
946
+ "onDblclick",
947
+ "onPointerdown",
948
+ "onPointerup",
949
+ "onPointercancel",
950
+ "onLostpointercapture",
951
+ "onWheel"
952
+ ];
953
+ const pointerEventsMapVueToThree = {
954
+ onClick: "click",
955
+ onContextmenu: "contextmenu",
956
+ onPointermove: "pointermove",
957
+ onPointerenter: "pointerenter",
958
+ onPointerleave: "pointerleave",
959
+ onPointerover: "pointerover",
960
+ onPointerout: "pointerout",
961
+ onDblclick: "dblclick",
962
+ onPointerdown: "pointerdown",
963
+ onPointerup: "pointerup",
964
+ onPointercancel: "pointercancel",
965
+ onLostpointercapture: "lostpointercapture",
966
+ onWheel: "wheel"
967
+ };
968
+ const isSupportedPointerEvent = (event) => supportedPointerEvents.includes(event);
969
+ //#endregion
970
+ //#region src/core/nodeOps.ts
971
+ const textNodeSymbol = Symbol("tresTextNode");
972
+ const commentNodeSymbol = Symbol("tresComment");
973
+ const nodeOps = ({ context, options = { primitivePrefix: "" } }) => {
974
+ const scene = context.scene.value;
975
+ const createTextNode = () => {
976
+ if (scene?.__tres?.isUnmounting) return;
977
+ return {
978
+ [textNodeSymbol]: true,
979
+ __tres: { parent: null }
980
+ };
981
+ };
982
+ const createCommentNode = () => ({
983
+ [commentNodeSymbol]: true,
984
+ __tres: { parent: null }
985
+ });
986
+ const isTextNode = (node) => !!node && typeof node === "object" && textNodeSymbol in node;
987
+ const isCommentNode = (node) => !!node && typeof node === "object" && commentNodeSymbol in node;
988
+ function createElement(tag, _namespace, _isCustomizedBuiltIn, props) {
989
+ if (!props) props = {};
990
+ if (!props.args) props.args = [];
991
+ if (isHTMLTag(tag)) return null;
992
+ if (tag.includes("-")) tag = tag.replace(/-([a-z])/g, (_, c) => c.toUpperCase()).replace(/^[a-z]/, (c) => c.toUpperCase());
993
+ let name = tag.replace("Tres", "");
994
+ let obj;
995
+ if (tag === `${options?.primitivePrefix ?? ""}primitive`) {
996
+ if (!isObject(props.object) || isRef(props.object)) logError("Tres primitives need an 'object' prop, whose value is an object or shallowRef<object>");
997
+ name = props.object.type;
998
+ const __tres = {};
999
+ obj = createRetargetingProxy(props.object, {
1000
+ object: (t) => t,
1001
+ isPrimitive: () => true,
1002
+ __tres: () => __tres
1003
+ }, {
1004
+ object: (object, _, primitive, setTarget) => {
1005
+ setPrimitiveObject(object, primitive, setTarget, {
1006
+ patchProp,
1007
+ remove,
1008
+ insert
1009
+ }, context);
1010
+ },
1011
+ __tres: (t) => {
1012
+ Object.assign(__tres, t);
1013
+ }
1014
+ });
1015
+ } else {
1016
+ const target = catalogue.value[name];
1017
+ if (!target) logError(`${name} is not defined on the THREE namespace. Use extend to add it to the catalog.`);
1018
+ obj = new target(...props.args);
1019
+ }
1020
+ if (!obj) return null;
1021
+ if (isTresCamera(obj)) {
1022
+ if (!props?.position) obj.position.set(3, 3, 3);
1023
+ if (!props?.lookAt) obj.lookAt(0, 0, 0);
1024
+ }
1025
+ obj = prepareTresInstance(obj, {
1026
+ ...isTresInstance(obj) ? obj.__tres : {},
1027
+ type: name,
1028
+ memoizedProps: props,
1029
+ primitive: tag === "primitive",
1030
+ attach: props.attach
1031
+ }, context);
1032
+ return obj;
1033
+ }
1034
+ function insert(child, parent, anchor) {
1035
+ if (!child) return;
1036
+ parent = parent || scene;
1037
+ if (isTextNode(child) || isCommentNode(child)) {
1038
+ const parentInstance = parent.__tres ? parent : prepareTresInstance(parent, {}, context);
1039
+ child.__tres.parent = parentInstance;
1040
+ if (parentInstance.__tres.objects) {
1041
+ const insertIndex = anchor ? parentInstance.__tres.objects.findIndex((obj) => obj === anchor) : -1;
1042
+ if (insertIndex >= 0) parentInstance.__tres.objects.splice(insertIndex, 0, child);
1043
+ else parentInstance.__tres.objects.push(child);
1044
+ }
1045
+ return;
1046
+ }
1047
+ const childInstance = child.__tres ? child : prepareTresInstance(child, {}, context);
1048
+ const parentInstance = parent.__tres ? parent : prepareTresInstance(parent, {}, context);
1049
+ child = unboxTresPrimitive(childInstance);
1050
+ parent = unboxTresPrimitive(parentInstance);
1051
+ if (isTresCamera(child)) context.camera?.registerCamera(child);
1052
+ if (childInstance.__tres.attach) attach(parentInstance, childInstance, childInstance.__tres.attach);
1053
+ else if (isObject3D(child) && isObject3D(parentInstance)) {
1054
+ parentInstance.add(child);
1055
+ child.dispatchEvent({ type: "added" });
1056
+ }
1057
+ childInstance.__tres.parent = parentInstance;
1058
+ if (parentInstance.__tres.objects && !parentInstance.__tres.objects.includes(childInstance)) {
1059
+ const insertIndex = anchor ? parentInstance.__tres.objects.findIndex((obj) => obj === anchor) : -1;
1060
+ if (~insertIndex) parentInstance.__tres.objects.splice(insertIndex, 0, childInstance);
1061
+ else parentInstance.__tres.objects.push(childInstance);
1062
+ }
1063
+ }
1064
+ /**
1065
+ * @param node – the node root to remove
1066
+ * @param dispose – the disposal type
1067
+ */
1068
+ function remove(node, dispose) {
1069
+ if (!node) return;
1070
+ if (isTextNode(node) || isCommentNode(node)) {
1071
+ const parent = node.__tres.parent;
1072
+ if (parent?.__tres?.objects) filterInPlace(parent.__tres.objects, (obj) => obj !== node);
1073
+ node.__tres.parent = null;
1074
+ return;
1075
+ }
1076
+ dispose = isUndefined(dispose) ? "default" : dispose;
1077
+ const userDispose = node.__tres?.dispose;
1078
+ if (!isUndefined(userDispose)) {
1079
+ if (userDispose === null) dispose = false;
1080
+ else dispose = userDispose;
1081
+ }
1082
+ const isPrimitive = node.__tres?.primitive;
1083
+ const shouldDispose = dispose === "default" ? !isPrimitive : !!dispose;
1084
+ if (node.__tres && "objects" in node.__tres) [...node.__tres.objects].forEach((obj) => remove(obj, dispose));
1085
+ if (shouldDispose) {
1086
+ if (node.children) [...node.children].forEach((child) => remove(child, dispose));
1087
+ }
1088
+ doRemoveDetach(node, context);
1089
+ doRemoveDeregister(node, context);
1090
+ if (shouldDispose && !isScene(node)) {
1091
+ if (isFunction(dispose)) dispose(node);
1092
+ else if (isFunction(node.dispose)) try {
1093
+ node.dispose();
1094
+ } catch (e) {}
1095
+ }
1096
+ if ("__tres" in node) delete node.__tres;
1097
+ }
1098
+ function markNodeMaterialForRecompile(target, prevValue, nextValue) {
1099
+ if (isNodeMaterial(target) && (isNode(prevValue) || isNode(nextValue))) target.needsUpdate = true;
1100
+ }
1101
+ function patchProp(node, prop, prevValue, nextValue) {
1102
+ if (!node) return;
1103
+ let root = node;
1104
+ const key = prop;
1105
+ if (node.__tres) node.__tres.memoizedProps[prop] = nextValue;
1106
+ if (prop === "attach") {
1107
+ const maybeParent = node.__tres?.parent || node.parent;
1108
+ remove(node);
1109
+ prepareTresInstance(node, { attach: nextValue }, context);
1110
+ if (maybeParent) insert(node, maybeParent);
1111
+ return;
1112
+ }
1113
+ if (prop === "dispose") {
1114
+ if (!node.__tres) node = prepareTresInstance(node, {}, context);
1115
+ node.__tres.dispose = nextValue;
1116
+ return;
1117
+ }
1118
+ if (isSupportedPointerEvent(prop) && isFunction(nextValue)) node.addEventListener(pointerEventsMapVueToThree[prop], nextValue);
1119
+ let finalKey = camel(key);
1120
+ let target = root?.[finalKey];
1121
+ if (key === "args") {
1122
+ const prevNode = node;
1123
+ const prevArgs = prevValue ?? [];
1124
+ const args = nextValue ?? [];
1125
+ const instanceName = node.__tres?.type || node.type;
1126
+ if (instanceName && prevArgs.length && !isEqual(prevArgs, args)) {
1127
+ const newInstance = new catalogue.value[instanceName](...nextValue);
1128
+ const descriptors = Object.getOwnPropertyDescriptors(newInstance);
1129
+ Object.entries(descriptors).forEach(([key, descriptor]) => {
1130
+ if (!descriptor.writable && !descriptor.set) return;
1131
+ if (key in prevNode) try {
1132
+ prevNode[key] = newInstance[key];
1133
+ } catch (e) {
1134
+ console.warn(`Could not set property ${key} on ${instanceName}:`, e);
1135
+ }
1136
+ });
1137
+ root = prevNode;
1138
+ }
1139
+ return;
1140
+ }
1141
+ if (root.type === "BufferGeometry") {
1142
+ if (key === "args") return;
1143
+ root.setAttribute(camel(key), new BufferAttribute(...nextValue));
1144
+ return;
1145
+ }
1146
+ if (key.includes("-") && target === void 0) {
1147
+ const resolved = resolve(root, key);
1148
+ target = resolved.target;
1149
+ root = resolved.target;
1150
+ finalKey = resolved.key;
1151
+ if (target && finalKey) {
1152
+ target[finalKey] = nextValue;
1153
+ markNodeMaterialForRecompile(target, prevValue, nextValue);
1154
+ if (isTresCamera(node)) node.updateProjectionMatrix();
1155
+ invalidateInstance(node);
1156
+ return;
1157
+ }
1158
+ }
1159
+ let value = nextValue;
1160
+ if (value === "") value = true;
1161
+ if (isFunction(target)) {
1162
+ if (!isSupportedPointerEvent(prop)) {
1163
+ if (Array.isArray(value)) node[finalKey](...value);
1164
+ else node[finalKey](value);
1165
+ }
1166
+ if (finalKey.startsWith("on") && isFunction(value)) root[finalKey] = value;
1167
+ return;
1168
+ }
1169
+ if (isLayers(target) && isLayers(value)) target.mask = value.mask;
1170
+ else if (isColor(target) && isColorRepresentation(value)) target.set(value);
1171
+ else if (isCopyable(target) && isClassInstance(value) && target.constructor === value.constructor) target.copy(value);
1172
+ else if (isVectorLike(target) && Array.isArray(value)) {
1173
+ if ("fromArray" in target && typeof target.fromArray === "function") target.fromArray(value);
1174
+ else target.set(...value);
1175
+ } else if (isVectorLike(target) && typeof value === "number") {
1176
+ if ("setScalar" in target && typeof target.setScalar === "function") target.setScalar(value);
1177
+ else target.set(value);
1178
+ } else root[finalKey] = value;
1179
+ markNodeMaterialForRecompile(root, prevValue, value);
1180
+ if (isTresCamera(node)) node.updateProjectionMatrix();
1181
+ invalidateInstance(node);
1182
+ }
1183
+ function parentNode(node) {
1184
+ return node?.__tres?.parent || null;
1185
+ }
1186
+ function nextSibling(node) {
1187
+ if (!node || !("__tres" in node)) return null;
1188
+ const siblings = parentNode(node)?.__tres?.objects || [];
1189
+ const index = siblings.findIndex((sibling) => sibling === node);
1190
+ if (index < 0 || index >= siblings.length - 1) return null;
1191
+ return siblings[index + 1];
1192
+ }
1193
+ const noop = () => {};
1194
+ return {
1195
+ insert,
1196
+ remove,
1197
+ createElement,
1198
+ patchProp,
1199
+ parentNode,
1200
+ createText: createTextNode,
1201
+ createComment: createCommentNode,
1202
+ setText: noop,
1203
+ setElementText: noop,
1204
+ nextSibling,
1205
+ querySelector: noop,
1206
+ setScopeId: noop,
1207
+ cloneNode: noop,
1208
+ insertStaticContent: noop
1209
+ };
1210
+ };
1211
+ //#endregion
1212
+ //#region src/core/roots.ts
1213
+ const _roots = /* @__PURE__ */ new Map();
1214
+ const getRoot = (canvas) => _roots.get(canvas);
1215
+ const setRoot = (canvas, root) => {
1216
+ _roots.set(canvas, root);
1217
+ };
1218
+ const deleteRoot = (canvas) => _roots.delete(canvas);
1219
+ //#endregion
1220
+ //#region src/devtools/DevtoolsMessenger.ts
1221
+ const QUEUEABLE_MESSAGE_TYPES = ["asset-load"];
1222
+ /**
1223
+ * Messenger class for communicating with Tres DevTools
1224
+ * This class will be attached to window.__TRES__DEVTOOLS__
1225
+ */
1226
+ var DevtoolsMessenger = class {
1227
+ subscribers = /* @__PURE__ */ new Set();
1228
+ messageQueue = [];
1229
+ maxQueueSize = 100;
1230
+ /**
1231
+ * Send a message to devtools subscribers
1232
+ * If no subscribers are available, only queueable message types are queued
1233
+ */
1234
+ send(type, data) {
1235
+ const message = {
1236
+ type,
1237
+ data,
1238
+ timestamp: Date.now()
1239
+ };
1240
+ if (this.subscribers.size > 0) this.subscribers.forEach((subscriber) => subscriber(message));
1241
+ else if (QUEUEABLE_MESSAGE_TYPES.includes(type)) this.queueMessage(message);
1242
+ }
1243
+ /**
1244
+ * Queue a message for later delivery
1245
+ */
1246
+ queueMessage(message) {
1247
+ this.messageQueue.push(message);
1248
+ if (this.messageQueue.length > this.maxQueueSize) this.messageQueue.shift();
1249
+ }
1250
+ /**
1251
+ * Flush all queued messages to current subscribers
1252
+ */
1253
+ flushQueue() {
1254
+ if (this.messageQueue.length === 0 || this.subscribers.size === 0) return;
1255
+ this.messageQueue.forEach((message) => {
1256
+ this.subscribers.forEach((subscriber) => subscriber(message));
1257
+ });
1258
+ this.messageQueue = [];
1259
+ }
1260
+ /**
1261
+ * Subscribe to devtools messages
1262
+ * When a new subscriber is added, all queued messages (asset-load events) are immediately delivered
1263
+ */
1264
+ subscribe(subscriber) {
1265
+ this.subscribers.add(subscriber);
1266
+ this.flushQueue();
1267
+ return () => {
1268
+ this.subscribers.delete(subscriber);
1269
+ };
1270
+ }
1271
+ /**
1272
+ * Check if there are any subscribers
1273
+ */
1274
+ get hasSubscribers() {
1275
+ return this.subscribers.size > 0;
1276
+ }
1277
+ /**
1278
+ * Get the current queue size
1279
+ */
1280
+ get queueSize() {
1281
+ return this.messageQueue.length;
1282
+ }
1283
+ /**
1284
+ * Clear all queued messages
1285
+ */
1286
+ clearQueue() {
1287
+ this.messageQueue = [];
1288
+ }
1289
+ };
1290
+ //#endregion
1291
+ //#region src/devtools/utils.ts
1292
+ /**
1293
+ * Shows a toast or console.log
1294
+ *
1295
+ * @param message - message to log
1296
+ * @param type - different color of the tooltip
1297
+ */
1298
+ function toastMessage(message, type) {
1299
+ const tresMessage = `▲ ■ ●${message}`;
1300
+ if (typeof __VUE_DEVTOOLS_TOAST__ === "function") __VUE_DEVTOOLS_TOAST__(tresMessage, type);
1301
+ else if (type === "error") console.error(tresMessage);
1302
+ else if (type === "warn") console.warn(tresMessage);
1303
+ else console.log(tresMessage);
1304
+ }
1305
+ function __VUE_DEVTOOLS_TOAST__(tresMessage, type) {
1306
+ throw new Error(tresMessage + type);
1307
+ }
1308
+ //#endregion
1309
+ //#region src/utils/perf.ts
1310
+ function calculateMemoryUsage(object) {
1311
+ let totalMemory = 0;
1312
+ object.traverse((node) => {
1313
+ if (isMesh(node) && node.type !== "HightlightMesh") {
1314
+ const geometry = node.geometry;
1315
+ const verticesMemory = geometry.attributes.position.count * 3 * Float32Array.BYTES_PER_ELEMENT;
1316
+ const facesMemory = geometry.index ? geometry.index.count * Uint32Array.BYTES_PER_ELEMENT : 0;
1317
+ const normalsMemory = geometry.attributes.normal ? geometry.attributes.normal.count * 3 * Float32Array.BYTES_PER_ELEMENT : 0;
1318
+ const uvsMemory = geometry.attributes.uv ? geometry.attributes.uv.count * 2 * Float32Array.BYTES_PER_ELEMENT : 0;
1319
+ const geometryMemory = verticesMemory + facesMemory + normalsMemory + uvsMemory;
1320
+ totalMemory += geometryMemory;
1321
+ }
1322
+ });
1323
+ return totalMemory;
1324
+ }
1325
+ function boundedPush(arr, value, max) {
1326
+ arr.push(value);
1327
+ if (arr.length > max) arr.shift();
1328
+ }
1329
+ function bytesToKB(bytes) {
1330
+ return (bytes / 1024).toFixed(2);
1331
+ }
1332
+ //#endregion
1333
+ //#region src/devtools/setupDevtools.ts
1334
+ function setupTresDevtools(ctx) {
1335
+ if (!ctx) return;
1336
+ if (typeof window !== "undefined" && !window.__TRES__DEVTOOLS__) window.__TRES__DEVTOOLS__ = new DevtoolsMessenger();
1337
+ const performanceState = {
1338
+ maxFrames: 160,
1339
+ fps: {
1340
+ value: 0,
1341
+ accumulator: []
1342
+ },
1343
+ memory: {
1344
+ currentMem: 0,
1345
+ allocatedMem: 0,
1346
+ accumulator: []
1347
+ }
1348
+ };
1349
+ const updateInterval = 100;
1350
+ const fps = useFps({ every: updateInterval });
1351
+ const { isSupported, memory } = useMemory({ scheduler: (cb) => useIntervalFn(cb, updateInterval) });
1352
+ const maxFrames = 160;
1353
+ let lastUpdateTime = performance.now();
1354
+ let accumulatedTime = 0;
1355
+ const interval = 1;
1356
+ const updatePerformanceData = ({ timestamp }) => {
1357
+ if (ctx.scene.value) performanceState.memory.allocatedMem = calculateMemoryUsage(ctx.scene.value);
1358
+ if (timestamp - lastUpdateTime >= updateInterval) {
1359
+ lastUpdateTime = timestamp;
1360
+ boundedPush(performanceState.fps.accumulator, fps.value, maxFrames);
1361
+ performanceState.fps.value = fps.value;
1362
+ if (isSupported.value && memory.value?.usedJSHeapSize) {
1363
+ boundedPush(performanceState.memory.accumulator, memory.value.usedJSHeapSize / 1024 / 1024, maxFrames);
1364
+ if (performanceState.memory.accumulator.length > 0) performanceState.memory.currentMem = performanceState.memory.accumulator.reduce((a, b) => a + b, 0) / performanceState.memory.accumulator.length;
1365
+ }
1366
+ }
1367
+ };
1368
+ const { pause } = useRafFn(({ delta }) => {
1369
+ if (!window.__TRES__DEVTOOLS__) return;
1370
+ updatePerformanceData({ timestamp: performance.now() });
1371
+ accumulatedTime += delta;
1372
+ if (accumulatedTime >= interval) {
1373
+ window.__TRES__DEVTOOLS__.send("context", ctx);
1374
+ window.__TRES__DEVTOOLS__.send("performance", performanceState);
1375
+ accumulatedTime = 0;
1376
+ }
1377
+ }, { immediate: true });
1378
+ onUnmounted(() => {
1379
+ pause();
1380
+ });
1381
+ }
1382
+ //#endregion
1383
+ //#region src/utils/three.ts
1384
+ /**
1385
+ * Recursively searches for an Object3D with the specified UUID within a Three.js scene graph.
1386
+ *
1387
+ * @param node - The root Object3D to start searching from
1388
+ * @param uuid - The unique identifier of the object to find
1389
+ * @returns The Object3D with the matching UUID, or undefined if not found
1390
+ */
1391
+ const getObjectByUuid = (node, uuid) => {
1392
+ if (node.uuid === uuid) return node;
1393
+ for (const child of node.children) {
1394
+ const found = getObjectByUuid(child, uuid);
1395
+ if (found) return found;
1396
+ }
1397
+ };
1398
+ //#endregion
1399
+ //#region src/devtools/highlight.ts
1400
+ var HightlightMesh = class extends THREE.Mesh {
1401
+ type = "HightlightMesh";
1402
+ createTime;
1403
+ constructor(...args) {
1404
+ super(...args);
1405
+ this.createTime = Date.now();
1406
+ }
1407
+ onBeforeRender() {
1408
+ const time = (Date.now() - this.createTime) / 1e3;
1409
+ const scaleFactor = 1 + .07 * Math.sin(2.5 * time);
1410
+ this.scale.set(scaleFactor, scaleFactor, scaleFactor);
1411
+ }
1412
+ };
1413
+ //#endregion
1414
+ //#region src/devtools/inspectorHandlers.ts
1415
+ /**
1416
+ * Creates a node representation of a Three.js object for the inspector tree
1417
+ * @param object - The Three.js object to create a node for
1418
+ * @returns A SceneGraphObject representing the Three.js object with relevant metadata
1419
+ */
1420
+ const createNode = (object) => {
1421
+ const node = {
1422
+ id: `scene-${object.uuid}`,
1423
+ label: object.type,
1424
+ children: [],
1425
+ tags: []
1426
+ };
1427
+ if (object.name !== "") node.tags.push({
1428
+ label: object.name,
1429
+ textColor: 5750629,
1430
+ backgroundColor: 15793395
1431
+ });
1432
+ const memory = calculateMemoryUsage(object);
1433
+ if (memory > 0) node.tags.push({
1434
+ label: `${bytesToKB(memory)} KB`,
1435
+ textColor: 15707189,
1436
+ backgroundColor: 16775644,
1437
+ tooltip: "Memory usage"
1438
+ });
1439
+ if (object.type.includes("Light")) {
1440
+ if (isLight(object)) node.tags.push({
1441
+ label: `${object.intensity}`,
1442
+ textColor: 9738662,
1443
+ backgroundColor: 16316922,
1444
+ tooltip: "Intensity"
1445
+ });
1446
+ node.tags.push({
1447
+ label: `#${new Color(object.color).getHexString()}`,
1448
+ textColor: 9738662,
1449
+ backgroundColor: 16316922,
1450
+ tooltip: "Color"
1451
+ });
1452
+ }
1453
+ if (object.type.includes("Camera")) {
1454
+ node.tags.push({
1455
+ label: `${object.fov}°`,
1456
+ textColor: 9738662,
1457
+ backgroundColor: 16316922,
1458
+ tooltip: "Field of view"
1459
+ });
1460
+ node.tags.push({
1461
+ label: `x: ${Math.round(object.position.x)} y: ${Math.round(object.position.y)} z: ${Math.round(object.position.z)}`,
1462
+ textColor: 9738662,
1463
+ backgroundColor: 16316922,
1464
+ tooltip: "Position"
1465
+ });
1466
+ }
1467
+ return node;
1468
+ };
1469
+ /**
1470
+ * Creates a context node for the inspector tree
1471
+ * @param key - The key identifier for the context node
1472
+ * @param uuid - The unique identifier for the context
1473
+ * @param parentKey - Optional parent key for nested context nodes
1474
+ * @returns A SceneGraphObject representing the context node
1475
+ */
1476
+ function createContextNode(key, uuid, parentKey = "") {
1477
+ return {
1478
+ id: `context-${uuid}-${parentKey ? `${parentKey}.${key}` : key}`,
1479
+ label: key,
1480
+ children: [],
1481
+ tags: []
1482
+ };
1483
+ }
1484
+ /**
1485
+ * Recursively builds a graph representation of Three.js objects for the inspector
1486
+ * @param object - The root Three.js object to build the graph from
1487
+ * @param node - The current node in the graph being built
1488
+ * @param filter - Optional filter string to filter objects by type or name
1489
+ */
1490
+ function buildGraph$1(object, node, filter = "") {
1491
+ object.children.forEach((child) => {
1492
+ if (child.type === "HightlightMesh") return;
1493
+ if (filter && !child.type.includes(filter) && !child.name.includes(filter)) return;
1494
+ const childNode = createNode(child);
1495
+ node.children.push(childNode);
1496
+ buildGraph$1(child, childNode, filter);
1497
+ });
1498
+ }
1499
+ /**
1500
+ * Recursively builds a graph representation of context objects for the inspector
1501
+ * @param object - The root object to build the context graph from
1502
+ * @param node - The current node in the graph being built
1503
+ * @param visited - WeakSet to track visited objects and prevent circular references
1504
+ * @param depth - Current depth in the object tree
1505
+ * @param maxDepth - Maximum depth to traverse
1506
+ * @param contextUuid - Optional UUID for the context
1507
+ * @param parentKey - Optional parent key for nested objects
1508
+ */
1509
+ function buildContextGraph(object, node, visited = /* @__PURE__ */ new WeakSet(), depth = 0, maxDepth = 4, contextUuid, parentKey = "") {
1510
+ if (depth >= maxDepth || !object || visited.has(object)) return;
1511
+ const uuid = depth === 0 ? object?.scene?.value?.uuid || Math.random().toString(36).slice(2, 11) : contextUuid;
1512
+ visited.add(object);
1513
+ Object.entries(object).forEach(([key, value]) => {
1514
+ if (key.startsWith("_") || typeof value === "function") return;
1515
+ const chainedKey = parentKey ? `${parentKey}.${key}` : key;
1516
+ const childNode = createContextNode(key, uuid, parentKey);
1517
+ if (key === "scene") return;
1518
+ if (isRef(value)) {
1519
+ childNode.tags.push({
1520
+ label: `Ref<${typeof value.value}>`,
1521
+ textColor: 4372611,
1522
+ backgroundColor: 15793395
1523
+ });
1524
+ if (value.value && typeof value.value === "object") buildContextGraph(value.value, childNode, visited, depth + 1, maxDepth, uuid, chainedKey);
1525
+ else childNode.label = `${key}: ${JSON.stringify(value.value)}`;
1526
+ } else if (value && typeof value === "object" && !Array.isArray(value)) {
1527
+ if (Object.keys(value).length > 0) {
1528
+ if (visited.has(value)) childNode.tags.push({
1529
+ label: "Circular",
1530
+ textColor: 16711680,
1531
+ backgroundColor: 16773360
1532
+ });
1533
+ else buildContextGraph(value, childNode, visited, depth + 1, maxDepth, uuid, chainedKey);
1534
+ } else childNode.label = `${key}: {}`;
1535
+ } else if (Array.isArray(value)) {
1536
+ childNode.label = `${key}: Array(${value.length})`;
1537
+ childNode.tags.push({
1538
+ label: `length: ${value.length}`,
1539
+ textColor: 9738662,
1540
+ backgroundColor: 16316922
1541
+ });
1542
+ } else childNode.label = `${key}: ${JSON.stringify(value)}`;
1543
+ node.children.push(childNode);
1544
+ });
1545
+ }
1546
+ /**
1547
+ * Handler for inspector tree updates
1548
+ * @param tres - The TresContext instance
1549
+ * @returns A function that handles inspector tree payload updates
1550
+ */
1551
+ const inspectorTreeHandler = (tres) => (payload) => {
1552
+ if (payload.inspectorId === "tres:inspector") {
1553
+ const root = createNode(tres.scene.value);
1554
+ buildGraph$1(tres.scene.value, root, payload.filter);
1555
+ const rootContext = {
1556
+ id: "context-root",
1557
+ label: "Context",
1558
+ children: [],
1559
+ tags: []
1560
+ };
1561
+ buildContextGraph(tres, rootContext);
1562
+ payload.rootNodes = [root, rootContext];
1563
+ }
1564
+ };
1565
+ /**
1566
+ * Handler for inspector state updates
1567
+ * @param tres - The TresContext instance
1568
+ * @param options - Options for the handler
1569
+ * @param options.highlightMesh - The currently highlighted mesh
1570
+ * @param options.prevInstance - The previously selected instance
1571
+ * @returns A function that handles inspector state payload updates
1572
+ */
1573
+ const inspectorStateHandler = (tres, { highlightMesh, prevInstance }) => (payload) => {
1574
+ if (payload.inspectorId !== "tres:inspector") return;
1575
+ const highlightMaterial = new MeshBasicMaterial({
1576
+ color: 11003607,
1577
+ transparent: true,
1578
+ opacity: .2,
1579
+ depthTest: false,
1580
+ side: DoubleSide
1581
+ });
1582
+ if (payload.nodeId.includes("scene")) {
1583
+ const match = payload.nodeId.match(/^scene-(.+)$/);
1584
+ const uuid = match ? match[1] : null;
1585
+ if (!uuid) return;
1586
+ const [instance] = tres.scene.value.getObjectsByProperty("uuid", uuid);
1587
+ if (!instance) return;
1588
+ if (prevInstance && highlightMesh && highlightMesh.parent) prevInstance.remove(highlightMesh);
1589
+ if (isMesh(instance)) {
1590
+ const newHighlightMesh = new HightlightMesh(instance.geometry.clone(), highlightMaterial);
1591
+ instance.add(newHighlightMesh);
1592
+ highlightMesh = newHighlightMesh;
1593
+ prevInstance = instance;
1594
+ }
1595
+ payload.state = { object: Object.entries(instance).map(([key, value]) => {
1596
+ if (key === "children") return {
1597
+ key,
1598
+ value: value.filter((child) => child.type !== "HightlightMesh")
1599
+ };
1600
+ return {
1601
+ key,
1602
+ value,
1603
+ editable: true
1604
+ };
1605
+ }).filter(({ key }) => {
1606
+ return key !== "parent";
1607
+ }) };
1608
+ if (isScene(instance)) {
1609
+ const sceneState = {
1610
+ ...payload.state,
1611
+ state: [{
1612
+ key: "Scene Info",
1613
+ value: {
1614
+ objects: instance.children.length,
1615
+ memory: calculateMemoryUsage(instance),
1616
+ calls: tres.renderer.instance.info.render.calls,
1617
+ triangles: tres.renderer.instance.info.render.triangles,
1618
+ points: tres.renderer.instance.info.render.points,
1619
+ lines: tres.renderer.instance.info.render.lines
1620
+ }
1621
+ }]
1622
+ };
1623
+ if ("programs" in tres.renderer.instance.info) sceneState.state.push({
1624
+ key: "Programs",
1625
+ value: tres.renderer.instance.info.programs?.map((program) => ({
1626
+ ...program,
1627
+ programName: program.name
1628
+ }))
1629
+ });
1630
+ payload.state = sceneState;
1631
+ }
1632
+ } else if (payload.nodeId.includes("context")) {
1633
+ const match = payload.nodeId.match(/^context-([^-]+(?:-[^-]+)*)-(.+)$/);
1634
+ const chainedKey = match ? match[2] : "context";
1635
+ if (!chainedKey || chainedKey === "context") {
1636
+ payload.state = { object: Object.entries(tres).filter(([key]) => !key.startsWith("_") && key !== "parent").map(([key, value]) => ({
1637
+ key,
1638
+ value: isRef(value) ? value.value : value,
1639
+ editable: false
1640
+ })) };
1641
+ return;
1642
+ }
1643
+ const parts = chainedKey.split(".");
1644
+ let value = tres;
1645
+ for (const part of parts) {
1646
+ if (!value || typeof value !== "object") break;
1647
+ value = isRef(value[part]) ? value[part].value : value[part];
1648
+ }
1649
+ if (value !== void 0) payload.state = { object: Object.entries(value).filter(([key]) => !key.startsWith("_") && key !== "parent").map(([key, val]) => {
1650
+ if (isRef(val)) return {
1651
+ key,
1652
+ value: val.value,
1653
+ editable: false
1654
+ };
1655
+ if (typeof val === "function") return {
1656
+ key,
1657
+ value: "ƒ()",
1658
+ editable: false
1659
+ };
1660
+ if (val && typeof val === "object") return {
1661
+ key,
1662
+ value: Array.isArray(val) ? `Array(${val.length})` : "Object",
1663
+ editable: false
1664
+ };
1665
+ return {
1666
+ key,
1667
+ value: val,
1668
+ editable: false
1669
+ };
1670
+ }) };
1671
+ }
1672
+ };
1673
+ const editSceneObject = (scene, objectUuid, propertyPath, value) => {
1674
+ const targetObject = getObjectByUuid(scene, objectUuid);
1675
+ if (!targetObject) {
1676
+ console.warn("Object with UUID not found in the scene.");
1677
+ return;
1678
+ }
1679
+ let currentProperty = targetObject;
1680
+ for (let i = 0; i < propertyPath.length - 1; i++) if (currentProperty[propertyPath[i]] !== void 0) currentProperty = currentProperty[propertyPath[i]];
1681
+ else {
1682
+ console.warn(`Property path is not valid: ${propertyPath.join(".")}`);
1683
+ return;
1684
+ }
1685
+ const lastProperty = propertyPath[propertyPath.length - 1];
1686
+ if (currentProperty[lastProperty] !== void 0) currentProperty[lastProperty] = value;
1687
+ else console.warn(`Property path is not valid: ${propertyPath.join(".")}`);
1688
+ };
1689
+ /**
1690
+ * Handler for inspector state edits
1691
+ * @param tres - The TresContext instance
1692
+ * @returns A function that handles inspector state edit payload updates
1693
+ */
1694
+ const inspectorEditStateHandler = (tres) => (payload) => {
1695
+ if (payload.inspectorId === "tres:inspector") {
1696
+ if (payload.nodeId.includes("scene")) {
1697
+ const match = payload.nodeId.match(/^scene-(.+)$/);
1698
+ const uuid = match ? match[1] : null;
1699
+ if (!uuid) return;
1700
+ editSceneObject(tres.scene.value, uuid, payload.path, payload.state.value);
1701
+ }
1702
+ }
1703
+ };
1704
+ //#endregion
1705
+ //#region src/devtools/plugin.ts
1706
+ const INSPECTOR_ID = "tres:inspector";
1707
+ function registerTresDevtools(app, tres) {
1708
+ const pluginDescriptor = {
1709
+ id: "dev.esm.tres",
1710
+ label: "TresJS 🪐",
1711
+ logo: "https://raw.githubusercontent.com/Tresjs/tres/main/public/favicon.svg",
1712
+ packageName: "tresjs",
1713
+ homepage: "https://docs.tresjs.org",
1714
+ app
1715
+ };
1716
+ const highlightMesh = null;
1717
+ const prevInstance = null;
1718
+ setupTresDevtools(tres);
1719
+ setupDevtoolsPlugin(pluginDescriptor, (api) => {
1720
+ 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.");
1721
+ api.addInspector({
1722
+ id: INSPECTOR_ID,
1723
+ label: "TresJS 🪐",
1724
+ icon: "account_tree",
1725
+ treeFilterPlaceholder: "Search instances"
1726
+ });
1727
+ setInterval(() => {
1728
+ api.sendInspectorTree(INSPECTOR_ID);
1729
+ }, 1e3);
1730
+ setInterval(() => {
1731
+ api.notifyComponentUpdate();
1732
+ }, 5e3);
1733
+ api.on.getInspectorTree(inspectorTreeHandler(tres));
1734
+ api.on.getInspectorState(inspectorStateHandler(tres, {
1735
+ highlightMesh,
1736
+ prevInstance
1737
+ }));
1738
+ api.on.editInspectorState(inspectorEditStateHandler(tres));
1739
+ });
1740
+ }
1741
+ //#endregion
1742
+ //#region src/components/Context.vue
1743
+ var Context_default = /* @__PURE__ */ defineComponent({
1744
+ __name: "Context",
1745
+ props: {
1746
+ camera: {
1747
+ type: null,
1748
+ required: false
1749
+ },
1750
+ windowSize: {
1751
+ type: Boolean,
1752
+ required: false
1753
+ },
1754
+ fpsLimit: {
1755
+ type: Number,
1756
+ required: false
1757
+ },
1758
+ enableProvideBridge: {
1759
+ type: Boolean,
1760
+ required: false
1761
+ },
1762
+ customRendererOptions: {
1763
+ type: Object,
1764
+ required: false
1765
+ },
1766
+ antialias: {
1767
+ type: Boolean,
1768
+ required: false
1769
+ },
1770
+ stencil: {
1771
+ type: Boolean,
1772
+ required: false
1773
+ },
1774
+ depth: {
1775
+ type: Boolean,
1776
+ required: false
1777
+ },
1778
+ precision: {
1779
+ type: String,
1780
+ required: false
1781
+ },
1782
+ logarithmicDepthBuffer: {
1783
+ type: Boolean,
1784
+ required: false
1785
+ },
1786
+ preserveDrawingBuffer: {
1787
+ type: Boolean,
1788
+ required: false
1789
+ },
1790
+ powerPreference: {
1791
+ type: null,
1792
+ required: false
1793
+ },
1794
+ alpha: {
1795
+ type: Boolean,
1796
+ required: false
1797
+ },
1798
+ premultipliedAlpha: {
1799
+ type: Boolean,
1800
+ required: false
1801
+ },
1802
+ failIfMajorPerformanceCaveat: {
1803
+ type: Boolean,
1804
+ required: false
1805
+ },
1806
+ clearColor: {
1807
+ type: [
1808
+ Object,
1809
+ String,
1810
+ Number
1811
+ ],
1812
+ required: false
1813
+ },
1814
+ clearAlpha: {
1815
+ type: Number,
1816
+ required: false
1817
+ },
1818
+ shadows: {
1819
+ type: Boolean,
1820
+ required: false
1821
+ },
1822
+ toneMapping: {
1823
+ type: null,
1824
+ required: false
1825
+ },
1826
+ shadowMapType: {
1827
+ type: null,
1828
+ required: false
1829
+ },
1830
+ useLegacyLights: {
1831
+ type: Boolean,
1832
+ required: false
1833
+ },
1834
+ outputColorSpace: {
1835
+ type: null,
1836
+ required: false
1837
+ },
1838
+ toneMappingExposure: {
1839
+ type: Number,
1840
+ required: false
1841
+ },
1842
+ renderMode: {
1843
+ type: String,
1844
+ required: false
1845
+ },
1846
+ dpr: {
1847
+ type: [Number, Array],
1848
+ required: false
1849
+ },
1850
+ renderer: {
1851
+ type: Function,
1852
+ required: false
1853
+ },
1854
+ canvas: {
1855
+ type: null,
1856
+ required: true
1857
+ }
1858
+ },
1859
+ emits: [
1860
+ "ready",
1861
+ "error",
1862
+ "pointermissed",
1863
+ "render",
1864
+ "beforeLoop",
1865
+ "loop",
1866
+ "click",
1867
+ "contextmenu",
1868
+ "pointermove",
1869
+ "pointerenter",
1870
+ "pointerleave",
1871
+ "pointerover",
1872
+ "pointerout",
1873
+ "dblclick",
1874
+ "pointerdown",
1875
+ "pointerup",
1876
+ "pointercancel",
1877
+ "lostpointercapture",
1878
+ "wheel"
1879
+ ],
1880
+ setup(__props, { expose: __expose, emit: __emit }) {
1881
+ const props = __props;
1882
+ const emit = __emit;
1883
+ const slots = useSlots();
1884
+ const scene = shallowRef(new Scene());
1885
+ const instance = getCurrentInstance();
1886
+ const createInternalComponent = (context, hmrTick) => defineComponent({ setup() {
1887
+ const ctx = getCurrentInstance()?.appContext;
1888
+ if (ctx) ctx.app = instance?.appContext.app;
1889
+ const provides = {};
1890
+ function mergeProvides(currentInstance) {
1891
+ if (!currentInstance) return;
1892
+ if (currentInstance.parent) mergeProvides(currentInstance.parent);
1893
+ if (currentInstance.provides) Object.assign(provides, currentInstance.provides);
1894
+ }
1895
+ if (instance?.parent && props.enableProvideBridge) {
1896
+ mergeProvides(instance.parent);
1897
+ Reflect.ownKeys(provides).forEach((key) => {
1898
+ provide(key, provides[key]);
1899
+ });
1900
+ }
1901
+ provide(INJECTION_KEY, context);
1902
+ provide("extend", extend);
1903
+ if (typeof process !== "undefined" && process.env.NODE_ENV !== "production" && typeof window !== "undefined" && ctx?.app) registerTresDevtools(ctx?.app, context);
1904
+ return () => {
1905
+ hmrTick.value;
1906
+ return h(Fragment, null, slots.default?.() ?? []);
1907
+ };
1908
+ } });
1909
+ const mountCustomRenderer = (context) => {
1910
+ const canvas = props.canvas;
1911
+ if (getRoot(canvas)) return;
1912
+ const hmrTick = ref(0);
1913
+ const internalComponent = createInternalComponent(context, hmrTick);
1914
+ const renderer = createRenderer(nodeOps({
1915
+ context,
1916
+ options: props.customRendererOptions
1917
+ }));
1918
+ renderer.render(h(internalComponent), scene.value);
1919
+ setRoot(canvas, {
1920
+ renderer,
1921
+ internalComponent,
1922
+ hmrTick,
1923
+ context
1924
+ });
1925
+ };
1926
+ const dispose = (context, force = false) => {
1927
+ disposeObject3D(context.scene.value);
1928
+ if (force) {
1929
+ context.renderer.instance.dispose();
1930
+ if (isWebGLRenderer(context.renderer.instance)) {
1931
+ context.renderer.instance.renderLists.dispose();
1932
+ context.renderer.instance.forceContextLoss();
1933
+ }
1934
+ }
1935
+ scene.value.__tres = {
1936
+ root: context,
1937
+ objects: [],
1938
+ isUnmounting: true
1939
+ };
1940
+ };
1941
+ const context = shallowRef(useBaseTresContextProvider({
1942
+ scene: scene.value,
1943
+ canvas: props.canvas,
1944
+ fpsLimit: () => props.fpsLimit ?? Infinity,
1945
+ windowSize: props.windowSize ?? false,
1946
+ rendererOptions: props
1947
+ }));
1948
+ __expose({
1949
+ context,
1950
+ dispose: () => dispose(context.value, true)
1951
+ });
1952
+ const handleHMR = () => {
1953
+ const root = getRoot(props.canvas);
1954
+ if (root) root.hmrTick.value++;
1955
+ };
1956
+ const unmountCanvas = () => {
1957
+ const root = getRoot(props.canvas);
1958
+ if (!root) return;
1959
+ const isTresScene = (value) => isScene(value) && "__tres" in value;
1960
+ if (isTresScene(scene.value)) scene.value.__tres.isUnmounting = true;
1961
+ root.renderer.render(null, scene.value);
1962
+ dispose(context.value);
1963
+ deleteRoot(props.canvas);
1964
+ };
1965
+ const { camera, renderer } = context.value;
1966
+ const { registerCamera, cameras, activeCamera, deregisterCamera } = camera;
1967
+ const addDefaultCamera = () => {
1968
+ const camera = new PerspectiveCamera(45, window.innerWidth / window.innerHeight, .1, 1e3);
1969
+ camera.position.set(3, 3, 3);
1970
+ camera.lookAt(0, 0, 0);
1971
+ registerCamera(camera);
1972
+ const unwatch = watchEffect(() => {
1973
+ if (cameras.value.length >= 2) {
1974
+ camera.removeFromParent();
1975
+ deregisterCamera(camera);
1976
+ unwatch?.();
1977
+ }
1978
+ });
1979
+ };
1980
+ context.value.events.onPointerMissed((event) => {
1981
+ emit("pointermissed", event);
1982
+ });
1983
+ watch(() => props.camera, (newCamera, oldCamera) => {
1984
+ if (newCamera) registerCamera(toValue(newCamera), true);
1985
+ if (oldCamera) {
1986
+ toValue(oldCamera).removeFromParent();
1987
+ deregisterCamera(toValue(oldCamera));
1988
+ }
1989
+ }, { immediate: true });
1990
+ renderer.onRender(() => {
1991
+ if (context.value) emit("render", context.value);
1992
+ });
1993
+ renderer.loop.onLoop((loopContext) => {
1994
+ if (context.value) emit("loop", {
1995
+ ...context.value,
1996
+ ...loopContext
1997
+ });
1998
+ });
1999
+ renderer.loop.onBeforeLoop((loopContext) => {
2000
+ if (context.value) emit("beforeLoop", {
2001
+ ...context.value,
2002
+ ...loopContext
2003
+ });
2004
+ });
2005
+ renderer.onReady(() => {
2006
+ mountCustomRenderer(context.value);
2007
+ emit("ready", context.value);
2008
+ if (!activeCamera.value) addDefaultCamera();
2009
+ });
2010
+ renderer.onError((error) => {
2011
+ emit("error", error);
2012
+ });
2013
+ if (import.meta.hot) {
2014
+ const hmrHandler = () => handleHMR();
2015
+ import.meta.hot.on("vite:afterUpdate", hmrHandler);
2016
+ onBeforeUnmount(() => {
2017
+ import.meta.hot?.off?.("vite:afterUpdate", hmrHandler);
2018
+ });
2019
+ }
2020
+ onMounted(async () => {
2021
+ await promiseTimeout(3e3);
2022
+ 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.`);
2023
+ });
2024
+ onUnmounted(unmountCanvas);
2025
+ return () => {};
2026
+ }
2027
+ });
2028
+ //#endregion
2029
+ //#region src/components/forwardContextEmits.ts
2030
+ const contextEvents = {
2031
+ ready: true,
2032
+ error: true,
2033
+ pointermissed: true,
2034
+ render: true,
2035
+ beforeLoop: true,
2036
+ loop: true,
2037
+ click: true,
2038
+ contextmenu: true,
2039
+ pointermove: true,
2040
+ pointerenter: true,
2041
+ pointerleave: true,
2042
+ pointerover: true,
2043
+ pointerout: true,
2044
+ dblclick: true,
2045
+ pointerdown: true,
2046
+ pointerup: true,
2047
+ pointercancel: true,
2048
+ lostpointercapture: true,
2049
+ wheel: true
2050
+ };
2051
+ /**
2052
+ * Listeners for `v-on` that re-emit every `ContextEmits` event through `emit`.
2053
+ * Both `TresCanvas` components declare these emits for typed handlers, and a declared emit
2054
+ * does not fall through `$attrs`, so each wrapper must forward them.
2055
+ */
2056
+ const forwardContextEmits = (emit) => Object.fromEntries(Object.keys(contextEvents).map((event) => [event, (...args) => emit(event, ...args)]));
2057
+ const forwardContext = (emit, child) => {
2058
+ return {
2059
+ listeners: forwardContextEmits(emit),
2060
+ exposed: {
2061
+ get context() {
2062
+ return child.value?.context;
2063
+ },
2064
+ dispose: () => child.value?.dispose()
2065
+ }
2066
+ };
2067
+ };
2068
+ //#endregion
2069
+ //#region src/components/tresCanvasDefaults.ts
2070
+ /**
2071
+ * Prop defaults shared by the root and the WebGPU `TresCanvas`.
2072
+ * Boolean props default to `undefined`, else Vue casts an absent boolean prop to `false`
2073
+ * and it overrides the renderer's own default.
2074
+ */
2075
+ const tresCanvasDefaults = {
2076
+ alpha: void 0,
2077
+ depth: void 0,
2078
+ shadows: void 0,
2079
+ stencil: void 0,
2080
+ antialias: true,
2081
+ windowSize: void 0,
2082
+ useLegacyLights: void 0,
2083
+ preserveDrawingBuffer: void 0,
2084
+ logarithmicDepthBuffer: void 0,
2085
+ failIfMajorPerformanceCaveat: void 0,
2086
+ renderMode: "always",
2087
+ clearColor: "#000000",
2088
+ clearAlpha: 1,
2089
+ enableProvideBridge: true,
2090
+ toneMapping: ACESFilmicToneMapping,
2091
+ shadowMapType: void 0,
2092
+ customRendererOptions: () => ({ primitivePrefix: "" })
2093
+ };
2094
+ //#endregion
2095
+ //#region src/components/BaseCanvas.vue?vue&type=script&setup=true&lang.ts
2096
+ const _hoisted_1 = ["data-scene", "data-tres"];
2097
+ //#endregion
2098
+ //#region src/components/BaseCanvas.vue
2099
+ var BaseCanvas_default = /* @__PURE__ */ defineComponent({
2100
+ __name: "BaseCanvas",
2101
+ props: /*@__PURE__*/ mergeDefaults({
2102
+ camera: {
2103
+ type: null,
2104
+ required: false
2105
+ },
2106
+ windowSize: {
2107
+ type: Boolean,
2108
+ required: false
2109
+ },
2110
+ fpsLimit: {
2111
+ type: Number,
2112
+ required: false
2113
+ },
2114
+ enableProvideBridge: {
2115
+ type: Boolean,
2116
+ required: false
2117
+ },
2118
+ customRendererOptions: {
2119
+ type: Object,
2120
+ required: false
2121
+ },
2122
+ antialias: {
2123
+ type: Boolean,
2124
+ required: false
2125
+ },
2126
+ stencil: {
2127
+ type: Boolean,
2128
+ required: false
2129
+ },
2130
+ depth: {
2131
+ type: Boolean,
2132
+ required: false
2133
+ },
2134
+ precision: {
2135
+ type: String,
2136
+ required: false
2137
+ },
2138
+ logarithmicDepthBuffer: {
2139
+ type: Boolean,
2140
+ required: false
2141
+ },
2142
+ preserveDrawingBuffer: {
2143
+ type: Boolean,
2144
+ required: false
2145
+ },
2146
+ powerPreference: {
2147
+ type: null,
2148
+ required: false
2149
+ },
2150
+ alpha: {
2151
+ type: Boolean,
2152
+ required: false
2153
+ },
2154
+ premultipliedAlpha: {
2155
+ type: Boolean,
2156
+ required: false
2157
+ },
2158
+ failIfMajorPerformanceCaveat: {
2159
+ type: Boolean,
2160
+ required: false
2161
+ },
2162
+ clearColor: {
2163
+ type: [
2164
+ Object,
2165
+ String,
2166
+ Number
2167
+ ],
2168
+ required: false
2169
+ },
2170
+ clearAlpha: {
2171
+ type: Number,
2172
+ required: false
2173
+ },
2174
+ shadows: {
2175
+ type: Boolean,
2176
+ required: false
2177
+ },
2178
+ toneMapping: {
2179
+ type: null,
2180
+ required: false
2181
+ },
2182
+ shadowMapType: {
2183
+ type: null,
2184
+ required: false
2185
+ },
2186
+ useLegacyLights: {
2187
+ type: Boolean,
2188
+ required: false
2189
+ },
2190
+ outputColorSpace: {
2191
+ type: null,
2192
+ required: false
2193
+ },
2194
+ toneMappingExposure: {
2195
+ type: Number,
2196
+ required: false
2197
+ },
2198
+ renderMode: {
2199
+ type: String,
2200
+ required: false
2201
+ },
2202
+ dpr: {
2203
+ type: [Number, Array],
2204
+ required: false
2205
+ },
2206
+ renderer: {
2207
+ type: Function,
2208
+ required: false
2209
+ }
2210
+ }, tresCanvasDefaults),
2211
+ emits: [
2212
+ "ready",
2213
+ "error",
2214
+ "pointermissed",
2215
+ "render",
2216
+ "beforeLoop",
2217
+ "loop",
2218
+ "click",
2219
+ "contextmenu",
2220
+ "pointermove",
2221
+ "pointerenter",
2222
+ "pointerleave",
2223
+ "pointerover",
2224
+ "pointerout",
2225
+ "dblclick",
2226
+ "pointerdown",
2227
+ "pointerup",
2228
+ "pointercancel",
2229
+ "lostpointercapture",
2230
+ "wheel"
2231
+ ],
2232
+ setup(__props, { expose: __expose, emit: __emit }) {
2233
+ const props = __props;
2234
+ const contextListeners = forwardContextEmits(__emit);
2235
+ const canvasRef = ref();
2236
+ const contextRef = shallowRef();
2237
+ __expose({
2238
+ get context() {
2239
+ return contextRef.value?.context;
2240
+ },
2241
+ dispose: () => contextRef.value?.dispose()
2242
+ });
2243
+ return (_ctx, _cache) => {
2244
+ return openBlock(), createElementBlock("canvas", {
2245
+ ref_key: "canvasRef",
2246
+ ref: canvasRef,
2247
+ "data-scene": contextRef.value?.context?.scene.value.uuid,
2248
+ class: normalizeClass(_ctx.$attrs.class),
2249
+ "data-tres": `tresjs ${unref(version)}`,
2250
+ style: normalizeStyle({
2251
+ display: "block",
2252
+ width: "100%",
2253
+ height: "100%",
2254
+ position: __props.windowSize ? "fixed" : "relative",
2255
+ top: 0,
2256
+ left: 0,
2257
+ pointerEvents: "auto",
2258
+ touchAction: "none",
2259
+ ..._ctx.$attrs.style
2260
+ })
2261
+ }, [canvasRef.value ? (openBlock(), createBlock(Context_default, mergeProps({
2262
+ key: 0,
2263
+ ref_key: "contextRef",
2264
+ ref: contextRef,
2265
+ canvas: canvasRef.value
2266
+ }, props, toHandlers(unref(contextListeners))), {
2267
+ default: withCtx(() => [renderSlot(_ctx.$slots, "default")]),
2268
+ _: 3
2269
+ }, 16, ["canvas"])) : createCommentVNode("v-if", true)], 14, _hoisted_1);
2270
+ };
2271
+ }
2272
+ });
2273
+ //#endregion
2274
+ //#region src/components/TresCanvas.vue
2275
+ var TresCanvas_default = /* @__PURE__ */ defineComponent({
2276
+ __name: "TresCanvas",
2277
+ props: /*@__PURE__*/ mergeDefaults({
2278
+ camera: {
2279
+ type: null,
2280
+ required: false
2281
+ },
2282
+ windowSize: {
2283
+ type: Boolean,
2284
+ required: false
2285
+ },
2286
+ fpsLimit: {
2287
+ type: Number,
2288
+ required: false
2289
+ },
2290
+ enableProvideBridge: {
2291
+ type: Boolean,
2292
+ required: false
2293
+ },
2294
+ customRendererOptions: {
2295
+ type: Object,
2296
+ required: false
2297
+ },
2298
+ antialias: {
2299
+ type: Boolean,
2300
+ required: false
2301
+ },
2302
+ stencil: {
2303
+ type: Boolean,
2304
+ required: false
2305
+ },
2306
+ depth: {
2307
+ type: Boolean,
2308
+ required: false
2309
+ },
2310
+ precision: {
2311
+ type: String,
2312
+ required: false
2313
+ },
2314
+ logarithmicDepthBuffer: {
2315
+ type: Boolean,
2316
+ required: false
2317
+ },
2318
+ preserveDrawingBuffer: {
2319
+ type: Boolean,
2320
+ required: false
2321
+ },
2322
+ powerPreference: {
2323
+ type: null,
2324
+ required: false
2325
+ },
2326
+ alpha: {
2327
+ type: Boolean,
2328
+ required: false
2329
+ },
2330
+ premultipliedAlpha: {
2331
+ type: Boolean,
2332
+ required: false
2333
+ },
2334
+ failIfMajorPerformanceCaveat: {
2335
+ type: Boolean,
2336
+ required: false
2337
+ },
2338
+ clearColor: {
2339
+ type: [
2340
+ Object,
2341
+ String,
2342
+ Number
2343
+ ],
2344
+ required: false
2345
+ },
2346
+ clearAlpha: {
2347
+ type: Number,
2348
+ required: false
2349
+ },
2350
+ shadows: {
2351
+ type: Boolean,
2352
+ required: false
2353
+ },
2354
+ toneMapping: {
2355
+ type: null,
2356
+ required: false
2357
+ },
2358
+ shadowMapType: {
2359
+ type: null,
2360
+ required: false
2361
+ },
2362
+ useLegacyLights: {
2363
+ type: Boolean,
2364
+ required: false
2365
+ },
2366
+ outputColorSpace: {
2367
+ type: null,
2368
+ required: false
2369
+ },
2370
+ toneMappingExposure: {
2371
+ type: Number,
2372
+ required: false
2373
+ },
2374
+ renderMode: {
2375
+ type: String,
2376
+ required: false
2377
+ },
2378
+ dpr: {
2379
+ type: [Number, Array],
2380
+ required: false
2381
+ },
2382
+ renderer: {
2383
+ type: Function,
2384
+ required: false
2385
+ }
2386
+ }, tresCanvasDefaults),
2387
+ emits: [
2388
+ "ready",
2389
+ "error",
2390
+ "pointermissed",
2391
+ "render",
2392
+ "beforeLoop",
2393
+ "loop",
2394
+ "click",
2395
+ "contextmenu",
2396
+ "pointermove",
2397
+ "pointerenter",
2398
+ "pointerleave",
2399
+ "pointerover",
2400
+ "pointerout",
2401
+ "dblclick",
2402
+ "pointerdown",
2403
+ "pointerup",
2404
+ "pointercancel",
2405
+ "lostpointercapture",
2406
+ "wheel"
2407
+ ],
2408
+ setup(__props, { expose: __expose, emit: __emit }) {
2409
+ const props = __props;
2410
+ const emit = __emit;
2411
+ const child = shallowRef();
2412
+ const { listeners, exposed } = forwardContext(emit, child);
2413
+ __expose(exposed);
2414
+ extendWithThree();
2415
+ const createDefaultRenderer = (ctx) => createWebGLRenderer(ctx, props);
2416
+ return (_ctx, _cache) => {
2417
+ return openBlock(), createBlock(BaseCanvas_default, mergeProps({
2418
+ ref_key: "child",
2419
+ ref: child
2420
+ }, props, { renderer: props.renderer ?? createDefaultRenderer }, toHandlers(unref(listeners))), {
2421
+ default: withCtx(() => [renderSlot(_ctx.$slots, "default")]),
2422
+ _: 3
2423
+ }, 16, ["renderer"]);
2424
+ };
2425
+ }
2426
+ });
2427
+ //#endregion
2428
+ //#region src/utils/normalize.ts
2429
+ function normalizeVectorFlexibleParam(value) {
2430
+ if (typeof value === "number") return [
2431
+ value,
2432
+ value,
2433
+ value
2434
+ ];
2435
+ if (value instanceof Vector3) return [
2436
+ value.x,
2437
+ value.y,
2438
+ value.z
2439
+ ];
2440
+ return value;
2441
+ }
2442
+ function normalizeColor(value) {
2443
+ if (value instanceof Color) return value;
2444
+ if (Array.isArray(value)) return new Color(...value);
2445
+ return new Color(value);
2446
+ }
2447
+ //#endregion
2448
+ //#region src/utils/template-compiler-options.ts
2449
+ const whitelist = [
2450
+ "TresCanvas",
2451
+ "TresCanvasContext",
2452
+ "TresLeches",
2453
+ "TresScene",
2454
+ "TresPortal"
2455
+ ];
2456
+ const templateCompilerOptions = { template: { compilerOptions: { isCustomElement: (tag) => (/^Tres[A-Z]/.test(tag) || tag.startsWith("tres-")) && !whitelist.includes(tag) || tag === "primitive" } } };
2457
+ //#endregion
2458
+ //#region src/components/WebGLContext.vue
2459
+ var WebGLContext_default = /* @__PURE__ */ defineComponent({
2460
+ __name: "WebGLContext",
2461
+ props: {
2462
+ camera: {
2463
+ type: null,
2464
+ required: false
2465
+ },
2466
+ windowSize: {
2467
+ type: Boolean,
2468
+ required: false
2469
+ },
2470
+ fpsLimit: {
2471
+ type: Number,
2472
+ required: false
2473
+ },
2474
+ enableProvideBridge: {
2475
+ type: Boolean,
2476
+ required: false
2477
+ },
2478
+ customRendererOptions: {
2479
+ type: Object,
2480
+ required: false
2481
+ },
2482
+ antialias: {
2483
+ type: Boolean,
2484
+ required: false
2485
+ },
2486
+ stencil: {
2487
+ type: Boolean,
2488
+ required: false
2489
+ },
2490
+ depth: {
2491
+ type: Boolean,
2492
+ required: false
2493
+ },
2494
+ precision: {
2495
+ type: String,
2496
+ required: false
2497
+ },
2498
+ logarithmicDepthBuffer: {
2499
+ type: Boolean,
2500
+ required: false
2501
+ },
2502
+ preserveDrawingBuffer: {
2503
+ type: Boolean,
2504
+ required: false
2505
+ },
2506
+ powerPreference: {
2507
+ type: null,
2508
+ required: false
2509
+ },
2510
+ alpha: {
2511
+ type: Boolean,
2512
+ required: false
2513
+ },
2514
+ premultipliedAlpha: {
2515
+ type: Boolean,
2516
+ required: false
2517
+ },
2518
+ failIfMajorPerformanceCaveat: {
2519
+ type: Boolean,
2520
+ required: false
2521
+ },
2522
+ clearColor: {
2523
+ type: [
2524
+ Object,
2525
+ String,
2526
+ Number
2527
+ ],
2528
+ required: false
2529
+ },
2530
+ clearAlpha: {
2531
+ type: Number,
2532
+ required: false
2533
+ },
2534
+ shadows: {
2535
+ type: Boolean,
2536
+ required: false
2537
+ },
2538
+ toneMapping: {
2539
+ type: null,
2540
+ required: false
2541
+ },
2542
+ shadowMapType: {
2543
+ type: null,
2544
+ required: false
2545
+ },
2546
+ useLegacyLights: {
2547
+ type: Boolean,
2548
+ required: false
2549
+ },
2550
+ outputColorSpace: {
2551
+ type: null,
2552
+ required: false
2553
+ },
2554
+ toneMappingExposure: {
2555
+ type: Number,
2556
+ required: false
2557
+ },
2558
+ renderMode: {
2559
+ type: String,
2560
+ required: false
2561
+ },
2562
+ dpr: {
2563
+ type: [Number, Array],
2564
+ required: false
2565
+ },
2566
+ renderer: {
2567
+ type: Function,
2568
+ required: false
2569
+ },
2570
+ canvas: {
2571
+ type: null,
2572
+ required: true
2573
+ }
2574
+ },
2575
+ emits: [
2576
+ "ready",
2577
+ "error",
2578
+ "pointermissed",
2579
+ "render",
2580
+ "beforeLoop",
2581
+ "loop",
2582
+ "click",
2583
+ "contextmenu",
2584
+ "pointermove",
2585
+ "pointerenter",
2586
+ "pointerleave",
2587
+ "pointerover",
2588
+ "pointerout",
2589
+ "dblclick",
2590
+ "pointerdown",
2591
+ "pointerup",
2592
+ "pointercancel",
2593
+ "lostpointercapture",
2594
+ "wheel"
2595
+ ],
2596
+ setup(__props, { expose: __expose, emit: __emit }) {
2597
+ const props = __props;
2598
+ const emit = __emit;
2599
+ const child = shallowRef();
2600
+ const { listeners, exposed } = forwardContext(emit, child);
2601
+ __expose(exposed);
2602
+ extendWithThree();
2603
+ const createDefaultRenderer = (ctx) => createWebGLRenderer(ctx, props);
2604
+ return (_ctx, _cache) => {
2605
+ return openBlock(), createBlock(Context_default, mergeProps({
2606
+ ref_key: "child",
2607
+ ref: child
2608
+ }, props, { renderer: props.renderer ?? createDefaultRenderer }, toHandlers(unref(listeners))), {
2609
+ default: withCtx(() => [renderSlot(_ctx.$slots, "default")]),
2610
+ _: 3
2611
+ }, 16, ["renderer"]);
2612
+ };
2613
+ }
2614
+ });
2615
+ //#endregion
2616
+ //#region src/components/TresPortal.vue
2617
+ var TresPortal_default = /* @__PURE__ */ defineComponent({
2618
+ __name: "TresPortal",
2619
+ props: {
2620
+ to: {
2621
+ type: Object,
2622
+ required: true
2623
+ },
2624
+ disabled: {
2625
+ type: Boolean,
2626
+ required: false
2627
+ }
2628
+ },
2629
+ setup(__props) {
2630
+ return (_ctx, _cache) => {
2631
+ return openBlock(), createBlock(Teleport, {
2632
+ to: __props.to,
2633
+ disabled: __props.disabled
2634
+ }, [renderSlot(_ctx.$slots, "default")], 8, ["to", "disabled"]);
2635
+ };
2636
+ }
2637
+ });
2638
+ //#endregion
2639
+ //#region src/composables/useLoader/index.ts
2640
+ /**
2641
+ * Vue composable for loading 3D models using Three.js loaders
2642
+ * @param Loader - The Three.js loader constructor
2643
+ * @param path - The path to the model file
2644
+ * @param options - Optional configuration for the loader
2645
+ * @returns UseAsyncState composable with the loaded model
2646
+ */
2647
+ function useLoader(Loader, path, options) {
2648
+ const proto = new Loader(options?.manager);
2649
+ const progress = reactive({
2650
+ loaded: 0,
2651
+ total: 0,
2652
+ percentage: 0
2653
+ });
2654
+ if (options?.extensions) options.extensions(proto);
2655
+ const initialPath = toValue(path);
2656
+ const result = useAsyncState((path) => new Promise((resolve, reject) => {
2657
+ const assetPath = path || initialPath || "";
2658
+ proto.load(assetPath, (result) => {
2659
+ resolve(result);
2660
+ }, (event) => {
2661
+ progress.loaded = event.loaded;
2662
+ progress.total = event.total;
2663
+ progress.percentage = progress.loaded / progress.total * 100;
2664
+ }, (err) => {
2665
+ reject(err);
2666
+ });
2667
+ }), options?.initialValue ?? null, {
2668
+ ...options?.asyncOptions,
2669
+ immediate: options?.asyncOptions?.immediate ?? true
2670
+ });
2671
+ const unsub = watch(() => toValue(path), (newPath) => {
2672
+ if (newPath) {
2673
+ const value = result.state.value;
2674
+ if (value && typeof value === "object" && "scene" in value && value.scene) disposeObject3D(value.scene);
2675
+ result.execute(0, newPath);
2676
+ }
2677
+ });
2678
+ onUnmounted(() => {
2679
+ unsub();
2680
+ const value = result.state.value;
2681
+ if (value && typeof value === "object" && "scene" in value && value.scene) disposeObject3D(value.scene);
2682
+ });
2683
+ return {
2684
+ ...result,
2685
+ load: (path) => {
2686
+ result.execute(0, path);
2687
+ },
2688
+ progress,
2689
+ then(onFulfilled, onRejected) {
2690
+ const { then: _then, ...resolved } = this;
2691
+ return result.then(() => resolved).then(onFulfilled, onRejected);
2692
+ }
2693
+ };
2694
+ }
2695
+ //#endregion
2696
+ //#region src/composables/useLoader/component.vue
2697
+ var component_default = /* @__PURE__ */ defineComponent({
2698
+ __name: "component",
2699
+ props: {
2700
+ loader: {
2701
+ type: null,
2702
+ required: true
2703
+ },
2704
+ path: {
2705
+ type: String,
2706
+ required: true
2707
+ },
2708
+ manager: {
2709
+ type: Object,
2710
+ required: false
2711
+ },
2712
+ extensions: {
2713
+ type: Function,
2714
+ required: false
2715
+ }
2716
+ },
2717
+ emits: ["loaded", "error"],
2718
+ setup(__props, { emit: __emit }) {
2719
+ const props = __props;
2720
+ const emit = __emit;
2721
+ const { state, isLoading, error } = useLoader(props.loader, props.path, {
2722
+ manager: props.manager,
2723
+ extensions: props.extensions
2724
+ });
2725
+ whenever(error, (err) => {
2726
+ if (err) emit("error", err);
2727
+ });
2728
+ whenever(state, (value) => {
2729
+ if (value) emit("loaded", value);
2730
+ });
2731
+ return (_ctx, _cache) => {
2732
+ return renderSlot(_ctx.$slots, "default", {
2733
+ state: unref(state),
2734
+ isLoading: unref(isLoading),
2735
+ error: unref(error)
2736
+ });
2737
+ };
2738
+ }
2739
+ });
2740
+ //#endregion
2741
+ //#region src/utils/graph.ts
2742
+ function buildGraph(object) {
2743
+ const data = {
2744
+ nodes: {},
2745
+ materials: {},
2746
+ meshes: {}
2747
+ };
2748
+ if (object) object.traverse((obj) => {
2749
+ if (obj.name) data.nodes[obj.name] = obj;
2750
+ if (isMesh(obj)) {
2751
+ if (!data.meshes[obj.name]) data.meshes[obj.name] = obj;
2752
+ (Array.isArray(obj.material) ? obj.material : [obj.material]).forEach((material) => {
2753
+ if (material.name && !data.materials[material.name]) data.materials[material.name] = material;
2754
+ });
2755
+ }
2756
+ });
2757
+ return data;
2758
+ }
2759
+ //#endregion
2760
+ //#region src/composables/useGraph/index.ts
2761
+ const useGraph = (object) => {
2762
+ return computed(() => {
2763
+ const obj = toValue(object);
2764
+ if (!obj) return;
2765
+ return buildGraph(obj);
2766
+ });
2767
+ };
2768
+ //#endregion
2769
+ //#region src/utils/createPriorityEventHook.ts
2770
+ function createPriorityEventHook() {
2771
+ const eventToPriority = /* @__PURE__ */ new Map();
2772
+ const ascending = /* @__PURE__ */ new Set();
2773
+ let ADD_COUNT = 0;
2774
+ let dirty = false;
2775
+ const sort = () => {
2776
+ const sorted = Array.from(eventToPriority.entries()).sort((a, b) => {
2777
+ const priorityDiff = a[1].priority - b[1].priority;
2778
+ return priorityDiff === 0 ? a[1].addI - b[1].addI : priorityDiff;
2779
+ });
2780
+ ascending.clear();
2781
+ sorted.forEach((entry) => ascending.add(entry[0]));
2782
+ };
2783
+ const off = (fn) => {
2784
+ eventToPriority.delete(fn);
2785
+ ascending.delete(fn);
2786
+ };
2787
+ const on = (fn, priority = 0) => {
2788
+ eventToPriority.set(fn, {
2789
+ priority,
2790
+ addI: ADD_COUNT++
2791
+ });
2792
+ const offFn = () => off(fn);
2793
+ tryOnScopeDispose(offFn);
2794
+ dirty = true;
2795
+ return { off: offFn };
2796
+ };
2797
+ const trigger = (...args) => {
2798
+ if (dirty) {
2799
+ sort();
2800
+ dirty = false;
2801
+ }
2802
+ return Promise.all(Array.from(ascending).map((fn) => fn(...args)));
2803
+ };
2804
+ const dispose = () => {
2805
+ eventToPriority.clear();
2806
+ ascending.clear();
2807
+ };
2808
+ return {
2809
+ on,
2810
+ off,
2811
+ trigger,
2812
+ dispose,
2813
+ get count() {
2814
+ return eventToPriority.size;
2815
+ }
2816
+ };
2817
+ }
2818
+ //#endregion
2819
+ //#region src/composables/useTres/index.ts
2820
+ function useTres() {
2821
+ const { scene, renderer, camera, sizes, controls, extend, events, isWebGPU } = useTresContext();
2822
+ return {
2823
+ scene,
2824
+ renderer: renderer.instance,
2825
+ camera: camera.activeCamera,
2826
+ sizes,
2827
+ controls,
2828
+ extend,
2829
+ events,
2830
+ isWebGPU,
2831
+ invalidate: renderer.invalidate,
2832
+ advance: renderer.advance
2833
+ };
2834
+ }
2835
+ //#endregion
2836
+ //#region src/composables/useLoop/index.ts
2837
+ /**
2838
+ * Composable that provides control over the render loop and animation lifecycle.
2839
+ */
2840
+ const useLoop = () => {
2841
+ const tresContext = useTres();
2842
+ const { renderer: rendererManager } = useTresContext();
2843
+ const eventHookBeforeRender = createPriorityEventHook();
2844
+ const eventHookAfterRender = createPriorityEventHook();
2845
+ rendererManager.loop.onBeforeLoop((loopContext) => {
2846
+ eventHookBeforeRender.trigger({
2847
+ ...tresContext,
2848
+ ...loopContext
2849
+ });
2850
+ });
2851
+ rendererManager.loop.onLoop((loopContext) => {
2852
+ eventHookAfterRender.trigger({
2853
+ ...tresContext,
2854
+ ...loopContext
2855
+ });
2856
+ });
2857
+ const render = rendererManager.replaceRenderFunction;
2858
+ return {
2859
+ stop: rendererManager.loop.stop,
2860
+ start: rendererManager.loop.start,
2861
+ isActive: rendererManager.loop.isActive,
2862
+ onBeforeRender: eventHookBeforeRender.on,
2863
+ onRender: eventHookAfterRender.on,
2864
+ render
2865
+ };
2866
+ };
2867
+ //#endregion
2868
+ //#region src/directives/vDistanceTo.ts
2869
+ let arrowHelper = null;
2870
+ const vDistanceTo = {
2871
+ updated: (el, binding) => {
2872
+ const extractBindingPosition = (binding) => {
2873
+ let observer = binding.value;
2874
+ if (binding.value && isMesh(binding.value)) observer = binding.value.position;
2875
+ if (Array.isArray(binding.value)) observer = new Vector3(...observer);
2876
+ return observer;
2877
+ };
2878
+ const observer = extractBindingPosition(binding);
2879
+ if (!observer) {
2880
+ logWarning(`v-distance-to: problem with binding value: ${binding.value}`);
2881
+ return;
2882
+ }
2883
+ if (arrowHelper) {
2884
+ arrowHelper.dispose();
2885
+ el.parent.remove(arrowHelper);
2886
+ }
2887
+ const dir = observer.clone().sub(el.position);
2888
+ dir.normalize();
2889
+ arrowHelper = new ArrowHelper(dir, el.position, el.position.distanceTo(observer), 16776960);
2890
+ el.parent.add(arrowHelper);
2891
+ console.table([
2892
+ ["Distance:", el.position.distanceTo(observer)],
2893
+ [`origin: ${el.name || el.type}`, `x:${el.position.x}, y:${el.position.y}, z:${el.position?.z}`],
2894
+ [`Destiny: ${el.name || el.type}`, `x:${observer.x}, y:${observer.y}, z:${observer?.z}`]
2895
+ ]);
2896
+ },
2897
+ unmounted: (el) => {
2898
+ arrowHelper?.dispose();
2899
+ if (el.parent) el.parent.remove(arrowHelper);
2900
+ }
2901
+ };
2902
+ //#endregion
2903
+ //#region src/directives/vLightHelper.ts
2904
+ let CurrentHelper;
2905
+ let currentInstance;
2906
+ const helpers = {
2907
+ DirectionalLight: DirectionalLightHelper,
2908
+ PointLight: PointLightHelper,
2909
+ SpotLight: SpotLightHelper,
2910
+ HemisphereLight: HemisphereLightHelper,
2911
+ RectAreaLight: RectAreaLightHelper
2912
+ };
2913
+ const vLightHelper = {
2914
+ mounted: (el) => {
2915
+ if (!isLight(el)) {
2916
+ logWarning(`${el.type} is not a light`);
2917
+ return;
2918
+ }
2919
+ CurrentHelper = helpers[el.type];
2920
+ el.parent?.add(new CurrentHelper(el, 1, el.color.getHex()));
2921
+ },
2922
+ updated: (el) => {
2923
+ currentInstance = el.parent?.children.find((child) => child instanceof CurrentHelper);
2924
+ if (currentInstance instanceof RectAreaLightHelper) return;
2925
+ currentInstance.update();
2926
+ },
2927
+ unmounted: (el) => {
2928
+ if (!el.isLight) {
2929
+ logWarning(`${el.type} is not a light`);
2930
+ return;
2931
+ }
2932
+ currentInstance = el.parent?.children.find((child) => child instanceof CurrentHelper);
2933
+ if (currentInstance && currentInstance.dispose) currentInstance.dispose();
2934
+ if (el.parent) el.parent.remove(currentInstance);
2935
+ }
2936
+ };
2937
+ //#endregion
2938
+ //#region src/directives/vLog.ts
2939
+ const vLog = { mounted: (el, binding) => {
2940
+ if (binding.arg) {
2941
+ console.log(`v-log:${binding.arg}`, el[binding.arg]);
2942
+ return;
2943
+ }
2944
+ console.log("v-log", el);
2945
+ } };
2946
+ //#endregion
2947
+ //#region src/index.ts
2948
+ const plugin = { install(app) {
2949
+ app.component("TresCanvas", TresCanvas_default);
2950
+ } };
2951
+ //#endregion
2952
+ 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 };