UNPKG

3d-scene-creator

Version:

A simple utility to create and manage 3D scenes

707 lines (706 loc) 30.9 kB
import * as e from "three"; import { OrbitControls as t } from "three/examples/jsm/controls/OrbitControls.js"; import { GLTFLoader as n } from "three/examples/jsm/loaders/GLTFLoader.js"; //#region node_modules/.pnpm/@tweenjs+tween.js@25.0.0/node_modules/@tweenjs/tween.js/dist/tween.esm.js var r = Object.freeze({ Linear: Object.freeze({ None: function(e) { return e; }, In: function(e) { return e; }, Out: function(e) { return e; }, InOut: function(e) { return e; } }), Quadratic: Object.freeze({ In: function(e) { return e * e; }, Out: function(e) { return e * (2 - e); }, InOut: function(e) { return (e *= 2) < 1 ? .5 * e * e : -.5 * (--e * (e - 2) - 1); } }), Cubic: Object.freeze({ In: function(e) { return e * e * e; }, Out: function(e) { return --e * e * e + 1; }, InOut: function(e) { return (e *= 2) < 1 ? .5 * e * e * e : .5 * ((e -= 2) * e * e + 2); } }), Quartic: Object.freeze({ In: function(e) { return e * e * e * e; }, Out: function(e) { return 1 - --e * e * e * e; }, InOut: function(e) { return (e *= 2) < 1 ? .5 * e * e * e * e : -.5 * ((e -= 2) * e * e * e - 2); } }), Quintic: Object.freeze({ In: function(e) { return e * e * e * e * e; }, Out: function(e) { return --e * e * e * e * e + 1; }, InOut: function(e) { return (e *= 2) < 1 ? .5 * e * e * e * e * e : .5 * ((e -= 2) * e * e * e * e + 2); } }), Sinusoidal: Object.freeze({ In: function(e) { return 1 - Math.sin((1 - e) * Math.PI / 2); }, Out: function(e) { return Math.sin(e * Math.PI / 2); }, InOut: function(e) { return .5 * (1 - Math.sin(Math.PI * (.5 - e))); } }), Exponential: Object.freeze({ In: function(e) { return e === 0 ? 0 : 1024 ** (e - 1); }, Out: function(e) { return e === 1 ? 1 : 1 - 2 ** (-10 * e); }, InOut: function(e) { return e === 0 ? 0 : e === 1 ? 1 : (e *= 2) < 1 ? .5 * 1024 ** (e - 1) : .5 * (-(2 ** (-10 * (e - 1))) + 2); } }), Circular: Object.freeze({ In: function(e) { return 1 - Math.sqrt(1 - e * e); }, Out: function(e) { return Math.sqrt(1 - --e * e); }, InOut: function(e) { return (e *= 2) < 1 ? -.5 * (Math.sqrt(1 - e * e) - 1) : .5 * (Math.sqrt(1 - (e -= 2) * e) + 1); } }), Elastic: Object.freeze({ In: function(e) { return e === 0 ? 0 : e === 1 ? 1 : -(2 ** (10 * (e - 1))) * Math.sin((e - 1.1) * 5 * Math.PI); }, Out: function(e) { return e === 0 ? 0 : e === 1 ? 1 : 2 ** (-10 * e) * Math.sin((e - .1) * 5 * Math.PI) + 1; }, InOut: function(e) { return e === 0 ? 0 : e === 1 ? 1 : (e *= 2, e < 1 ? -.5 * 2 ** (10 * (e - 1)) * Math.sin((e - 1.1) * 5 * Math.PI) : .5 * 2 ** (-10 * (e - 1)) * Math.sin((e - 1.1) * 5 * Math.PI) + 1); } }), Back: Object.freeze({ In: function(e) { var t = 1.70158; return e === 1 ? 1 : e * e * ((t + 1) * e - t); }, Out: function(e) { var t = 1.70158; return e === 0 ? 0 : --e * e * ((t + 1) * e + t) + 1; }, InOut: function(e) { var t = 1.70158 * 1.525; return (e *= 2) < 1 ? .5 * (e * e * ((t + 1) * e - t)) : .5 * ((e -= 2) * e * ((t + 1) * e + t) + 2); } }), Bounce: Object.freeze({ In: function(e) { return 1 - r.Bounce.Out(1 - e); }, Out: function(e) { return e < 1 / 2.75 ? 7.5625 * e * e : e < 2 / 2.75 ? 7.5625 * (e -= 1.5 / 2.75) * e + .75 : e < 2.5 / 2.75 ? 7.5625 * (e -= 2.25 / 2.75) * e + .9375 : 7.5625 * (e -= 2.625 / 2.75) * e + .984375; }, InOut: function(e) { return e < .5 ? r.Bounce.In(e * 2) * .5 : r.Bounce.Out(e * 2 - 1) * .5 + .5; } }), generatePow: function(e) { return e === void 0 && (e = 4), e = e < 2 ** -52 ? 2 ** -52 : e, e = e > 1e4 ? 1e4 : e, { In: function(t) { return t ** +e; }, Out: function(t) { return 1 - (1 - t) ** e; }, InOut: function(t) { return t < .5 ? (t * 2) ** e / 2 : (1 - (2 - t * 2) ** e) / 2 + .5; } }; } }), i = function() { return performance.now(); }, a = function() { function e() { var e = [...arguments]; this._tweens = {}, this._tweensAddedDuringUpdate = {}, this.add.apply(this, e); } return e.prototype.getAll = function() { var e = this; return Object.keys(this._tweens).map(function(t) { return e._tweens[t]; }); }, e.prototype.removeAll = function() { this._tweens = {}; }, e.prototype.add = function() { for (var e, t = [], n = 0; n < arguments.length; n++) t[n] = arguments[n]; for (var r = 0, i = t; r < i.length; r++) { var a = i[r]; (e = a._group) == null || e.remove(a), a._group = this, this._tweens[a.getId()] = a, this._tweensAddedDuringUpdate[a.getId()] = a; } }, e.prototype.remove = function() { for (var e = [...arguments], t = 0, n = e; t < n.length; t++) { var r = n[t]; r._group = void 0, delete this._tweens[r.getId()], delete this._tweensAddedDuringUpdate[r.getId()]; } }, e.prototype.allStopped = function() { return this.getAll().every(function(e) { return !e.isPlaying(); }); }, e.prototype.update = function(e, t) { e === void 0 && (e = i()), t === void 0 && (t = !0); var n = Object.keys(this._tweens); if (n.length !== 0) for (; n.length > 0;) { this._tweensAddedDuringUpdate = {}; for (var r = 0; r < n.length; r++) { var a = this._tweens[n[r]], o = !t; a && a.update(e, o) === !1 && !t && this.remove(a); } n = Object.keys(this._tweensAddedDuringUpdate); } }, e; }(), o = { Linear: function(e, t) { var n = e.length - 1, r = n * t, i = Math.floor(r), a = o.Utils.Linear; return t < 0 ? a(e[0], e[1], r) : t > 1 ? a(e[n], e[n - 1], n - r) : a(e[i], e[i + 1 > n ? n : i + 1], r - i); }, Bezier: function(e, t) { for (var n = 0, r = e.length - 1, i = Math.pow, a = o.Utils.Bernstein, s = 0; s <= r; s++) n += i(1 - t, r - s) * i(t, s) * e[s] * a(r, s); return n; }, CatmullRom: function(e, t) { var n = e.length - 1, r = n * t, i = Math.floor(r), a = o.Utils.CatmullRom; return e[0] === e[n] ? (t < 0 && (i = Math.floor(r = n * (1 + t))), a(e[(i - 1 + n) % n], e[i], e[(i + 1) % n], e[(i + 2) % n], r - i)) : t < 0 ? e[0] - (a(e[0], e[0], e[1], e[1], -r) - e[0]) : t > 1 ? e[n] - (a(e[n], e[n], e[n - 1], e[n - 1], r - n) - e[n]) : a(e[i ? i - 1 : 0], e[i], e[n < i + 1 ? n : i + 1], e[n < i + 2 ? n : i + 2], r - i); }, Utils: { Linear: function(e, t, n) { return (t - e) * n + e; }, Bernstein: function(e, t) { var n = o.Utils.Factorial; return n(e) / n(t) / n(e - t); }, Factorial: (function() { var e = [1]; return function(t) { var n = 1; if (e[t]) return e[t]; for (var r = t; r > 1; r--) n *= r; return e[t] = n, n; }; })(), CatmullRom: function(e, t, n, r, i) { var a = (n - e) * .5, o = (r - t) * .5, s = i * i, c = i * s; return (2 * t - 2 * n + a + o) * c + (-3 * t + 3 * n - 2 * a - o) * s + a * i + t; } } }, s = function() { function e() {} return e.nextId = function() { return e._nextId++; }, e._nextId = 0, e; }(), c = new a(), l = function() { function e(e, t) { this._isPaused = !1, this._pauseStart = 0, this._valuesStart = {}, this._valuesEnd = {}, this._valuesStartRepeat = {}, this._duration = 1e3, this._isDynamic = !1, this._initialRepeat = 0, this._repeat = 0, this._yoyo = !1, this._isPlaying = !1, this._reversed = !1, this._delayTime = 0, this._startTime = 0, this._easingFunction = r.Linear.None, this._interpolationFunction = o.Linear, this._chainedTweens = [], this._onStartCallbackFired = !1, this._onEveryStartCallbackFired = !1, this._id = s.nextId(), this._isChainStopped = !1, this._propertiesAreSetUp = !1, this._goToEnd = !1, this._object = e, typeof t == "object" ? (this._group = t, t.add(this)) : t === !0 && (this._group = c, c.add(this)); } return e.prototype.getId = function() { return this._id; }, e.prototype.isPlaying = function() { return this._isPlaying; }, e.prototype.isPaused = function() { return this._isPaused; }, e.prototype.getDuration = function() { return this._duration; }, e.prototype.to = function(e, t) { if (t === void 0 && (t = 1e3), this._isPlaying) throw Error("Can not call Tween.to() while Tween is already started or paused. Stop the Tween first."); return this._valuesEnd = e, this._propertiesAreSetUp = !1, this._duration = t < 0 ? 0 : t, this; }, e.prototype.duration = function(e) { return e === void 0 && (e = 1e3), this._duration = e < 0 ? 0 : e, this; }, e.prototype.dynamic = function(e) { return e === void 0 && (e = !1), this._isDynamic = e, this; }, e.prototype.start = function(e, t) { if (e === void 0 && (e = i()), t === void 0 && (t = !1), this._isPlaying) return this; if (this._repeat = this._initialRepeat, this._reversed) for (var n in this._reversed = !1, this._valuesStartRepeat) this._swapEndStartRepeatValues(n), this._valuesStart[n] = this._valuesStartRepeat[n]; if (this._isPlaying = !0, this._isPaused = !1, this._onStartCallbackFired = !1, this._onEveryStartCallbackFired = !1, this._isChainStopped = !1, this._startTime = e, this._startTime += this._delayTime, !this._propertiesAreSetUp || t) { if (this._propertiesAreSetUp = !0, !this._isDynamic) { var r = {}; for (var a in this._valuesEnd) r[a] = this._valuesEnd[a]; this._valuesEnd = r; } this._setupProperties(this._object, this._valuesStart, this._valuesEnd, this._valuesStartRepeat, t); } return this; }, e.prototype.startFromCurrentValues = function(e) { return this.start(e, !0); }, e.prototype._setupProperties = function(e, t, n, r, i) { for (var a in n) { var o = e[a], s = Array.isArray(o), c = s ? "array" : typeof o, l = !s && Array.isArray(n[a]); if (!(c === "undefined" || c === "function")) { if (l) { var u = n[a]; if (u.length === 0) continue; for (var d = [o], f = 0, p = u.length; f < p; f += 1) { var m = this._handleRelativeValue(o, u[f]); if (isNaN(m)) { l = !1, console.warn("Found invalid interpolation list. Skipping."); break; } d.push(m); } l && (n[a] = d); } if ((c === "object" || s) && o && !l) { t[a] = s ? [] : {}; var h = o; for (var g in h) t[a][g] = h[g]; r[a] = s ? [] : {}; var u = n[a]; if (!this._isDynamic) { var _ = {}; for (var g in u) _[g] = u[g]; n[a] = u = _; } this._setupProperties(h, t[a], u, r[a], i); } else (t[a] === void 0 || i) && (t[a] = o), s || (t[a] *= 1), l ? r[a] = n[a].slice().reverse() : r[a] = t[a] || 0; } } }, e.prototype.stop = function() { return this._isChainStopped || (this._isChainStopped = !0, this.stopChainedTweens()), this._isPlaying ? (this._isPlaying = !1, this._isPaused = !1, this._onStopCallback && this._onStopCallback(this._object), this) : this; }, e.prototype.end = function() { return this._goToEnd = !0, this.update(this._startTime + this._duration), this; }, e.prototype.pause = function(e) { return e === void 0 && (e = i()), this._isPaused || !this._isPlaying ? this : (this._isPaused = !0, this._pauseStart = e, this); }, e.prototype.resume = function(e) { return e === void 0 && (e = i()), !this._isPaused || !this._isPlaying ? this : (this._isPaused = !1, this._startTime += e - this._pauseStart, this._pauseStart = 0, this); }, e.prototype.stopChainedTweens = function() { for (var e = 0, t = this._chainedTweens.length; e < t; e++) this._chainedTweens[e].stop(); return this; }, e.prototype.group = function(e) { return e ? (e.add(this), this) : (console.warn("tween.group() without args has been removed, use group.add(tween) instead."), this); }, e.prototype.remove = function() { var e; return (e = this._group) == null || e.remove(this), this; }, e.prototype.delay = function(e) { return e === void 0 && (e = 0), this._delayTime = e, this; }, e.prototype.repeat = function(e) { return e === void 0 && (e = 0), this._initialRepeat = e, this._repeat = e, this; }, e.prototype.repeatDelay = function(e) { return this._repeatDelayTime = e, this; }, e.prototype.yoyo = function(e) { return e === void 0 && (e = !1), this._yoyo = e, this; }, e.prototype.easing = function(e) { return e === void 0 && (e = r.Linear.None), this._easingFunction = e, this; }, e.prototype.interpolation = function(e) { return e === void 0 && (e = o.Linear), this._interpolationFunction = e, this; }, e.prototype.chain = function() { var e = [...arguments]; return this._chainedTweens = e, this; }, e.prototype.onStart = function(e) { return this._onStartCallback = e, this; }, e.prototype.onEveryStart = function(e) { return this._onEveryStartCallback = e, this; }, e.prototype.onUpdate = function(e) { return this._onUpdateCallback = e, this; }, e.prototype.onRepeat = function(e) { return this._onRepeatCallback = e, this; }, e.prototype.onComplete = function(e) { return this._onCompleteCallback = e, this; }, e.prototype.onStop = function(e) { return this._onStopCallback = e, this; }, e.prototype.update = function(t, n) { var r = this; if (t === void 0 && (t = i()), n === void 0 && (n = e.autoStartOnUpdate), this._isPaused) return !0; var a; if (!this._goToEnd && !this._isPlaying) if (n) this.start(t, !0); else return !1; if (this._goToEnd = !1, t < this._startTime) return !0; this._onStartCallbackFired === !1 && (this._onStartCallback && this._onStartCallback(this._object), this._onStartCallbackFired = !0), this._onEveryStartCallbackFired === !1 && (this._onEveryStartCallback && this._onEveryStartCallback(this._object), this._onEveryStartCallbackFired = !0); var o = t - this._startTime, s = this._duration + (this._repeatDelayTime ?? this._delayTime), c = this._duration + this._repeat * s, l = function() { if (r._duration === 0 || o > c) return 1; var e = o - Math.trunc(o / s) * s, t = Math.min(e / r._duration, 1); return t === 0 && o === r._duration ? 1 : t; }(), u = this._easingFunction(l); if (this._updateProperties(this._object, this._valuesStart, this._valuesEnd, u), this._onUpdateCallback && this._onUpdateCallback(this._object, l), this._duration === 0 || o >= this._duration) if (this._repeat > 0) { var d = Math.min(Math.trunc((o - this._duration) / s) + 1, this._repeat); for (a in isFinite(this._repeat) && (this._repeat -= d), this._valuesStartRepeat) !this._yoyo && typeof this._valuesEnd[a] == "string" && (this._valuesStartRepeat[a] = this._valuesStartRepeat[a] + parseFloat(this._valuesEnd[a])), this._yoyo && this._swapEndStartRepeatValues(a), this._valuesStart[a] = this._valuesStartRepeat[a]; return this._yoyo && (this._reversed = !this._reversed), this._startTime += s * d, this._onRepeatCallback && this._onRepeatCallback(this._object), this._onEveryStartCallbackFired = !1, !0; } else { this._onCompleteCallback && this._onCompleteCallback(this._object); for (var f = 0, p = this._chainedTweens.length; f < p; f++) this._chainedTweens[f].start(this._startTime + this._duration, !1); return this._isPlaying = !1, !1; } return !0; }, e.prototype._updateProperties = function(e, t, n, r) { for (var i in n) if (t[i] !== void 0) { var a = t[i] || 0, o = n[i]; !Array.isArray(e[i]) && Array.isArray(o) ? e[i] = this._interpolationFunction(o, r) : typeof o == "object" && o ? this._updateProperties(e[i], a, o, r) : (o = this._handleRelativeValue(a, o), typeof o == "number" && (e[i] = a + (o - a) * r)); } }, e.prototype._handleRelativeValue = function(e, t) { return typeof t == "string" ? t.charAt(0) === "+" || t.charAt(0) === "-" ? e + parseFloat(t) : parseFloat(t) : t; }, e.prototype._swapEndStartRepeatValues = function(e) { var t = this._valuesStartRepeat[e], n = this._valuesEnd[e]; typeof n == "string" ? this._valuesStartRepeat[e] = this._valuesStartRepeat[e] + parseFloat(n) : this._valuesStartRepeat[e] = this._valuesEnd[e], this._valuesEnd[e] = t; }, e.autoStartOnUpdate = !1, e; }(); s.nextId; var u = c; u.getAll.bind(u), u.removeAll.bind(u), u.add.bind(u), u.remove.bind(u), u.update.bind(u); //#endregion //#region src/classes/SceneCreator.ts var d = class { scene; renderer; container; cWidth; cHeight; camera; initialCamPos; initialTargetPos; prevCamPos; controls; additionalRenderFn; stopLoop = !1; scale = 1; animating = 0; tweens = new a(); mixers = []; lastFrameTime; physicsWorld; physicsBodies = []; physicsFixedStep = 1 / 60; physicsMaxSubSteps = 3; cannon; raycaster; mouse; selectedObject = null; pickingEnabled = !1; onObjectClick; onObjectHover; onObjectContextMenu; gltfLoader; gltfCache = /* @__PURE__ */ new Map(); clickDragThreshold = 6; pointerDownX = 0; pointerDownY = 0; pointerDragging = !1; constructor(t, n, r, i) { if (t && !(t instanceof HTMLElement)) throw Error("Container must be a valid HTMLElement"); this.scene = new e.Scene(), this.renderer = new e.WebGLRenderer({ antialias: !0, alpha: !0 }), this.raycaster = new e.Raycaster(), this.mouse = new e.Vector2(), n && (this.scale = n), this.cWidth = 0, this.cHeight = 0, this.camera = new e.PerspectiveCamera(50, this.cWidth / this.cHeight, .1 * this.scale, 2e3 * this.scale), r ? this.initialCamPos = r : this.initialCamPos = new e.Vector3(10 * this.scale, 10 * this.scale, 10 * this.scale), this.prevCamPos = new e.Vector3(this.initialCamPos.x + 1, 1, 1), i ? this.initialTargetPos = i : this.initialTargetPos = new e.Vector3(0, 0, 0), this.camera.position.set(this.initialCamPos.x, this.initialCamPos.y, this.initialCamPos.z); var a = this.initialTargetPos; this.camera.lookAt(a), this.resizeListener(), t && this.attachRenderer(t); } addControls(e = {}) { this.controls = new t(this.camera, this.renderer.domElement); let n = { enabled: !0, enableDamping: !0, dampingFactor: .25, maxPolarAngle: Math.PI / 2, maxDistance: 500 * this.scale, minDistance: 0, rotateSpeed: 1, enableZoom: !0, enablePan: !0 }, r = Object.assign(n, e); return Object.assign(this.controls, r), this.controls.target.set(this.initialTargetPos.x, this.initialTargetPos.y, this.initialTargetPos.z), this; } setAdditionalRenderFn(e) { return this.additionalRenderFn = e, this; } resetSizes() { this.container && (this.cWidth = this.container.clientWidth, this.cHeight = this.container.clientHeight); } tween(e, t, n, i) { return this.animating++, new l(e, this.tweens).to(t, n * 1e3).easing(r.Quadratic.Out).onComplete(() => { this.animating--, i && i(); }).start(); } animateModelColor(t, n, r = 2) { let i = this.scene.getObjectByName(t); if (!i) return console.warn(`Object with name "${t}" not found in scene`), this; let a = new e.Color(n); return i.traverse((t) => { t instanceof e.Mesh && this.tween(t.material.color, { r: a.r, g: a.g, b: a.b }, r); }), this; } animateModelOpacity(t, n, r = 2) { let i = this.scene.getObjectByName(t); return i ? (i.traverse((t) => { t instanceof e.Mesh && (t.material.transparent = !0, t.material.needsUpdate = !0, this.tween(t.material, { opacity: n }, r, () => { t.material.needsUpdate = !0; })); }), this) : (console.warn(`Object with name "${t}" not found in scene`), this); } animateModelPosition(e, t, n = 2) { let r = this.scene.getObjectByName(e); return r ? (this.tween(r.position, { x: t.x, y: t.y, z: t.z }, n), this) : (console.warn(`Object with name "${e}" not found in scene`), this); } attachRenderer(e) { return this.container = e, this.resetSizes(), this.camera.aspect = this.cWidth / this.cHeight, this.camera.updateProjectionMatrix(), this.renderer.setSize(this.cWidth, this.cHeight), this.container.appendChild(this.renderer.domElement), this.scene.updateMatrixWorld(!0), this.startRenderLoop(), this; } addLighting(t = {}) { let { hemisphere: n = { sky: 12375295, ground: 3812374, intensity: 1.4 }, key: r = {}, fill: i = { color: 10404863, intensity: .6, position: new e.Vector3(-10, 8, -8) }, shadows: a = !0, shadowArea: o = 16, shadowMapSize: s = 2048, toneMapping: c = !0, exposure: l = 1.05 } = t; if (n) { let t = new e.HemisphereLight(n.sky ?? 12375295, n.ground ?? 3812374, n.intensity ?? 1.4); t.position.set(0, 20, 0), this.scene.add(t); } let u = new e.DirectionalLight(r.color ?? 16773853, r.intensity ?? 2.6); if (u.position.copy(r.position ?? new e.Vector3(8, 16, 10)), this.scene.add(u), i) { let t = new e.DirectionalLight(i.color ?? 10404863, i.intensity ?? .6); t.position.copy(i.position ?? new e.Vector3(-10, 8, -8)), this.scene.add(t); } if (a) { this.renderer.shadowMap.enabled = !0, this.renderer.shadowMap.type = e.PCFSoftShadowMap, u.castShadow = !0, u.shadow.mapSize.set(s, s); let t = u.shadow.camera; t.near = 1, t.far = 60, t.left = -o, t.right = o, t.top = o, t.bottom = -o, t.updateProjectionMatrix(), u.shadow.bias = -4e-4; } return c && (this.renderer.toneMapping = e.ACESFilmicToneMapping, this.renderer.toneMappingExposure = l), this; } applyShadows(e = !0, t = !0) { return this.scene.traverse((n) => { n.isMesh && (n.castShadow = e, n.receiveShadow = t); }), this; } addSkybox(t, n = "#B2FFFF", r) { let i = new e.SphereGeometry(1e3 * this.scale, 60, 60); i.scale(-1, 1, 1); let a; t ? (e.TextureLoader.prototype.crossOrigin = "anonymous", a = new e.MeshBasicMaterial({ map: new e.TextureLoader().load(t) })) : a = new e.MeshStandardMaterial({ color: n }); let o = new e.Mesh(i, a); return o.name = r || "skybox", this.scene.add(o), this; } resizeListener() { this.resetSizes(), window.addEventListener("resize", () => { this.resetSizes(); let e = this.camera, t = this.renderer; e.aspect = this.cWidth / this.cHeight, e.updateProjectionMatrix(), t.setSize(this.cWidth * 1, this.cHeight * 1), e.position.x = e.position.x + .001; }, !1); } stopRenderLoop() { return this.stopLoop = !0, this; } startRenderLoop() { return this.stopLoop = !1, this.renderLoop(), this; } renderLoop() { if (this.stopLoop) return; requestAnimationFrame(this.renderLoop.bind(this)); let e = performance.now(), t = this.lastFrameTime === void 0 ? 0 : (e - this.lastFrameTime) / 1e3; if (this.lastFrameTime = e, this.tweens.update(), this.mixers.length) for (let e of this.mixers) e.update(t); if (this.physicsWorld) { this.physicsWorld.step(this.physicsFixedStep, t, this.physicsMaxSubSteps); for (let { mesh: e, body: t } of this.physicsBodies) e.position.set(t.position.x, t.position.y, t.position.z), e.quaternion.set(t.quaternion.x, t.quaternion.y, t.quaternion.z, t.quaternion.w); } let n = this.scene, r = this.renderer, i = this.camera; (i.position.x !== this.prevCamPos.x || i.position.y !== this.prevCamPos.y || i.position.z !== this.prevCamPos.z || this.animating || this.mixers.length || this.physicsWorld || this.additionalRenderFn) && r.render(n, i), this.prevCamPos = this.camera.position.clone(), this.additionalRenderFn && this.additionalRenderFn(), this.controls && this.controls.update(); } resetCameraPosition() { return this.moveCamera(this.initialCamPos, this.initialTargetPos), this; } moveCamera(e, t, n) { let r = this.camera, i; return this.controls && (i = this.controls.enabled, this.controls.enabled = !1), this.tween(r.position, { x: e.x, y: e.y, z: e.z }, 3, () => { this.controls && typeof i == "boolean" && (this.controls.enabled = i), typeof n == "function" && n(); }), t && this.controls && this.tween(this.controls.target, { x: t.x, y: t.y, z: t.z }, 3), this; } loadModel(t, n) { return t ? (n ||= new e.ObjectLoader(), n.loadAsync(t).then(((e) => { let t = e; return this.scene.add(t), t; })).catch((e) => { throw console.error(`Failed to load model from "${t}":`, e), e; })) : Promise.reject(/* @__PURE__ */ Error("URL is required")); } loadGLTF(e) { this.gltfLoader ||= new n(); let t = this.gltfCache.get(e); return t || (t = this.gltfLoader.loadAsync(e).then((e) => e.scene), t.catch(() => this.gltfCache.delete(e)), this.gltfCache.set(e, t)), t.then((e) => e.clone(!0)); } addMixer(e) { return this.mixers.includes(e) || this.mixers.push(e), this; } removeMixer(e) { let t = this.mixers.indexOf(e); return t !== -1 && this.mixers.splice(t, 1), this; } async loadAnimatedModel(t, r = {}) { let { add: i = !0, shadows: a = !0, autoplay: o } = r; this.gltfLoader ||= new n(); let s = await this.gltfLoader.loadAsync(t), c = s.scene; a && c.traverse((e) => { e.isMesh && (e.castShadow = !0, e.receiveShadow = !0); }), i && this.scene.add(c); let l = new e.AnimationMixer(c); this.addMixer(l); let u = {}; for (let e of s.animations) u[e.name] = l.clipAction(e); let d = null, f = (n, r = {}) => { let { fade: i = .3, loop: a = !0, clampWhenFinished: o = !0 } = r, s = u[n]; return s ? s === d ? s : (s.loop = a ? e.LoopRepeat : e.LoopOnce, s.clampWhenFinished = o, d && d.fadeOut(i), s.reset().setEffectiveTimeScale(1).setEffectiveWeight(1).fadeIn(i).play(), d = s, s) : (console.warn(`Animation clip "${n}" not found on model "${t}"`), null); }, p = (e = .3) => { d &&= (d.fadeOut(e), null); }, m = s.animations.map((e) => e.name); return o !== !1 && m.length && f(typeof o == "string" ? o : m[0], { fade: 0 }), { model: c, animations: s.animations, names: m, mixer: l, actions: u, play: f, stop: p }; } async enablePhysics(t = {}) { if (this.physicsWorld) return this; let n = this.cannon ??= await import("cannon-es"), r = t.gravity ?? new e.Vector3(0, -9.82, 0), i = Array.isArray(r) ? new n.Vec3(r[0], r[1], r[2]) : new n.Vec3(r.x, r.y, r.z), a = new n.World({ gravity: i }); return a.allowSleep = t.allowSleep ?? !0, a.defaultContactMaterial.restitution = t.restitution ?? .3, a.defaultContactMaterial.friction = t.friction ?? .4, this.physicsWorld = a, this; } addBody(e, t = {}) { if (!this.physicsWorld || !this.cannon) throw Error("Call (and await) enablePhysics() before addBody()."); let n = this.cannon, { mass: r = 1, shape: i = "box", linearDamping: a = .01, angularDamping: o = .01 } = t, s = new n.Body({ mass: r, shape: this.shapeFromMesh(e, i) }); return s.position.set(e.position.x, e.position.y, e.position.z), s.quaternion.set(e.quaternion.x, e.quaternion.y, e.quaternion.z, e.quaternion.w), s.linearDamping = a, s.angularDamping = o, this.physicsWorld.addBody(s), r > 0 && this.physicsBodies.push({ mesh: e, body: s }), s; } addGround(e = 0) { if (!this.physicsWorld || !this.cannon) throw Error("Call (and await) enablePhysics() before addGround()."); let t = this.cannon, n = new t.Body({ mass: 0, shape: new t.Plane() }); return n.quaternion.setFromEuler(-Math.PI / 2, 0, 0), n.position.set(0, e, 0), this.physicsWorld.addBody(n), n; } removeBody(e) { return this.physicsWorld && this.physicsWorld.removeBody(e), this.physicsBodies = this.physicsBodies.filter((t) => t.body !== e), this; } shapeFromMesh(t, n) { let r = this.cannon, i = t.geometry, a = t.scale; if (n === "sphere") { i && !i.boundingSphere && i.computeBoundingSphere(); let e = i?.boundingSphere?.radius ?? .5; return new r.Sphere(e * Math.max(a.x, a.y, a.z)); } let o = new e.Vector3(1, 1, 1); return i && (i.boundingBox || i.computeBoundingBox(), i.boundingBox?.getSize(o)), new r.Box(new r.Vec3(o.x * a.x / 2, o.y * a.y / 2, o.z * a.z / 2)); } enablePicking(e, t, n) { if (this.pickingEnabled = !0, this.onObjectClick = e, this.onObjectHover = t, this.onObjectContextMenu = n, this.onObjectHover = t, !this.container) return console.warn("Container not set. Picking requires attachRenderer to be called first."), this; this.container.addEventListener("mousemove", (e) => { let t = this.renderer.domElement.getBoundingClientRect(); this.mouse.x = (e.clientX - t.left) / t.width * 2 - 1, this.mouse.y = -((e.clientY - t.top) / t.height) * 2 + 1, this.raycaster.setFromCamera(this.mouse, this.camera); let n = this.raycaster.intersectObjects(this.scene.children, !0); if (n.length > 0) { let e = n[0].object; e !== this.selectedObject && (this.selectedObject = e, this.onObjectHover && this.onObjectHover(e)); } else this.selectedObject !== null && (this.selectedObject = null, this.onObjectHover && this.onObjectHover(null)); }), this.container.addEventListener("pointerdown", (e) => { this.pointerDownX = e.clientX, this.pointerDownY = e.clientY, this.pointerDragging = !1; }), this.container.addEventListener("pointermove", (e) => { if (this.pointerDragging) return; let t = e.clientX - this.pointerDownX, n = e.clientY - this.pointerDownY; Math.hypot(t, n) > this.clickDragThreshold && (this.pointerDragging = !0); }), this.container.addEventListener("click", () => { this.pointerDragging || this.selectedObject && this.onObjectClick && this.onObjectClick(this.selectedObject); }); let r = 0, i = 0, a = !1; return this.container.addEventListener("touchstart", (e) => { e.touches.length === 1 ? (r = e.touches[0].clientX, i = e.touches[0].clientY, a = !1) : a = !0; }), this.container.addEventListener("touchmove", (e) => { if (a || e.touches.length !== 1) return; let t = e.touches[0].clientX - r, n = e.touches[0].clientY - i; Math.hypot(t, n) > this.clickDragThreshold && (a = !0); }), this.container.addEventListener("touchend", (e) => { if (a) return; let t = e.changedTouches[0]; if (!t) return; let n = this.renderer.domElement.getBoundingClientRect(); this.mouse.x = (t.clientX - n.left) / n.width * 2 - 1, this.mouse.y = -((t.clientY - n.top) / n.height) * 2 + 1, this.raycaster.setFromCamera(this.mouse, this.camera); let r = this.raycaster.intersectObjects(this.scene.children, !0); r.length > 0 && this.onObjectClick && this.onObjectClick(r[0].object); }), this.container.addEventListener("contextmenu", (e) => { e.preventDefault(), this.selectedObject && this.onObjectContextMenu && this.onObjectContextMenu(this.selectedObject); }), this; } disablePicking() { return this.pickingEnabled = !1, this.selectedObject = null, this.onObjectClick = void 0, this.onObjectHover = void 0, this; } getSelectedObject() { return this.selectedObject; } pickAt(t, n) { this.mouse.set(t, n), this.raycaster.setFromCamera(this.mouse, this.camera); let r = this.raycaster.intersectObjects(this.scene.children, !0); if (r.length > 0) { let t = r[0]; return { object: t.object, distance: t.distance, point: t.point, normal: t.normal || new e.Vector3(0, 1, 0), uv: t.uv || void 0 }; } return null; } dispose() { this.stopRenderLoop(), this.tweens.removeAll(), this.mixers.forEach((e) => e.stopAllAction()), this.mixers = [], this.physicsBodies = [], this.physicsWorld = void 0, this.animating = 0, this.renderer.dispose(), this.scene.clear(), this.container && this.renderer.domElement.parentNode === this.container && this.container.removeChild(this.renderer.domElement); } }; //#endregion export { d as SceneCreator };