@tomorrowevening/hermes
Version:
An extendable set of Web Tools controlled via a separate window for non-intereference with content.
891 lines (890 loc) • 43.5 kB
JavaScript
import { jsxs as b, jsx as h, Fragment as v } from "react/jsx-runtime";
import { Component as O, createRef as C } from "react";
import { Group as L, AxesHelper as D, MeshBasicMaterial as x, Raycaster as R, Vector2 as w, Scene as U, Sphere as B, Box3 as F, Spherical as G, Matrix4 as P, Quaternion as z, Vector4 as _, Vector3 as c, WebGPURenderer as A, MeshNormalNodeMaterial as Q, PerspectiveCamera as E, OrthographicCamera as M, CameraHelper as q, Mesh as K, SkinnedMesh as N, SpotLightHelper as $, PointLightHelper as Y, HemisphereLightHelper as J, DirectionalLightHelper as X } from "three/webgpu";
import { WebGLRenderer as T } from "three";
import { RectAreaLightHelper as j } from "three/examples/jsm/helpers/RectAreaLightHelper.js";
import { OrbitControls as Z } from "three/examples/jsm/controls/OrbitControls.js";
import { mapLinear as g, degToRad as H } from "three/src/math/MathUtils.js";
import p from "camera-controls";
import { ToolEvents as o } from "../../core/remote/RemoteThree.js";
import u, { Dropdown as V } from "./CameraWindow.js";
import ee from "./InfiniteGridHelper.js";
import te from "./InfiniteGridHelperGPU.js";
/* empty css */
import S from "../sidePanel/DebugData.js";
import { InspectTransform as ie } from "../sidePanel/inspector/utils/InspectTransform.js";
import I from "./Toggle.js";
import se from "./DepthNodeMaterial.js";
import re from "./UVNodeMaterial.js";
import ae from "../tools/splineEditor/index.js";
import m from "../tools/Transform.js";
import { mix as ne } from "../../utils/math.js";
import { dispose as y } from "../../utils/three.js";
const W = [
"Single",
"Side by Side",
"Stacked",
"Quad"
], oe = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAYAAACM/rhtAAAACXBIWXMAAAsTAAALEwEAmpwYAAAC60lEQVRYhe2YT4hNcRTHP48xpmYaNAvRyEQxEhnKQljYsRm9/EmSyJiMFAsMZWNhJYoNIUVJ2VGiyb9ZzIpMpkQSahbGv9GMYWrM+1rc2zjvzvvdd+99rzdvMd+6de75nd+5387vnN/v/G5KEuWMKRNNIB8mCRaKiiL5qQb2ApuBuUAV0Ad0AJeB3sSeJRX6LJbULTf6JTUn9Z+KWMUpPyp/Avoa4CNQZ3Sj/lNpdL/xottR7AjOkHRUUpekN5I6JbVLavDH75lIfZN0UFKTpCWS0pJem/HeJBEMG6yV1ONYtgFJbZJ+GF1jDh+zJb03NuliEuwMkMo4yErS2RA/LcbuYVyCrm1mA7Dal/8Cu4FG4JD/HsTTkCy6a+SVMTPQuc1sBKb78nHghi+/A+YBxwL2lbhRY+ThuARdEVxu5JdGFvACr0otdoZ8Y4+Rn0Sn5sFFsMvI6YB9MzA1YJ8mN8k1wAHzfj4uQVdyrpI0aJL7oqTtkq4FiqPLyCOSbktqlbRL0jlJQ2b8QdwCUZ4qvhRStZL0XFK1pMd57CRvq5mfhKBriRfiFUMY6oD7eOdwPlQAN4G10dfWg+uouwXsiOssAj4AC+JMcEWwvnAuOTEr7gTXPmg34zagOwkbIIOXAo9CbDYBrcBXYN+4UUdy2sRflyS5zVNlfPX7ugpJW5V9nI7mmh+lYU0lCZ2B3TOnAVuAk0BTwC5nuhWro46KauBOQJch5OpRaoIW34GreGf+YZdRqS9NAj4Bp4ClQDvwOWxCqSM4ADQEdKE5XvbXzlITrAVe4TW+M6NMKDXBFLAMuAD0ACfIc7pMZBXXA2cY3/xmodQRHAL2A2+NLtj8ZiEKwUL/z2WMPAJcAVYALWSf8dZuDFGWeBHwKxm3sWYhiGG8Tfo6sA2vSfiSy4GrH3wGrDcfKSSKKf6v1E9yF0XK9Q1XBPuMXMw8HXTonQFwETwNzMFr64v1jzgFHIk9ybHEZYPJo65QlD3Bf2/Q/eaHPiSWAAAAAElFTkSuQmCC", he = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAYAAACM/rhtAAAACXBIWXMAAAsTAAALEwEAmpwYAAAETklEQVRYhe2YXYhVVRTHf3d0/JhyUrMpFbImM+whSa3Mynww+4AeIgiKoozooQ+KyMyXIAujF6OXqHyI6iEKKYgIP/owsjSFqCkprdDUTEcjbWZ0HHV+Pex1ucfb9Z57Z9REXHA4Z++99l7/s/ZZ/7X2KaiczNLwfwPIk9MA+yunNMAG4DHgV+BvoB3YFff2TPstYEyfrajVroI6Sr1GvahsbJC63HzZq04pmztSvU5tVRuqYRiYg78JeBR4HPg5ntdmxovz9wJfA3uAxuibDLQCA+IqynnAQuBuYCnwMLC1rx48U12U8cZqdWqMDcx4cI16qTpYbVKHqa+ovWUeHKsujn7VL9ULq2HI+wY7gVeBJdGeFu3J4ZUiy/cAXeHRRuAQsC/GC0B3eG4BcH/0tUV7czUAeVsMsBF4Kha9HZgCvAncAuwPnXHA0wFuGLANuDrGuoGDwFxgTqzzQ7RX5FrP2eLsdb76vnogtme6+nE8H1YPlQXHwbi3q9eqn0e7Tb25VrsF68vFY0lb1AGcDTwItGQ8tQM4AIwGmqP/EPAu8A0wElgJfFGzxTo8mKWXu9Rd4ZF96jvqjSbauEC9Sn1O3R463epLaku99voCsFXdkAH3kNpcQW+IiT/bQne3eufxBtioPqP2mKhioXpWzpxb1T8C5Ifh4ZptVoriRmAIKdrI3Lujf3bobAPeJpF0NVkKrAFuI9HTOBIxnxFrF4OgQKKr/dnJlQA+AcwABgOHA4zAamA5cE7orQL+zAEHiWI+A2bFC7aSguheYCgpqAphZz3wSB7AecCICv2TgH8oFRib4gVqka0k0h8CXAxMBW6ooDejFoCvA9OBQUAvKWM0kDz4XUZvNKXtyZMWUl7vBbaQXq457PeEziDgx/KJlQAuIPFdMcEXv5OdYWBP9M8k8VpnDQCvB4aTSrCNJB5cSdrW3tBpIJVnR0qdUTxAfdGUOVTnmwqEanNmqltC/9OgqePKg5eom8PgdvW+oJ9Kupera0O3U50TL3lcAaI+oHaF4R3qa6Z822wq0Saq89T1lmSxOrxeW/Xm4uGkiqQDmECKuKHxHW0HdpMiewSpvGqKeR8Ay0iBsYIjg6261PE2o9Q3Yqv+MqWxO9R1Hl12qs+q49Vlppy82pSr+5xJKsko4AXgHlJ0t5NIeg3wLXAlqZhtIUV8J6nmWwX8QiLjdhL5TwMWkY4R6/rrwYKlMr1Y321QZ/vfwBijPqm+rF4Wc7PjE9QllurGVeoV5gRNHsAm9fnMom3qrKPoTjKdMXaYyrFygEWQ72XWW24qhPu8xcUc2UU6O8wFPjmK7jhgPHAuMJGUGQ6U6WwE5sfzTXGvei7KA9hFOiR9D/wOfFVFt4dS2tpfRe83Ur7/CPiJakfOGgBCypubatArl2r8VfOax/LfzABKtePAzHO/5FgC7KBEzB2kOrDfUm8mOeFySv9+OyFyGmB/5aQH+C9BVKmVCNuMZgAAAABJRU5ErkJggg==", le = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABUAAAAVCAYAAACpF6WWAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAQ0lEQVQ4jWP8////fwYqAxYozUhFM/8zkaKYWIWkGEq0b0ZdSjQY5i79TyWagRGaTUdzFEEw6lLqGzqwLoVVJ1StpwA9sBwbUqAh5gAAAABJRU5ErkJggg==";
class k extends O {
static instance = null;
scene;
renderer;
currentScene;
scenes = /* @__PURE__ */ new Map();
cameras = /* @__PURE__ */ new Map();
controls = /* @__PURE__ */ new Map();
currentCamera;
currentWindow;
// RefObject to one of the "windows"
helpersContainer = new L();
grid;
cameraHelpers = /* @__PURE__ */ new Map();
lightHelpers = /* @__PURE__ */ new Map();
interactionHelper = new D(25);
currentTransform;
// Tools
splineEditor;
// Override Materials
depthMaterial;
normalsMaterial;
uvMaterial;
wireframeMaterial = new x({
opacity: 0.33,
transparent: !0,
wireframe: !0
});
// Playback
playing = !1;
rafID = -1;
cameraControlsRafID = -1;
width = 0;
height = 0;
// Windows
tlCam = null;
trCam = null;
blCam = null;
brCam = null;
tlRender = "Renderer";
trRender = "Renderer";
blRender = "Renderer";
brRender = "Renderer";
cameraVisibility = !0;
lightVisibility = !0;
gridVisibility = !0;
rendererReady = !1;
// Interactions
selectedItem = void 0;
cameraControlsStartTime = 0;
cameraControlsLastTime = 0;
debugCamera;
raycaster = new R();
pointer = new w();
cameraControls = void 0;
// References
canvasRef;
containerRef;
tlWindow;
trWindow;
blWindow;
brWindow;
editorCameras = [
"Top",
"Bottom",
"Left",
"Right",
"Front",
"Back",
"Orthographic",
"UI",
"Debug"
];
constructor(i) {
super(i), this.props.three.addEventListener(o.ADD_RENDERER, this.setupRenderer), this.scene = new U(), this.scene.name = this.scene.uuid = "", this.canvasRef = C(), this.containerRef = C(), this.tlWindow = C(), this.trWindow = C(), this.blWindow = C(), this.brWindow = C();
const e = i.three.name, t = localStorage, s = t.getItem(`${e}_mode`);
this.state = {
mode: s !== null ? s : "Single",
modeOpen: !1,
renderModeOpen: !1,
interactionMode: "Orbit",
interactionModeOpen: !1,
lastUpdate: Date.now()
}, t.setItem(`${e}_mode`, this.state.mode), t.setItem(`${e}_tlCam`, t.getItem(`${e}_tlCam`) !== null ? t.getItem(`${e}_tlCam`) : "Debug"), t.setItem(`${e}_trCam`, t.getItem(`${e}_trCam`) !== null ? t.getItem(`${e}_trCam`) : "Orthographic"), t.setItem(`${e}_blCam`, t.getItem(`${e}_blCam`) !== null ? t.getItem(`${e}_blCam`) : "Front"), t.setItem(`${e}_brCam`, t.getItem(`${e}_brCam`) !== null ? t.getItem(`${e}_brCam`) : "Top"), t.setItem(`${e}_tlRender`, t.getItem(`${e}_tlRender`) !== null ? t.getItem(`${e}_tlRender`) : "Renderer"), t.setItem(`${e}_trRender`, t.getItem(`${e}_trRender`) !== null ? t.getItem(`${e}_trRender`) : "Renderer"), t.setItem(`${e}_blRender`, t.getItem(`${e}_blRender`) !== null ? t.getItem(`${e}_blRender`) : "Renderer"), t.setItem(`${e}_brRender`, t.getItem(`${e}_brRender`) !== null ? t.getItem(`${e}_brRender`) : "Renderer");
const a = {
Vector2: w,
Vector3: c,
Vector4: _,
Quaternion: z,
Matrix4: P,
Spherical: G,
Box3: F,
Sphere: B,
Raycaster: R
};
p.install({ THREE: a });
const r = localStorage.getItem(this.expandedCameraVisibility);
r !== null && (this.cameraVisibility = r === "open"), this.saveExpandedCameraVisibility();
const n = localStorage.getItem(this.expandedLightVisibility);
n !== null && (this.lightVisibility = n === "open"), this.saveExpandedLightVisibility();
const d = localStorage.getItem(this.expandedGridVisibility);
d !== null && (this.gridVisibility = d === "open"), this.grid && (this.grid.visible = this.gridVisibility), this.saveExpandedGridVisibility(), k.instance = this;
}
componentDidMount() {
this.setupScene(), this.setupTools(), this.enable(), this.assignControls(), this.resize(), this.play(), m.instance.setApp(this.props.three), m.instance.activeCamera = this.debugCamera, this.props.three.requestRenderer();
}
componentDidUpdate(i, e, t) {
e.mode !== this.state.mode && (this.assignControls(), this.resize());
}
componentWillUnmount() {
this.disable(), this.clear(), S.removeEditorGroup("View Settings");
}
render() {
const i = [];
return this.cameras.forEach((e, t) => {
i.push(t);
}), /* @__PURE__ */ b("div", { className: "multiview", children: [
/* @__PURE__ */ h("canvas", { ref: this.canvasRef }),
/* @__PURE__ */ b("div", { className: `cameras ${this.state.mode === "Single" || this.state.mode === "Stacked" ? "single" : ""}`, ref: this.containerRef, children: [
this.state.mode === "Single" && /* @__PURE__ */ h(v, { children: /* @__PURE__ */ h(
u,
{
name: "tl",
camera: this.tlCam,
options: i,
ref: this.tlWindow,
onSelectCamera: (e) => {
this.controls.get(this.tlCam.name)?.dispose();
const t = this.cameras.get(e);
t !== void 0 && (this.clearCamera(this.tlCam), this.tlCam = t, localStorage.setItem(`${this.appID}_tlCam`, t.name), this.createControls(t, this.tlWindow.current));
},
onSelectRenderMode: (e) => {
this.tlRender = e, localStorage.setItem(`${this.appID}_tlRender`, e);
}
}
) }),
(this.state.mode === "Side by Side" || this.state.mode === "Stacked") && /* @__PURE__ */ b(v, { children: [
/* @__PURE__ */ h(
u,
{
name: "tl",
camera: this.tlCam,
options: i,
ref: this.tlWindow,
onSelectCamera: (e) => {
this.controls.get(this.tlCam.name)?.dispose();
const t = this.cameras.get(e);
t !== void 0 && (this.clearCamera(this.tlCam), this.tlCam = t, localStorage.setItem(`${this.appID}_tlCam`, t.name), this.createControls(t, this.tlWindow.current));
},
onSelectRenderMode: (e) => {
this.tlRender = e, localStorage.setItem(`${this.appID}_tlRender`, e);
}
}
),
/* @__PURE__ */ h(
u,
{
name: "tr",
camera: this.trCam,
options: i,
ref: this.trWindow,
onSelectCamera: (e) => {
this.controls.get(this.trCam.name)?.dispose();
const t = this.cameras.get(e);
t !== void 0 && (this.clearCamera(this.trCam), this.trCam = t, localStorage.setItem(`${this.appID}_trCam`, t.name), this.createControls(t, this.trWindow.current));
},
onSelectRenderMode: (e) => {
this.trRender = e, localStorage.setItem(`${this.appID}_trRender`, e);
}
}
)
] }),
this.state.mode === "Quad" && /* @__PURE__ */ b(v, { children: [
/* @__PURE__ */ h(
u,
{
name: "tl",
camera: this.tlCam,
options: i,
ref: this.tlWindow,
onSelectCamera: (e) => {
this.controls.get(this.tlCam.name)?.dispose();
const t = this.cameras.get(e);
t !== void 0 && (this.clearCamera(this.tlCam), this.tlCam = t, localStorage.setItem(`${this.appID}_tlCam`, t.name), this.createControls(t, this.tlWindow.current));
},
onSelectRenderMode: (e) => {
this.tlRender = e, localStorage.setItem(`${this.appID}_tlRender`, e);
}
}
),
/* @__PURE__ */ h(
u,
{
name: "tr",
camera: this.trCam,
options: i,
ref: this.trWindow,
onSelectCamera: (e) => {
this.controls.get(this.trCam.name)?.dispose();
const t = this.cameras.get(e);
t !== void 0 && (this.clearCamera(this.trCam), this.trCam = t, localStorage.setItem(`${this.appID}_trCam`, t.name), this.createControls(t, this.trWindow.current));
},
onSelectRenderMode: (e) => {
this.trRender = e, localStorage.setItem(`${this.appID}_trRender`, e);
}
}
),
/* @__PURE__ */ h(
u,
{
name: "bl",
camera: this.blCam,
options: i,
ref: this.blWindow,
onSelectCamera: (e) => {
this.controls.get(this.blCam.name)?.dispose();
const t = this.cameras.get(e);
t !== void 0 && (this.clearCamera(this.blCam), this.blCam = t, localStorage.setItem(`${this.appID}_blCam`, t.name), this.createControls(t, this.blWindow.current));
},
onSelectRenderMode: (e) => {
this.blRender = e, localStorage.setItem(`${this.appID}_blRender`, e);
}
}
),
/* @__PURE__ */ h(
u,
{
name: "br",
camera: this.brCam,
options: i,
ref: this.brWindow,
onSelectCamera: (e) => {
this.controls.get(this.brCam.name)?.dispose();
const t = this.cameras.get(e);
t !== void 0 && (this.clearCamera(this.brCam), this.brCam = t, localStorage.setItem(`${this.appID}_brCam`, t.name), this.createControls(t, this.brWindow.current));
},
onSelectRenderMode: (e) => {
this.brRender = e, localStorage.setItem(`${this.appID}_brRender`, e);
}
}
)
] })
] }),
/* @__PURE__ */ b("div", { className: "settings", children: [
/* @__PURE__ */ h(
V,
{
title: "View",
index: W.indexOf(this.state.mode),
options: W,
onSelect: (e) => {
e !== this.state.mode && (this.killControls(), this.setState({ mode: e }), localStorage.setItem(`${this.appID}_mode`, e));
},
open: this.state.modeOpen,
onToggle: (e) => {
this.setState({
modeOpen: e,
renderModeOpen: !1,
interactionModeOpen: !1
});
}
}
),
/* @__PURE__ */ h(
V,
{
title: "Interact",
index: this.state.interactionMode === "Orbit" ? 0 : 1,
options: [
"Orbit Mode",
"Selection Mode"
],
onSelect: (e) => {
this.interactionHelper.visible = e === "Selection Mode", this.setState({ interactionMode: this.interactionHelper.visible ? "Selection" : "Orbit" });
},
open: this.state.interactionModeOpen,
onToggle: (e) => {
this.setState({
modeOpen: !1,
renderModeOpen: !1,
interactionModeOpen: e
});
}
}
),
/* @__PURE__ */ h(
I,
{
name: "cameraHelper",
icon: oe,
selected: this.cameraVisibility,
height: 24,
top: 2,
onClick: (e) => {
if (this.cameraVisibility = e, this.saveExpandedCameraVisibility(), this.cameraHelpers.forEach((t) => {
t.visible = e;
}), this.selectedItem !== void 0 && !e) {
const t = this.cameraHelpers.get(this.selectedItem.name);
t !== void 0 && (t.visible = !0);
}
}
}
),
/* @__PURE__ */ h(
I,
{
name: "lightHelper",
icon: he,
selected: this.lightVisibility,
height: 24,
top: 4,
onClick: (e) => {
if (this.lightVisibility = e, this.saveExpandedLightVisibility(), this.lightHelpers.forEach((t) => {
t.visible = e;
}), this.selectedItem !== void 0 && !e && this.selectedItem.isLight === !0) {
const t = this.lightHelpers.get(this.selectedItem.name);
t !== void 0 && (t.visible = !0);
}
}
}
),
/* @__PURE__ */ h(
I,
{
name: "gridHelper",
icon: le,
selected: this.gridVisibility,
height: 21,
width: 21,
onClick: (e) => {
this.setGridVisibility(e);
}
}
)
] }, this.state.lastUpdate)
] });
}
// Setup
setupRenderer = (i) => {
const e = i.value;
if (this.renderer) {
if (this.renderer instanceof T && e.type === "WebGLRenderer" || this.renderer instanceof A && e.type === "WebGPURenderer") return;
this.renderer.dispose();
}
this.rendererReady = !1;
const t = this.canvasRef.current;
if (t === null) {
setTimeout(() => this.props.three.requestRenderer(), 100);
return;
}
this.props.three.canvas = t, e.type === "WebGLRenderer" ? (this.renderer = new T({
canvas: t,
stencil: !1
}), this.grid && (this.scene.remove(this.grid), y(this.grid)), this.grid = new ee(), this.scene.add(this.grid), this.rendererReady = !0) : e.type === "WebGPURenderer" && (this.renderer = new A({
canvas: t,
stencil: !1
}), this.grid && (this.scene.remove(this.grid), y(this.grid)), this.grid = new te(), this.scene.add(this.grid)), this.renderer && (this.renderer.autoClear = !1, this.renderer.shadowMap.enabled = !0, this.renderer.setClearColor(0), this.renderer.setPixelRatio(devicePixelRatio), this.renderer.setScissorTest(!0), this.resize(), this.props.three.renderer = this.renderer, this.depthMaterial?.dispose(), this.normalsMaterial?.dispose(), this.uvMaterial?.dispose(), this.depthMaterial = new se(), this.normalsMaterial = new Q(), this.uvMaterial = new re(), e.type === "WebGPURenderer" ? this.renderer.init().then(() => {
this.rendererReady = !0, this.props.three.requestScene();
}) : this.props.three.requestScene());
};
setupScene() {
this.helpersContainer.name = "helpers", this.scene.add(this.helpersContainer), this.interactionHelper.name = "interactionHelper", this.interactionHelper.visible = !1, this.helpersContainer.add(this.interactionHelper);
const i = (a, r) => {
const n = new M(-100, 100, 100, -100, 0, 3e3);
return n.name = a, n.position.copy(r), n.lookAt(0, 0, 0), this.cameras.set(a, n), n;
}, e = 1e3;
i("Top", new c(0, e, 0)), i("Bottom", new c(0, -e, 0)), i("Left", new c(-e, 0, 0)), i("Right", new c(e, 0, 0)), i("Front", new c(0, 0, e)), i("Back", new c(0, 0, -e)), i("Orthographic", new c(e, e, e)), i("UI", new c()), this.debugCamera = new E(60, 1, 0.01, 3e3), this.debugCamera.name = "Debug", this.debugCamera.position.set(300, 300, 300), this.debugCamera.lookAt(0, 0, 0), this.cameras.set("Debug", this.debugCamera), this.currentCamera = this.debugCamera;
const t = localStorage, s = this.props.three.name;
this.tlCam = this.cameras.get(t.getItem(`${s}_tlCam`)), this.trCam = this.cameras.get(t.getItem(`${s}_trCam`)), this.blCam = this.cameras.get(t.getItem(`${s}_blCam`)), this.brCam = this.cameras.get(t.getItem(`${s}_brCam`)), this.tlCam === void 0 && (this.tlCam = this.cameras.get("Debug")), this.trCam === void 0 && (this.trCam = this.cameras.get("Orthographic")), this.blCam === void 0 && (this.blCam = this.cameras.get("Front")), this.brCam === void 0 && (this.brCam = this.cameras.get("Top")), S.addEditorGroup({
title: "Editor",
items: [
{
type: "button",
prop: "Hide All Transforms"
},
{
type: "button",
prop: "Resize"
}
],
onUpdate: (a, r) => {
switch (a) {
case "Hide All Transforms":
m.instance.hide();
break;
case "Resize":
this.resize();
break;
}
},
subgroups: [
{
title: "Debug Camera",
items: [
{
type: "range",
prop: "Near",
step: 1e-4,
min: 1e-3,
max: 1e3,
value: this.debugCamera.near
},
{
type: "range",
prop: "Far",
step: 1e-3,
min: 1e-3,
max: 1e4,
value: this.debugCamera.far
}
],
onUpdate: (a, r) => {
switch (a) {
case "Near":
this.debugCamera.near = r, this.debugCamera.updateProjectionMatrix();
break;
case "Far":
this.debugCamera.far = r, this.debugCamera.updateProjectionMatrix();
break;
}
}
},
{
title: "Grid",
items: [
{
type: "number",
prop: "Position",
value: 0
},
{
type: "color",
prop: "Color",
value: "#FFFFFF"
},
{
type: "range",
prop: "Grid Opacity",
value: 0.25,
min: 0,
max: 1,
step: 0.01
},
{
type: "range",
prop: "Subgrid Opacity",
value: 0.15,
min: 0,
max: 1,
step: 0.01
}
],
onUpdate: (a, r) => {
switch (a) {
case "Position":
this.grid && (this.grid.position.y = r);
break;
case "Color":
this.grid && this.grid.color.setStyle(r);
break;
case "Grid Opacity":
this.grid && (this.grid.gridOpacity = r);
break;
case "Subgrid Opacity":
this.grid && (this.grid.subgridOpacity = r);
break;
}
}
}
]
});
}
setupTools() {
this.splineEditor = new ae(this.currentCamera, this.three), this.splineEditor.initDebug(), this.helpersContainer.add(this.splineEditor);
}
// Public
play() {
this.playing = !0, this.onUpdate();
}
pause() {
this.playing = !1, cancelAnimationFrame(this.rafID), this.rafID = -1;
}
toggleOrbitControls(i) {
this.controls.forEach((e) => {
e.enabled = !i;
});
}
clear() {
this.three.dispatchEvent({ type: o.CLEAR_OBJECT }), S.removeAllGroups(), this.clearLightHelpers(), this.clearControls(), this.currentTransform !== void 0 && (this.currentTransform.removeEventListener("objectChange", this.onUpdateTransform), m.instance.remove(this.currentTransform.getHelper().name)), this.currentTransform = void 0, m.instance.clear(), this.cameras.forEach((i) => {
this.editorCameras.indexOf(i.name) < 0 && this.three.dispatchEvent({ type: o.REMOVE_CAMERA, value: i });
}), this.currentCamera = this.debugCamera, this.currentScene = void 0, this.scenes.forEach((i) => {
this.three.dispatchEvent({ type: o.REMOVE_SCENE, value: i });
}), this.scenes.clear();
}
setGridVisibility(i) {
this.gridVisibility = i, this.saveExpandedGridVisibility(), this.grid && (this.grid.visible = i);
}
// Playback
update() {
this.controls.forEach((i) => i.update()), this.cameraHelpers.forEach((i) => i.update()), this.lightHelpers.forEach((i) => {
i.update !== void 0 && i.update();
}), this.props.onSceneUpdate !== void 0 && this.currentScene !== void 0 && this.props.onSceneUpdate(this.currentScene);
}
draw() {
if (this.rendererReady)
switch (this.renderer && this.renderer?.clear(), this.state.mode) {
case "Single":
this.drawSingle();
break;
case "Side by Side":
case "Stacked":
this.drawDouble();
break;
case "Quad":
this.drawQuad();
break;
}
}
onUpdate = () => {
this.playing && (this.update(), this.draw(), this.rafID = requestAnimationFrame(this.onUpdate));
};
// Events
enable() {
const i = this.containerRef.current;
i.addEventListener("mousemove", this.onMouseMove), i.addEventListener("click", this.onClick), window.addEventListener("keydown", this.onKey), window.addEventListener("resize", this.resize), this.three.addEventListener(o.ADD_SCENE, this.addScene), this.three.addEventListener(o.SET_SCENE, this.sceneUpdate), this.three.addEventListener(o.REMOVE_SCENE, this.removeScene), this.three.addEventListener(o.ADD_CAMERA, this.addCamera), this.three.addEventListener(o.REMOVE_CAMERA, this.removeCamera), this.three.addEventListener(o.SET_OBJECT, this.onSetSelectedItem);
}
disable() {
const i = this.containerRef.current;
i.removeEventListener("mousemove", this.onMouseMove), i.removeEventListener("click", this.onClick), window.removeEventListener("keydown", this.onKey), window.removeEventListener("resize", this.resize), this.three.removeEventListener(o.ADD_SCENE, this.addScene), this.three.removeEventListener(o.SET_SCENE, this.sceneUpdate), this.three.removeEventListener(o.REMOVE_SCENE, this.removeScene), this.three.removeEventListener(o.ADD_CAMERA, this.addCamera), this.three.removeEventListener(o.REMOVE_CAMERA, this.removeCamera), this.three.removeEventListener(o.SET_OBJECT, this.onSetSelectedItem);
}
resize = () => {
this.width = window.innerWidth - 300, this.height = window.innerHeight, this.renderer?.setSize(this.width, this.height);
const i = Math.floor(this.width / 2), e = Math.floor(this.height / 2);
this.props.three.resize(this.width, this.height), this.props.onSceneResize !== void 0 && this.currentScene !== void 0 && this.props.onSceneResize(this.currentScene, this.width, this.height);
let t = this.width, s = this.height;
switch (this.state.mode) {
case "Side by Side":
t = i, s = this.height;
break;
case "Stacked":
t = this.width, s = e;
break;
case "Quad":
t = i, s = e;
break;
}
const a = t / s;
this.cameras.forEach((r) => {
r instanceof M ? (r.left = t / -2, r.right = t / 2, r.top = s / 2, r.bottom = s / -2, r.name === "UI" && (r.position.x = this.width / 2, r.position.y = this.height / -2, r.position.z = 100), r.updateProjectionMatrix()) : r instanceof E && (r.aspect = a, r.updateProjectionMatrix()), this.cameraHelpers.get(r.name)?.update();
});
};
addScene = (i) => {
const e = this.props.scenes.get(i.value.name);
if (e !== void 0) {
const t = this.scenes.get(i.value.name);
if (t !== void 0) {
this.props.onSceneAdd !== void 0 && this.props.onSceneAdd(t), this.props.three.scene = t;
return;
}
const s = new e();
s.visible = !1, this.props.onSceneAdd !== void 0 && this.props.onSceneAdd(s), this.props.three.scene = s, this.scenes.set(i.value.name, s), this.scene.add(s);
} else
console.log("Hermes - Scene not found:", i.value.name, this.props.scenes);
};
sceneUpdate = (i) => {
this.currentScene !== void 0 && (this.currentScene.visible = !1, this.clearLightHelpers());
const e = this.scene.getObjectByName(i.value.name);
e !== void 0 && (this.currentScene = e, this.currentScene.visible = !0, this.addLightHelpers(this.currentScene)), this.cameraHelpers.forEach((t, s) => {
const a = this.currentScene !== void 0 && this.currentScene.getObjectByProperty("uuid", s) !== void 0;
t.visible = this.cameraVisibility && a;
});
};
removeScene = (i) => {
const e = i.value.name;
this.scenes.delete(e);
const t = this.scene.getObjectByName(e);
t && setTimeout(() => {
y(t);
}, 100), this.clearLightHelpers();
};
addCamera = (i) => {
const e = i.value, t = e.uuid, s = this.props.three.scene?.getObjectByProperty("uuid", e.uuid);
if (s !== void 0) {
const a = s;
this.cameras.set(t, a);
const r = new q(a), n = this.currentScene !== void 0 && this.currentScene.getObjectByProperty("uuid", t) !== void 0;
r.visible = this.cameraVisibility && n, this.cameraHelpers.set(t, r), this.helpersContainer.add(r), this.setState({ lastUpdate: Date.now() });
}
};
removeCamera = (i) => {
const t = i.value.uuid, s = this.cameraHelpers.get(t);
s !== void 0 && (this.helpersContainer.remove(s), s.dispose()), this.cameras.delete(t), this.setState({ lastUpdate: Date.now() });
};
onMouseMove = (i) => {
const e = new w();
this.renderer?.getSize(e);
const t = Math.min(i.clientX, e.x), s = Math.min(i.clientY, e.y);
this.pointer.x = g(t, 0, e.x, -1, 1), this.pointer.y = g(s, 0, e.y, 1, -1);
const a = e.x / 2, r = e.y / 2, n = () => {
t < a ? this.pointer.x = g(t, 0, a, -1, 1) : this.pointer.x = g(t, a, e.x, -1, 1);
}, d = () => {
s < r ? this.pointer.y = g(s, 0, r, 1, -1) : this.pointer.y = g(s, r, e.y, 1, -1);
};
switch (this.state.mode) {
case "Quad":
n(), d();
break;
case "Side by Side":
n();
break;
case "Stacked":
d(), d();
break;
}
if (this.updateCamera(t, s, a, r), this.state.interactionMode === "Orbit" || this.currentScene === void 0) return;
const l = this.raycaster.intersectObjects(this.currentScene.children);
l.length > 0 && this.interactionHelper.position.copy(l[0].point);
};
onClick = (i) => {
if (this.state.interactionMode === "Orbit" || this.currentScene === void 0) return;
const e = new w();
if (this.renderer.getSize(e), i.clientX >= e.x) return;
this.onMouseMove(i);
const t = this.raycaster.intersectObjects(this.currentScene.children);
t.length > 0 && (this.props.three.getObject(t[0].object.uuid), this.interactionHelper.visible = !1, this.setState({ interactionMode: "Orbit", lastUpdate: Date.now() }));
};
onKey = (i) => {
if (this.selectedItem !== void 0) {
if (i.ctrlKey) {
if (this.currentCamera.name === "UI") return;
const e = this.controls.get(this.currentCamera.name);
i.key === "0" ? (i.preventDefault(), this.clearControls(), this.cameraControls = new p(this.currentCamera, this.currentWindow.current), this.selectedItem instanceof K || this.selectedItem instanceof N ? (this.selectedItem.geometry.computeBoundingBox(), this.cameraControls.fitToBox(this.selectedItem.geometry.boundingBox, !0)) : this.cameraControls.fitToSphere(this.selectedItem, !0), this.updateCameraControls(e, !0)) : i.key === "1" ? (i.preventDefault(), this.clearControls(), this.cameraControls = new p(this.currentCamera, this.currentWindow.current), this.cameraControls.rotateTo(0, Math.PI * 0.5, !0), this.cameraControls.moveTo(this.selectedItem.position.x, this.selectedItem.position.y, 0, !0), this.updateCameraControls(e)) : i.key === "2" ? (i.preventDefault(), this.clearControls(), this.cameraControls = new p(this.currentCamera, this.currentWindow.current), this.cameraControls.rotateTo(0, 0, !0), this.cameraControls.moveTo(this.selectedItem.position.x, 0, this.selectedItem.position.z, !0), this.updateCameraControls(e)) : i.key === "3" ? (i.preventDefault(), this.clearControls(), this.cameraControls = new p(this.currentCamera, this.currentWindow.current), this.cameraControls.rotateTo(Math.PI / 2, Math.PI / 2, !0), this.cameraControls.moveTo(0, this.selectedItem.position.y, this.selectedItem.position.z, !0), this.updateCameraControls(e)) : i.key === "4" ? (i.preventDefault(), this.clearControls(), this.cameraControls = new p(this.currentCamera, this.currentWindow.current), this.cameraControls.rotateTo(Math.PI, Math.PI / 2, !0), this.cameraControls.moveTo(this.selectedItem.position.x, this.selectedItem.position.y, 0, !0), this.updateCameraControls(e)) : i.key === "5" && (i.preventDefault(), this.clearControls(), this.cameraControls = new p(this.currentCamera, this.currentWindow.current), this.cameraControls.rotateTo(H(45), H(45), !0), this.updateCameraControls(e));
} else if (this.currentTransform !== void 0)
switch (i.key) {
case "r":
this.currentTransform.setMode("rotate");
break;
case "s":
this.currentTransform.setMode("scale");
break;
case "t":
this.currentTransform.setMode("translate");
break;
case "q":
this.currentTransform.setSpace(this.currentTransform.space === "local" ? "world" : "local");
break;
}
}
};
onSetSelectedItem = (i) => {
this.selectedItem !== void 0 && this.updateSelectedItemHelper(!1), this.selectedItem = void 0;
const e = i.value.uuid;
if (this.scenes.forEach((t) => {
e.search(t.uuid) > -1 && (this.selectedItem = t.getObjectByProperty("uuid", e));
}), this.selectedItem === void 0) {
console.log(`Hermes - Can't find selected item: ${i.value.uuid}, ${i.value.name}`);
return;
}
this.currentTransform !== void 0 && (this.currentTransform.removeEventListener("objectChange", this.onUpdateTransform), m.instance.remove(this.currentTransform.getHelper().name)), this.currentTransform = m.instance.add(i.value.name), this.currentTransform && (this.currentTransform.attach(this.selectedItem), this.helpersContainer.add(this.currentTransform.getHelper()), this.currentTransform.addEventListener("objectChange", this.onUpdateTransform)), this.updateSelectedItemHelper(!0);
};
updateSelectedItemHelper(i) {
if (this.selectedItem !== void 0)
if (this.cameraVisibility) {
if (this.selectedItem.isLight === !0 && !this.lightVisibility) {
const e = this.lightHelpers.get(this.selectedItem.name);
e !== void 0 && (e.visible = i);
}
} else {
const e = this.cameraHelpers.get(this.selectedItem.name);
e !== void 0 && (e.visible = i);
}
}
onUpdateTransform = () => {
this.selectedItem !== void 0 && (this.props.three.updateObject(this.selectedItem.uuid, "position", this.selectedItem.position), this.props.three.updateObject(this.selectedItem.uuid, "rotation", {
x: this.selectedItem.rotation.x,
y: this.selectedItem.rotation.y,
z: this.selectedItem.rotation.z
}), this.props.three.updateObject(this.selectedItem.uuid, "scale", this.selectedItem.scale), ie.instance.update());
};
// Utils
clearLightHelpers = () => {
this.lightHelpers.forEach((i) => {
this.helpersContainer.remove(i), i.dispose();
}), this.lightHelpers.clear();
};
addLightHelpers = (i) => {
i.traverse((e) => {
if (e.type.search("Light") > -1) {
let t;
switch (e.type) {
case "DirectionalLight":
t = new X(e, 100), t.name = `${e.name}Helper`, t.visible = this.lightVisibility, this.lightHelpers.set(e.name, t), this.helpersContainer.add(t);
break;
case "HemisphereLight":
t = new J(e, 250), t.name = `${e.name}Helper`, t.visible = this.lightVisibility, this.lightHelpers.set(e.name, t), this.helpersContainer.add(t);
break;
case "RectAreaLight":
t = new j(e), t.name = `${e.name}Helper`, t.visible = this.lightVisibility, this.lightHelpers.set(e.name, t), this.helpersContainer.add(t);
break;
case "PointLight":
t = new Y(e, 100), t.name = `${e.name}Helper`, t.visible = this.lightVisibility, this.lightHelpers.set(e.name, t), this.helpersContainer.add(t);
break;
case "SpotLight":
t = new $(e), t.name = `${e.name}Helper`, t.visible = this.lightVisibility, this.lightHelpers.set(e.name, t), this.helpersContainer.add(t);
break;
}
}
});
};
createControls(i, e) {
const t = this.controls.get(i.name);
if (t !== void 0 && t.dispose(), this.controls.delete(i.name), i.name === "UI") return;
const s = new Z(i, e);
switch (s.enableDamping = !0, s.dampingFactor = 0.1, i.name) {
case "Top":
case "Bottom":
case "Left":
case "Right":
case "Front":
case "Back":
s.enableRotate = !1;
break;
}
this.controls.set(i.name, s);
}
clearCamera(i) {
const e = this.cameraHelpers.get(i.name);
e !== void 0 && (this.helpersContainer.remove(e), e.dispose(), this.cameraHelpers.delete(i.name));
const t = this.controls.get(i.name);
t !== void 0 && (t.dispose(), this.controls.delete(i.name));
}
killControls() {
this.controls.forEach((i, e) => {
i.dispose();
const t = this.cameraHelpers.get(e);
t !== void 0 && (this.helpersContainer.remove(t), t.dispose()), this.cameraHelpers.delete(e), this.controls.delete(e);
}), this.controls.clear(), this.cameraHelpers.clear();
}
assignControls() {
switch (this.state.mode) {
case "Single":
this.createControls(this.tlCam, this.tlWindow.current);
break;
case "Side by Side":
case "Stacked":
this.createControls(this.tlCam, this.tlWindow.current), this.createControls(this.trCam, this.trWindow.current);
break;
case "Quad":
this.createControls(this.tlCam, this.tlWindow.current), this.createControls(this.trCam, this.trWindow.current), this.createControls(this.blCam, this.blWindow.current), this.createControls(this.brCam, this.brWindow.current);
break;
}
}
updateCamera = (i, e, t, s) => {
switch (this.state.mode) {
case "Quad":
e < s ? i < t ? this.currentCamera = this.tlCam : this.currentCamera = this.trCam : i < t ? this.currentCamera = this.blCam : this.currentCamera = this.brCam;
break;
case "Side by Side":
i < t ? this.currentCamera = this.tlCam : this.currentCamera = this.trCam;
break;
case "Single":
this.currentCamera = this.tlCam;
break;
case "Stacked":
e < s ? this.currentCamera = this.tlCam : this.currentCamera = this.trCam;
break;
}
this.splineEditor.camera = this.currentCamera, this.raycaster.setFromCamera(this.pointer, this.currentCamera), this.currentCamera === this.tlCam ? this.currentWindow = this.tlWindow : this.currentCamera === this.trCam ? this.currentWindow = this.trWindow : this.currentCamera === this.blCam ? this.currentWindow = this.blWindow : this.currentCamera === this.brCam && (this.currentWindow = this.brWindow), m.instance.updateCamera(this.currentCamera, this.currentWindow.current);
};
updateCameraControls = (i, e = !1) => {
if (this.selectedItem === void 0) return;
cancelAnimationFrame(this.cameraControlsRafID), this.cameraControlsRafID = -1, this.cameraControls && (this.cameraControls.smoothTime = 0.1);
const t = 0.15;
this.cameraControlsStartTime = performance.now(), this.cameraControlsLastTime = this.cameraControlsStartTime, this.selectedItem.getWorldPosition(i.target0);
const s = () => {
const a = performance.now(), r = (a - this.cameraControlsLastTime) / 1e3;
this.cameraControlsLastTime = a, this.cameraControls && this.cameraControls.update(r), e && (i.target.lerp(i.target0, t), i.object.position.lerp(i.position0, t), i.object.zoom = ne(i.object.zoom, i.zoom0, t), i.object.updateProjectionMatrix(), i.dispatchEvent({ type: "change" })), (a - this.cameraControlsStartTime) / 1e3 >= 0.5 ? (cancelAnimationFrame(this.cameraControlsRafID), this.cameraControlsRafID = -1, this.clearControls()) : this.cameraControlsRafID = requestAnimationFrame(s);
};
s();
};
clearControls = () => {
this.cameraControls !== void 0 && (this.cameraControls.disconnect(), this.cameraControls.dispose(), this.cameraControls = void 0);
};
saveExpandedCameraVisibility() {
localStorage.setItem(this.expandedCameraVisibility, this.cameraVisibility ? "open" : "closed");
}
saveExpandedLightVisibility() {
localStorage.setItem(this.expandedLightVisibility, this.lightVisibility ? "open" : "closed");
}
saveExpandedGridVisibility() {
localStorage.setItem(this.expandedGridVisibility, this.gridVisibility ? "open" : "closed");
}
// Drawing
getSceneOverride(i) {
switch (i) {
case "Depth":
return this.depthMaterial;
case "Normals":
return this.normalsMaterial;
case "Renderer":
return null;
case "UVs":
return this.uvMaterial;
case "Wireframe":
return this.wireframeMaterial;
}
return null;
}
drawTo(i, e, t, s, a, r) {
switch (a.name) {
case "Left":
case "Right":
this.grid && (this.grid.rotation.z = Math.PI / 2);
break;
case "Front":
case "Back":
this.grid && (this.grid.rotation.x = Math.PI / 2);
break;
}
this.scene.overrideMaterial = r, this.renderer && (this.renderer?.setScissor(i, e, t, s), this.renderer?.setViewport(i, e, t, s), this.renderer?.render(this.scene, a)), this.grid && this.grid.rotation.set(0, 0, 0);
}
drawSingle() {
const i = this.getSceneOverride(this.tlRender);
this.drawTo(0, 0, this.width, this.height, this.tlCam, i);
}
drawDouble = () => {
const i = this.getSceneOverride(this.tlRender), e = this.getSceneOverride(this.trRender), t = Math.floor(this.width / 2), s = Math.floor(this.height / 2), a = this.renderer instanceof A;
if (this.state.mode === "Side by Side")
this.drawTo(0, 0, t, this.height, this.tlCam, i), this.drawTo(t, 0, t, this.height, this.trCam, e);
else {
const r = this.height - s;
a ? (this.drawTo(0, 0, this.width, s, this.tlCam, i), this.drawTo(0, r, this.width, s, this.trCam, e)) : (this.drawTo(0, r, this.width, s, this.tlCam, i), this.drawTo(0, 0, this.width, s, this.trCam, e));
}
};
drawQuad = () => {
const i = this.renderer instanceof A, e = this.getSceneOverride(this.tlRender), t = this.getSceneOverride(this.trRender), s = this.getSceneOverride(this.blRender), a = this.getSceneOverride(this.brRender), r = Math.floor(this.width / 2), n = Math.floor(this.height / 2), d = this.height - n;
let l = 0, f = i ? 0 : this.height - n;
l = 0, this.drawTo(l, f, r, n, this.tlCam, e), l = r, this.drawTo(l, f, r, n, this.trCam, t), f = i ? d : 0, l = 0, this.scene.overrideMaterial = s, this.drawTo(l, f, r, n, this.blCam, s), l = r, this.drawTo(l, f, r, n, this.brCam, a);
};
// Getters
get appID() {
return this.props.three.name;
}
get mode() {
return this.state.mode;
}
get three() {
return this.props.three;
}
get expandedCameraVisibility() {
return `${this.appID}_multiviewCameraVisibility`;
}
get expandedLightVisibility() {
return `${this.appID}_multiviewLightVisibility`;
}
get expandedGridVisibility() {
return `${this.appID}_multiviewGridVisibility`;
}
}
export {
k as default
};