@tresjs/core 5.9.2 → 5.10.0

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