3d-scene-creator
Version:
A simple utility to create and manage 3D scenes
707 lines (706 loc) • 30.9 kB
JavaScript
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 };