abstract-3d 3.1.2 → 4.0.0-beta.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +53 -1
- package/lib/abstract-3d.d.ts +27 -1
- package/lib/abstract-3d.d.ts.map +1 -1
- package/lib/abstract-3d.js +26 -2
- package/lib/abstract-3d.js.map +1 -1
- package/lib/renderers/dxf/dxf-encoding/dxf-build.d.ts.map +1 -1
- package/lib/renderers/dxf/dxf-encoding/dxf-build.js +1 -2
- package/lib/renderers/dxf/dxf-encoding/dxf-build.js.map +1 -1
- package/lib/renderers/ifc/ifc.test.d.ts +2 -0
- package/lib/renderers/ifc/ifc.test.d.ts.map +1 -0
- package/lib/renderers/ifc/ifc.test.js +380 -0
- package/lib/renderers/ifc/ifc.test.js.map +1 -0
- package/lib/renderers/index.d.ts +1 -0
- package/lib/renderers/index.d.ts.map +1 -1
- package/lib/renderers/index.js +1 -0
- package/lib/renderers/index.js.map +1 -1
- package/lib/renderers/react/index.d.ts +2 -5
- package/lib/renderers/react/index.d.ts.map +1 -1
- package/lib/renderers/react/index.js +1 -2
- package/lib/renderers/react/index.js.map +1 -1
- package/lib/renderers/react/react.d.ts +16 -42
- package/lib/renderers/react/react.d.ts.map +1 -1
- package/lib/renderers/react/react.js +56 -32
- package/lib/renderers/react/react.js.map +1 -1
- package/lib/renderers/three/appearance/appearance.d.ts +46 -0
- package/lib/renderers/three/appearance/appearance.d.ts.map +1 -0
- package/lib/renderers/three/appearance/appearance.js +102 -0
- package/lib/renderers/three/appearance/appearance.js.map +1 -0
- package/lib/renderers/three/appearance/appearance.test.d.ts +2 -0
- package/lib/renderers/three/appearance/appearance.test.d.ts.map +1 -0
- package/lib/renderers/three/appearance/appearance.test.js +97 -0
- package/lib/renderers/three/appearance/appearance.test.js.map +1 -0
- package/lib/renderers/three/appearance.d.ts +41 -0
- package/lib/renderers/three/appearance.d.ts.map +1 -0
- package/lib/renderers/three/appearance.js +100 -0
- package/lib/renderers/three/appearance.js.map +1 -0
- package/lib/renderers/three/build/build.d.ts +53 -0
- package/lib/renderers/three/build/build.d.ts.map +1 -0
- package/lib/renderers/three/build/build.js +181 -0
- package/lib/renderers/three/build/build.js.map +1 -0
- package/lib/renderers/three/build/build.test.d.ts +2 -0
- package/lib/renderers/three/build/build.test.d.ts.map +1 -0
- package/lib/renderers/three/build/build.test.js +120 -0
- package/lib/renderers/three/build/build.test.js.map +1 -0
- package/lib/renderers/three/build/materials.d.ts +20 -0
- package/lib/renderers/three/build/materials.d.ts.map +1 -0
- package/lib/renderers/three/build/materials.js +168 -0
- package/lib/renderers/three/build/materials.js.map +1 -0
- package/lib/renderers/three/build/parts.d.ts +65 -0
- package/lib/renderers/three/build/parts.d.ts.map +1 -0
- package/lib/renderers/three/build/parts.js +203 -0
- package/lib/renderers/three/build/parts.js.map +1 -0
- package/lib/renderers/three/build.d.ts +50 -0
- package/lib/renderers/three/build.d.ts.map +1 -0
- package/lib/renderers/three/build.js +210 -0
- package/lib/renderers/three/build.js.map +1 -0
- package/lib/renderers/three/camera/camera.d.ts +40 -0
- package/lib/renderers/three/camera/camera.d.ts.map +1 -0
- package/lib/renderers/three/camera/camera.js +51 -0
- package/lib/renderers/three/camera/camera.js.map +1 -0
- package/lib/renderers/three/camera/camera.test.d.ts +2 -0
- package/lib/renderers/three/camera/camera.test.d.ts.map +1 -0
- package/lib/renderers/three/camera/camera.test.js +46 -0
- package/lib/renderers/three/camera/camera.test.js.map +1 -0
- package/lib/renderers/three/camera/controls.d.ts +24 -0
- package/lib/renderers/three/camera/controls.d.ts.map +1 -0
- package/lib/renderers/three/camera/controls.js +39 -0
- package/lib/renderers/three/camera/controls.js.map +1 -0
- package/lib/renderers/three/camera/gizmo.d.ts +44 -0
- package/lib/renderers/three/camera/gizmo.d.ts.map +1 -0
- package/lib/renderers/three/camera/gizmo.js +43 -0
- package/lib/renderers/three/camera/gizmo.js.map +1 -0
- package/lib/renderers/three/camera.d.ts +63 -0
- package/lib/renderers/three/camera.d.ts.map +1 -0
- package/lib/renderers/three/camera.js +85 -0
- package/lib/renderers/three/camera.js.map +1 -0
- package/lib/renderers/three/compile/compile.d.ts +59 -0
- package/lib/renderers/three/compile/compile.d.ts.map +1 -0
- package/lib/renderers/three/compile/compile.js +88 -0
- package/lib/renderers/three/compile/compile.js.map +1 -0
- package/lib/renderers/three/compile/compile.test.d.ts +2 -0
- package/lib/renderers/three/compile/compile.test.d.ts.map +1 -0
- package/lib/renderers/three/compile/compile.test.js +93 -0
- package/lib/renderers/three/compile/compile.test.js.map +1 -0
- package/lib/renderers/three/compile.d.ts +58 -0
- package/lib/renderers/three/compile.d.ts.map +1 -0
- package/lib/renderers/three/compile.js +89 -0
- package/lib/renderers/three/compile.js.map +1 -0
- package/lib/renderers/three/controls.d.ts +27 -0
- package/lib/renderers/three/controls.d.ts.map +1 -0
- package/lib/renderers/three/controls.js +115 -0
- package/lib/renderers/three/controls.js.map +1 -0
- package/lib/renderers/three/environment/studio.d.ts +9 -0
- package/lib/renderers/three/environment/studio.d.ts.map +1 -0
- package/lib/renderers/three/environment/studio.js +15 -0
- package/lib/renderers/three/environment/studio.js.map +1 -0
- package/lib/renderers/three/geometry/geometry.d.ts +21 -0
- package/lib/renderers/three/geometry/geometry.d.ts.map +1 -0
- package/lib/renderers/three/geometry/geometry.js +141 -0
- package/lib/renderers/three/geometry/geometry.js.map +1 -0
- package/lib/renderers/three/geometry/geometry.test.d.ts +2 -0
- package/lib/renderers/three/geometry/geometry.test.d.ts.map +1 -0
- package/lib/renderers/three/geometry/geometry.test.js +80 -0
- package/lib/renderers/three/geometry/geometry.test.js.map +1 -0
- package/lib/renderers/three/geometry.d.ts +20 -0
- package/lib/renderers/three/geometry.d.ts.map +1 -0
- package/lib/renderers/three/geometry.js +164 -0
- package/lib/renderers/three/geometry.js.map +1 -0
- package/lib/renderers/three/gizmo.d.ts +49 -0
- package/lib/renderers/three/gizmo.d.ts.map +1 -0
- package/lib/renderers/three/gizmo.js +172 -0
- package/lib/renderers/three/gizmo.js.map +1 -0
- package/lib/renderers/three/index.d.ts +16 -0
- package/lib/renderers/three/index.d.ts.map +1 -0
- package/lib/renderers/three/index.js +6 -0
- package/lib/renderers/three/index.js.map +1 -0
- package/lib/renderers/three/input.d.ts +25 -0
- package/lib/renderers/three/input.d.ts.map +1 -0
- package/lib/renderers/three/input.js +71 -0
- package/lib/renderers/three/input.js.map +1 -0
- package/lib/renderers/three/lines.d.ts +16 -0
- package/lib/renderers/three/lines.d.ts.map +1 -0
- package/lib/renderers/three/lines.js +63 -0
- package/lib/renderers/three/lines.js.map +1 -0
- package/lib/renderers/three/materials.d.ts +15 -0
- package/lib/renderers/three/materials.d.ts.map +1 -0
- package/lib/renderers/three/materials.js +70 -0
- package/lib/renderers/three/materials.js.map +1 -0
- package/lib/renderers/three/options.d.ts +68 -0
- package/lib/renderers/three/options.d.ts.map +1 -0
- package/lib/renderers/three/options.js +64 -0
- package/lib/renderers/three/options.js.map +1 -0
- package/lib/renderers/three/overlays.d.ts +25 -0
- package/lib/renderers/three/overlays.d.ts.map +1 -0
- package/lib/renderers/three/overlays.js +57 -0
- package/lib/renderers/three/overlays.js.map +1 -0
- package/lib/renderers/three/quality/quality.d.ts +18 -0
- package/lib/renderers/three/quality/quality.d.ts.map +1 -0
- package/lib/renderers/three/quality/quality.js +28 -0
- package/lib/renderers/three/quality/quality.js.map +1 -0
- package/lib/renderers/three/quality/quality.test.d.ts +2 -0
- package/lib/renderers/three/quality/quality.test.d.ts.map +1 -0
- package/lib/renderers/three/quality/quality.test.js +23 -0
- package/lib/renderers/three/quality/quality.test.js.map +1 -0
- package/lib/renderers/three/quality.d.ts +24 -0
- package/lib/renderers/three/quality.d.ts.map +1 -0
- package/lib/renderers/three/quality.js +67 -0
- package/lib/renderers/three/quality.js.map +1 -0
- package/lib/renderers/three/shadow/key-light.d.ts +6 -0
- package/lib/renderers/three/shadow/key-light.d.ts.map +1 -0
- package/lib/renderers/three/shadow/key-light.js +39 -0
- package/lib/renderers/three/shadow/key-light.js.map +1 -0
- package/lib/renderers/three/shadow/shadow.d.ts +21 -0
- package/lib/renderers/three/shadow/shadow.d.ts.map +1 -0
- package/lib/renderers/three/shadow/shadow.js +120 -0
- package/lib/renderers/three/shadow/shadow.js.map +1 -0
- package/lib/renderers/three/text/text.d.ts +55 -0
- package/lib/renderers/three/text/text.d.ts.map +1 -0
- package/lib/renderers/three/text/text.js +90 -0
- package/lib/renderers/three/text/text.js.map +1 -0
- package/lib/renderers/three/text/text.test.d.ts +2 -0
- package/lib/renderers/three/text/text.test.d.ts.map +1 -0
- package/lib/renderers/three/text/text.test.js +70 -0
- package/lib/renderers/three/text/text.test.js.map +1 -0
- package/lib/renderers/three/text.d.ts +53 -0
- package/lib/renderers/three/text.d.ts.map +1 -0
- package/lib/renderers/three/text.js +151 -0
- package/lib/renderers/three/text.js.map +1 -0
- package/lib/renderers/three/viewer/options.d.ts +82 -0
- package/lib/renderers/three/viewer/options.d.ts.map +1 -0
- package/lib/renderers/three/viewer/options.js +34 -0
- package/lib/renderers/three/viewer/options.js.map +1 -0
- package/lib/renderers/three/viewer/options.test.d.ts +2 -0
- package/lib/renderers/three/viewer/options.test.d.ts.map +1 -0
- package/lib/renderers/three/viewer/options.test.js +38 -0
- package/lib/renderers/three/viewer/options.test.js.map +1 -0
- package/lib/renderers/three/viewer/overlays.d.ts +31 -0
- package/lib/renderers/three/viewer/overlays.d.ts.map +1 -0
- package/lib/renderers/three/viewer/overlays.js +43 -0
- package/lib/renderers/three/viewer/overlays.js.map +1 -0
- package/lib/renderers/three/viewer/viewer.d.ts +102 -0
- package/lib/renderers/three/viewer/viewer.d.ts.map +1 -0
- package/lib/renderers/three/viewer/viewer.js +488 -0
- package/lib/renderers/three/viewer/viewer.js.map +1 -0
- package/lib/renderers/three/viewer.d.ts +38 -0
- package/lib/renderers/three/viewer.d.ts.map +1 -0
- package/lib/renderers/three/viewer.js +435 -0
- package/lib/renderers/three/viewer.js.map +1 -0
- package/package.json +34 -16
- package/src/abstract-3d.ts +60 -2
- package/src/renderers/dxf/dxf-encoding/dxf-build.ts +1 -2
- package/src/renderers/ifc/ifc.test.ts +7 -5
- package/src/renderers/index.ts +1 -0
- package/src/renderers/react/index.ts +2 -5
- package/src/renderers/react/react.tsx +107 -181
- package/src/renderers/three/appearance/appearance.test.ts +153 -0
- package/src/renderers/three/appearance/appearance.ts +150 -0
- package/src/renderers/three/build/build.test.ts +149 -0
- package/src/renderers/three/build/build.ts +278 -0
- package/src/renderers/three/build/materials.ts +222 -0
- package/src/renderers/three/build/parts.ts +277 -0
- package/src/renderers/three/camera/camera.test.ts +53 -0
- package/src/renderers/three/camera/camera.ts +82 -0
- package/src/renderers/three/camera/controls.ts +65 -0
- package/src/renderers/three/camera/gizmo.ts +94 -0
- package/src/renderers/three/compile/compile.test.ts +126 -0
- package/src/renderers/three/compile/compile.ts +151 -0
- package/src/renderers/three/environment/studio.ts +18 -0
- package/src/renderers/three/geometry/geometry.test.ts +121 -0
- package/src/renderers/three/geometry/geometry.ts +189 -0
- package/src/renderers/three/index.ts +15 -0
- package/src/renderers/three/quality/quality.test.ts +37 -0
- package/src/renderers/three/quality/quality.ts +36 -0
- package/src/renderers/three/shadow/key-light.ts +43 -0
- package/src/renderers/three/shadow/shadow.ts +170 -0
- package/src/renderers/three/text/text.test.ts +79 -0
- package/src/renderers/three/text/text.ts +128 -0
- package/src/renderers/three/viewer/options.ts +112 -0
- package/src/renderers/three/viewer/overlays.ts +71 -0
- package/src/renderers/three/viewer/viewer.ts +596 -0
- package/lib/renderers/react/react-hotspot.d.ts +0 -27
- package/lib/renderers/react/react-hotspot.d.ts.map +0 -1
- package/lib/renderers/react/react-hotspot.js +0 -37
- package/lib/renderers/react/react-hotspot.js.map +0 -1
- package/src/custom_typings/react-three-fiber.d.ts +0 -9
- package/src/custom_typings/react-three-postprocessing.d.ts +0 -1
- package/src/renderers/react/react-camera.tsx +0 -391
- package/src/renderers/react/react-dimension.tsx +0 -122
- package/src/renderers/react/react-group.test.ts +0 -56
- package/src/renderers/react/react-group.tsx +0 -202
- package/src/renderers/react/react-image-material.tsx +0 -139
- package/src/renderers/react/react-material.tsx +0 -91
- package/src/renderers/react/react-mesh.tsx +0 -512
- package/src/renderers/react/react-relation.tsx +0 -86
- package/src/renderers/react/react-scene.tsx +0 -111
- package/src/renderers/react/react-types.ts +0 -9
|
@@ -0,0 +1,596 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type BufferGeometry,
|
|
3
|
+
DirectionalLight,
|
|
4
|
+
Group,
|
|
5
|
+
HemisphereLight,
|
|
6
|
+
NeutralToneMapping,
|
|
7
|
+
OrthographicCamera,
|
|
8
|
+
PerspectiveCamera,
|
|
9
|
+
Raycaster,
|
|
10
|
+
Scene as ThreeScene,
|
|
11
|
+
type Texture,
|
|
12
|
+
Vector2,
|
|
13
|
+
Vector3,
|
|
14
|
+
WebGLRenderer,
|
|
15
|
+
} from "three";
|
|
16
|
+
import type { ViewportGizmo } from "three-viewport-gizmo";
|
|
17
|
+
import type { StructureState } from "../appearance/appearance.js";
|
|
18
|
+
import type { BuiltScene } from "../build/build.js";
|
|
19
|
+
import { animateScene, buildScene, cullScene, disposeScene, getPickables, getPickedItem, paintScene } from "../build/build.js";
|
|
20
|
+
import type { TextureCache } from "../build/materials.js";
|
|
21
|
+
import { disposeTextures } from "../build/materials.js";
|
|
22
|
+
import type { Placement } from "../camera/camera.js";
|
|
23
|
+
import { fitCamera } from "../camera/camera.js";
|
|
24
|
+
import type { Controls } from "../camera/controls.js";
|
|
25
|
+
import { configureControls, createControls, setPose } from "../camera/controls.js";
|
|
26
|
+
import { createGizmo } from "../camera/gizmo.js";
|
|
27
|
+
import type { CompiledScene } from "../compile/compile.js";
|
|
28
|
+
import { compileScene } from "../compile/compile.js";
|
|
29
|
+
import type { TierGovernor } from "../quality/quality.js";
|
|
30
|
+
import { getPixelRatio, recordFrame } from "../quality/quality.js";
|
|
31
|
+
import { createKeyLight, disposeKeyLight } from "../shadow/key-light.js";
|
|
32
|
+
import type { GroundShadow } from "../shadow/shadow.js";
|
|
33
|
+
import { createGroundShadow, disposeGroundShadow } from "../shadow/shadow.js";
|
|
34
|
+
import type { GlyphAtlas } from "../text/text.js";
|
|
35
|
+
import { createGlyphAtlas } from "../text/text.js";
|
|
36
|
+
import type { Change, ViewerOptions } from "./options.js";
|
|
37
|
+
import { effects } from "./options.js";
|
|
38
|
+
import type { Overlay, OverlayLayer } from "./overlays.js";
|
|
39
|
+
import { createOverlayLayer, setOverlays } from "./overlays.js";
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* The interactive viewer: renderer, render on demand, camera, controls, pointer input, overlays and gizmo. Updates are batched into the next frame,
|
|
43
|
+
* as r3f applies props; render on demand and `invalidate` are r3f's `frameloop="demand"`; the state with functions instead of a class keeps it free
|
|
44
|
+
* of `this`
|
|
45
|
+
*/
|
|
46
|
+
|
|
47
|
+
type Hit = { readonly item: number; readonly target: number; readonly point: Vector3 };
|
|
48
|
+
|
|
49
|
+
export type Viewer<Content> = {
|
|
50
|
+
options: ViewerOptions<Content>;
|
|
51
|
+
readonly container: HTMLElement;
|
|
52
|
+
readonly canvas: HTMLCanvasElement;
|
|
53
|
+
readonly renderer: WebGLRenderer;
|
|
54
|
+
/** Apps can add their own objects, then `invalidate` */
|
|
55
|
+
readonly scene: ThreeScene;
|
|
56
|
+
readonly lights: Group;
|
|
57
|
+
readonly cameras: { readonly Perspective: PerspectiveCamera; readonly Orthographic: OrthographicCamera };
|
|
58
|
+
camera: PerspectiveCamera | OrthographicCamera;
|
|
59
|
+
readonly controls: Controls;
|
|
60
|
+
readonly geometries: Map<string, BufferGeometry>;
|
|
61
|
+
readonly atlas: GlyphAtlas;
|
|
62
|
+
readonly textures: TextureCache;
|
|
63
|
+
readonly overlays: OverlayLayer<Content>;
|
|
64
|
+
readonly size: { width: number; height: number };
|
|
65
|
+
readonly listeners: AbortController;
|
|
66
|
+
readonly resizeObserver: ResizeObserver;
|
|
67
|
+
readonly pointer: { last?: PointerEvent; down?: { readonly x: number; readonly y: number }; hovered?: Hit; frame: number };
|
|
68
|
+
readonly loop: { frame: number; last: number; wasMoving: boolean; seconds: number };
|
|
69
|
+
/** What the next frame redoes, from the options passed since the last */
|
|
70
|
+
readonly pending: Set<Change>;
|
|
71
|
+
/** CPU times in ms of the last frame, pick and repaint, and the last frame's draw calls, post-processing included */
|
|
72
|
+
readonly stats: { frames: number; frameMs: number; drawCalls: number; pickMs: number; paintMs: number };
|
|
73
|
+
structure: StructureState;
|
|
74
|
+
compiled?: CompiledScene;
|
|
75
|
+
built?: BuiltScene;
|
|
76
|
+
/** The previous build and ground shadow, out of the scene, disposed once the next frame has given their shader programs to the new ones */
|
|
77
|
+
readonly retired: { built?: BuiltScene; shadow?: GroundShadow };
|
|
78
|
+
gizmo?: ViewportGizmo;
|
|
79
|
+
shadow?: GroundShadow;
|
|
80
|
+
/** The key light that casts the solids' shadows on themselves */
|
|
81
|
+
keyLight?: Group;
|
|
82
|
+
governor?: TierGovernor;
|
|
83
|
+
/** The studio environment, loading and once loaded */
|
|
84
|
+
readonly studio: { loading?: Promise<unknown>; texture?: Texture };
|
|
85
|
+
/** The scene is built and its shaders compiled */
|
|
86
|
+
loaded: boolean;
|
|
87
|
+
fitted: boolean;
|
|
88
|
+
disposed: boolean;
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
/** Pointer travel in CSS pixels below which a press and release is a click, not a drag */
|
|
92
|
+
const clickTolerance = 4;
|
|
93
|
+
|
|
94
|
+
export function createViewer<Content>(container: HTMLElement, options: ViewerOptions<Content>): Viewer<Content> {
|
|
95
|
+
if (getComputedStyle(container).position === "static") {
|
|
96
|
+
container.style.position = "relative";
|
|
97
|
+
}
|
|
98
|
+
const canvas = document.createElement("canvas");
|
|
99
|
+
// Absolutely positioned, so the canvas' own size never feeds back into the container's
|
|
100
|
+
canvas.style.cssText = "position:absolute;inset:0;display:block;width:100%;height:100%;touch-action:none";
|
|
101
|
+
container.prepend(canvas);
|
|
102
|
+
// Khronos PBR Neutral tone mapping keeps product colors true; the canvas is transparent, as r3f's
|
|
103
|
+
const renderer = new WebGLRenderer({ canvas, antialias: true, alpha: true });
|
|
104
|
+
Object.assign(renderer, { toneMapping: NeutralToneMapping });
|
|
105
|
+
renderer.setClearColor(0x000000, 0);
|
|
106
|
+
// Rendered when the scene is built, not each frame
|
|
107
|
+
Object.assign(renderer.shadowMap, { enabled: true, autoUpdate: false });
|
|
108
|
+
renderer.info.autoReset = false;
|
|
109
|
+
const cameras = { Perspective: new PerspectiveCamera(45), Orthographic: new OrthographicCamera() };
|
|
110
|
+
const camera = cameras[options.camera?.type ?? "Perspective"];
|
|
111
|
+
const v: Viewer<Content> = {
|
|
112
|
+
options,
|
|
113
|
+
container,
|
|
114
|
+
canvas,
|
|
115
|
+
renderer,
|
|
116
|
+
scene: new ThreeScene(),
|
|
117
|
+
lights: new Group(),
|
|
118
|
+
cameras,
|
|
119
|
+
camera,
|
|
120
|
+
controls: createControls(camera, canvas, options.controls ?? {}, () => invalidate(v)),
|
|
121
|
+
geometries: new Map(),
|
|
122
|
+
atlas: createGlyphAtlas(),
|
|
123
|
+
textures: new Map(),
|
|
124
|
+
overlays: createOverlayLayer(container),
|
|
125
|
+
size: { width: 0, height: 0 },
|
|
126
|
+
listeners: new AbortController(),
|
|
127
|
+
resizeObserver: new ResizeObserver(([entry]) => entry && onResize(v, entry.contentRect)),
|
|
128
|
+
pointer: { frame: 0 },
|
|
129
|
+
loop: { frame: 0, last: 0, wasMoving: false, seconds: 0 },
|
|
130
|
+
pending: new Set(["compile", "build", "camera", "controls", "gizmo", "lights", "quality", "paint", "overlays"]),
|
|
131
|
+
stats: { frames: 0, frameMs: 0, drawCalls: 0, pickMs: 0, paintMs: 0 },
|
|
132
|
+
structure: { activeComponents: options.activeComponents, relationsActive: options.activeRelations !== undefined },
|
|
133
|
+
studio: {},
|
|
134
|
+
retired: {},
|
|
135
|
+
loaded: false,
|
|
136
|
+
fitted: false,
|
|
137
|
+
disposed: false,
|
|
138
|
+
};
|
|
139
|
+
v.scene.add(v.lights);
|
|
140
|
+
// Loaded while the first frame builds the scene
|
|
141
|
+
if (options.studio !== false) {
|
|
142
|
+
loadStudio(v).catch(() => undefined);
|
|
143
|
+
}
|
|
144
|
+
const signal = { signal: v.listeners.signal };
|
|
145
|
+
canvas.addEventListener("pointermove", (e) => onPointerMove(v, e), signal);
|
|
146
|
+
canvas.addEventListener("pointerleave", (e) => setHovered(v, undefined, e), signal);
|
|
147
|
+
canvas.addEventListener("pointerdown", (e) => (v.pointer.down = { x: e.clientX, y: e.clientY }), signal);
|
|
148
|
+
canvas.addEventListener("click", (e) => onClick(v, e), signal);
|
|
149
|
+
canvas.addEventListener("contextmenu", (e) => onContextMenu(v, e), signal);
|
|
150
|
+
v.resizeObserver.observe(container);
|
|
151
|
+
return v;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/** Sets the options passed, redone in the next frame; what each redoes is in `effects`, so pass only the ones that changed */
|
|
155
|
+
export function updateViewer<Content>(v: Viewer<Content>, options: Partial<ViewerOptions<Content>>): void {
|
|
156
|
+
v.options = { ...v.options, ...options };
|
|
157
|
+
Object.keys(options).forEach((key) => effects[key as keyof ViewerOptions<Content>].forEach((change) => v.pending.add(change)));
|
|
158
|
+
invalidate(v);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/** Renders a frame on the next animation frame, e.g. after changing objects added to `scene` */
|
|
162
|
+
export function invalidate<Content>(v: Viewer<Content>): void {
|
|
163
|
+
if (!v.loop.frame && !v.disposed && v.size.width > 0 && v.size.height > 0) {
|
|
164
|
+
v.loop.frame = requestAnimationFrame((now) => renderFrame(v, now));
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
export function disposeViewer<Content>(v: Viewer<Content>): void {
|
|
169
|
+
v.disposed = true;
|
|
170
|
+
cancelAnimationFrame(v.loop.frame);
|
|
171
|
+
cancelAnimationFrame(v.pointer.frame);
|
|
172
|
+
v.listeners.abort();
|
|
173
|
+
v.resizeObserver.disconnect();
|
|
174
|
+
v.controls.orbit.dispose();
|
|
175
|
+
v.gizmo?.dispose();
|
|
176
|
+
disposeRetired(v);
|
|
177
|
+
if (v.built) {
|
|
178
|
+
disposeScene(v.built);
|
|
179
|
+
}
|
|
180
|
+
if (v.shadow) {
|
|
181
|
+
disposeGroundShadow(v.shadow);
|
|
182
|
+
}
|
|
183
|
+
if (v.keyLight) {
|
|
184
|
+
disposeKeyLight(v.keyLight);
|
|
185
|
+
}
|
|
186
|
+
v.studio.texture?.dispose();
|
|
187
|
+
v.geometries.forEach((g) => g.dispose());
|
|
188
|
+
v.atlas.texture.dispose();
|
|
189
|
+
disposeTextures(v.textures);
|
|
190
|
+
setOverlays(v.overlays, [], v.options.onOverlays);
|
|
191
|
+
v.overlays.renderer.domElement.remove();
|
|
192
|
+
v.renderer.dispose();
|
|
193
|
+
v.canvas.remove();
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/** Redoes what the changes need, in dependency order */
|
|
197
|
+
function apply<Content>(v: Viewer<Content>, changes: ReadonlySet<Change>): void {
|
|
198
|
+
const { options } = v;
|
|
199
|
+
if (changes.has("compile")) {
|
|
200
|
+
v.compiled = options.scene && compileScene(options.scene, options.createGroupId);
|
|
201
|
+
v.pointer.hovered = undefined;
|
|
202
|
+
}
|
|
203
|
+
if (changes.has("build")) {
|
|
204
|
+
v.structure = { activeComponents: options.activeComponents, relationsActive: options.activeRelations !== undefined };
|
|
205
|
+
rebuild(v);
|
|
206
|
+
}
|
|
207
|
+
if (changes.has("shadow") || changes.has("build")) {
|
|
208
|
+
if (v.retired.shadow) {
|
|
209
|
+
disposeGroundShadow(v.retired.shadow);
|
|
210
|
+
}
|
|
211
|
+
v.retired.shadow = v.shadow;
|
|
212
|
+
v.shadow?.mesh.removeFromParent();
|
|
213
|
+
v.shadow?.floor.removeFromParent();
|
|
214
|
+
v.shadow = v.built && options.groundShadow !== false ? createGroundShadow(v.renderer, v.scene, v.built) : undefined;
|
|
215
|
+
if (v.keyLight) {
|
|
216
|
+
disposeKeyLight(v.keyLight);
|
|
217
|
+
}
|
|
218
|
+
v.keyLight = v.built ? createKeyLight(v.built, options.shadows !== false) : undefined;
|
|
219
|
+
if (v.keyLight) {
|
|
220
|
+
v.scene.add(v.keyLight);
|
|
221
|
+
}
|
|
222
|
+
// The shadow map is rendered once, as the scene doesn't move, or each frame while something animates
|
|
223
|
+
Object.assign(v.renderer.shadowMap, { needsUpdate: true, autoUpdate: !!v.built && v.built.animatedItems.length > 0 });
|
|
224
|
+
}
|
|
225
|
+
if (changes.has("camera")) {
|
|
226
|
+
const { near = 0.1, far = 2000, ...rest } = options.camera ?? { type: "Perspective" };
|
|
227
|
+
v.camera = v.cameras[rest.type];
|
|
228
|
+
Object.assign(v.camera, { near, far, ...(rest.type === "Perspective" ? { fov: rest.fov ?? 45 } : {}) });
|
|
229
|
+
v.controls.orbit.object = v.camera;
|
|
230
|
+
}
|
|
231
|
+
if (changes.has("controls")) {
|
|
232
|
+
configureControls(v.controls, options.controls ?? {});
|
|
233
|
+
}
|
|
234
|
+
// As the r3f renderer, the scene is fitted at first, and for a new camera or canvas aspect
|
|
235
|
+
if ((changes.has("build") && !v.fitted) || changes.has("camera")) {
|
|
236
|
+
fitPose(v);
|
|
237
|
+
}
|
|
238
|
+
if (changes.has("gizmo") || changes.has("camera")) {
|
|
239
|
+
updateGizmo(v);
|
|
240
|
+
}
|
|
241
|
+
if (changes.has("lights")) {
|
|
242
|
+
updateLights(v);
|
|
243
|
+
}
|
|
244
|
+
if (changes.has("quality")) {
|
|
245
|
+
const quality = options.quality ?? "high";
|
|
246
|
+
v.governor = { quality, tier: quality === "auto" ? "high" : quality, sum: 0, count: 0 };
|
|
247
|
+
v.renderer.setPixelRatio(getPixelRatio(v.governor.tier, window.devicePixelRatio));
|
|
248
|
+
}
|
|
249
|
+
if ((changes.has("paint") || changes.has("build")) && v.built) {
|
|
250
|
+
const start = performance.now();
|
|
251
|
+
paintScene(v.built, {
|
|
252
|
+
...v.structure,
|
|
253
|
+
selectedIds: options.selectedIds,
|
|
254
|
+
hoveredIds: new Set(options.hoveredIds),
|
|
255
|
+
hoveredTarget: v.pointer.hovered?.target,
|
|
256
|
+
dimensionsClickable: options.onClickGroup !== undefined,
|
|
257
|
+
});
|
|
258
|
+
v.stats.paintMs = performance.now() - start;
|
|
259
|
+
}
|
|
260
|
+
if (changes.has("dimensions") && v.built) {
|
|
261
|
+
v.built.dimensions.visible = options.showDimensions ?? true;
|
|
262
|
+
}
|
|
263
|
+
if (changes.has("overlays") || changes.has("build")) {
|
|
264
|
+
updateOverlays(v);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
function rebuild<Content>(v: Viewer<Content>): void {
|
|
269
|
+
if (v.retired.built) {
|
|
270
|
+
disposeScene(v.retired.built);
|
|
271
|
+
}
|
|
272
|
+
v.retired.built = v.built;
|
|
273
|
+
v.built?.root.removeFromParent();
|
|
274
|
+
const { options, compiled } = v;
|
|
275
|
+
v.built =
|
|
276
|
+
compiled &&
|
|
277
|
+
buildScene(compiled, {
|
|
278
|
+
...options,
|
|
279
|
+
structure: v.structure,
|
|
280
|
+
geometries: v.geometries,
|
|
281
|
+
atlas: v.atlas,
|
|
282
|
+
textures: v.textures,
|
|
283
|
+
onTextureLoaded: () => invalidate(v),
|
|
284
|
+
});
|
|
285
|
+
if (v.built) {
|
|
286
|
+
v.scene.add(v.built.root);
|
|
287
|
+
v.built.dimensions.visible = options.showDimensions ?? true;
|
|
288
|
+
// Compiled in parallel where the driver can, as three.js's compileAsync example, and after the studio arrived, which they use
|
|
289
|
+
(options.studio === false ? Promise.resolve() : loadStudio(v))
|
|
290
|
+
.catch(() => undefined)
|
|
291
|
+
.then(() => v.renderer.compileAsync(v.scene, v.camera))
|
|
292
|
+
.then(() => onLoaded(v))
|
|
293
|
+
.catch(() => undefined);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/** Three deletes a shader program once no material uses it, so the previous build's are disposed after the new build's took the programs over */
|
|
298
|
+
function disposeRetired<Content>(v: Viewer<Content>): void {
|
|
299
|
+
if (v.retired.built) {
|
|
300
|
+
disposeScene(v.retired.built);
|
|
301
|
+
}
|
|
302
|
+
if (v.retired.shadow) {
|
|
303
|
+
disposeGroundShadow(v.retired.shadow);
|
|
304
|
+
}
|
|
305
|
+
Object.assign(v.retired, { built: undefined, shadow: undefined });
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
function onLoaded<Content>(v: Viewer<Content>): void {
|
|
309
|
+
if (!v.disposed) {
|
|
310
|
+
v.loaded = true;
|
|
311
|
+
v.options.onLoad?.();
|
|
312
|
+
invalidate(v);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
function renderFrame<Content>(v: Viewer<Content>, now: number): void {
|
|
317
|
+
const { loop, renderer } = v;
|
|
318
|
+
const start = performance.now();
|
|
319
|
+
const interval = loop.last ? now - loop.last : 0;
|
|
320
|
+
loop.frame = 0;
|
|
321
|
+
loop.last = now;
|
|
322
|
+
if (v.pending.size > 0) {
|
|
323
|
+
apply(v, new Set(v.pending));
|
|
324
|
+
v.pending.clear();
|
|
325
|
+
}
|
|
326
|
+
const { built, camera, gizmo } = v;
|
|
327
|
+
renderer.info.reset();
|
|
328
|
+
const moving = v.controls.orbit.update(Math.min(interval / 1000, 0.1)) || !!gizmo?.animating;
|
|
329
|
+
if (moving && loop.wasMoving && v.governor && recordFrame(v.governor, interval)) {
|
|
330
|
+
renderer.setPixelRatio(getPixelRatio(v.governor.tier, window.devicePixelRatio));
|
|
331
|
+
}
|
|
332
|
+
loop.wasMoving = moving;
|
|
333
|
+
const animated = !!built && built.animatedItems.length > 0;
|
|
334
|
+
if (animated) {
|
|
335
|
+
loop.seconds += Math.min(interval / 1000, 0.1);
|
|
336
|
+
animateScene(built, loop.seconds);
|
|
337
|
+
}
|
|
338
|
+
camera.updateMatrixWorld();
|
|
339
|
+
if (built) {
|
|
340
|
+
cullScene(built, camera.position);
|
|
341
|
+
}
|
|
342
|
+
if (v.loaded) {
|
|
343
|
+
renderer.render(v.scene, camera);
|
|
344
|
+
disposeRetired(v);
|
|
345
|
+
}
|
|
346
|
+
gizmo?.render();
|
|
347
|
+
v.overlays.renderer.render(v.overlays.scene, camera);
|
|
348
|
+
Object.assign(v.stats, { frames: v.stats.frames + 1, frameMs: performance.now() - start, drawCalls: renderer.info.render.calls });
|
|
349
|
+
if (moving || animated) {
|
|
350
|
+
invalidate(v);
|
|
351
|
+
} else {
|
|
352
|
+
loop.last = 0;
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
/** As the r3f renderer, a resize refits only when the canvas' aspect changes */
|
|
357
|
+
function onResize<Content>(v: Viewer<Content>, rect: DOMRectReadOnly): void {
|
|
358
|
+
const aspectChanged = rect.width / rect.height !== v.size.width / v.size.height;
|
|
359
|
+
Object.assign(v.size, { width: rect.width, height: rect.height });
|
|
360
|
+
v.renderer.setSize(rect.width, rect.height, false);
|
|
361
|
+
v.overlays.renderer.setSize(rect.width, rect.height);
|
|
362
|
+
v.gizmo?.update();
|
|
363
|
+
if (aspectChanged) {
|
|
364
|
+
fitPose(v);
|
|
365
|
+
}
|
|
366
|
+
// Resizing cleared the canvas; rendered now, before the browser paints, instead of the next frame, so it never shows blank
|
|
367
|
+
if (v.loop.frame) {
|
|
368
|
+
cancelAnimationFrame(v.loop.frame);
|
|
369
|
+
v.loop.frame = 0;
|
|
370
|
+
}
|
|
371
|
+
if (rect.width > 0 && rect.height > 0 && !v.disposed) {
|
|
372
|
+
renderFrame(v, performance.now());
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
/** Fits the projection to the scene, and moves the camera to look from the view; as the r3f renderer, the target stays where it was panned to */
|
|
377
|
+
function fitPose<Content>(v: Viewer<Content>): void {
|
|
378
|
+
const placement = fitScene(v);
|
|
379
|
+
if (placement) {
|
|
380
|
+
const { x, y, z } = placement.direction;
|
|
381
|
+
setPose(v.controls, new Vector3(x, y, z).multiplyScalar(placement.distance), v.controls.orbit.target);
|
|
382
|
+
v.fitted = true;
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
function fitScene<Content>(v: Viewer<Content>): Placement | undefined {
|
|
387
|
+
const { scene, view = "front", bufferZones = {}, fitPadding = 0 } = v.options;
|
|
388
|
+
return scene && fitCamera(v.camera, { view, size: scene.size_deprecated, canvas: v.size, bufferZones, fitPadding });
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
function updateGizmo<Content>(v: Viewer<Content>): void {
|
|
392
|
+
v.gizmo?.dispose();
|
|
393
|
+
const { gizmo } = v.options;
|
|
394
|
+
v.gizmo = gizmo && createGizmo(v.camera, v.renderer, v.container, gizmo).attachControls(v.controls.orbit);
|
|
395
|
+
v.gizmo?.addEventListener("change", () => invalidate(v));
|
|
396
|
+
v.gizmo?.addEventListener("start", () => onGizmoStart(v));
|
|
397
|
+
// Its labels are drawn once, so ones drawn before the page's web fonts loaded are redrawn when they have
|
|
398
|
+
const created = v.gizmo;
|
|
399
|
+
if (created && document.fonts.status !== "loaded") {
|
|
400
|
+
document.fonts.ready.then(() => redrawGizmo(v, created)).catch(() => undefined);
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
/** Unless the gizmo was replaced or the viewer disposed meanwhile */
|
|
405
|
+
function redrawGizmo<Content>(v: Viewer<Content>, gizmo: ViewportGizmo): void {
|
|
406
|
+
if (v.gizmo === gizmo && !v.disposed) {
|
|
407
|
+
gizmo.set(gizmo.options);
|
|
408
|
+
invalidate(v);
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
/**
|
|
413
|
+
* A click turns the camera, which can also move the target back to the scene's center and fit the scene. The gizmo turns around its target at the
|
|
414
|
+
* distance it read when pressed, its internal `_distance`, so that's set to the fitted one
|
|
415
|
+
*/
|
|
416
|
+
function onGizmoStart<Content>(v: Viewer<Content>): void {
|
|
417
|
+
const { gizmo } = v.options;
|
|
418
|
+
const reset = gizmo?.type === "Viewcube" ? gizmo.viewcubeProps?.resetZoomAndPanOnClick : gizmo?.viewportProps?.resetZoomAndPanOnClick;
|
|
419
|
+
const placement = v.gizmo?.animating && reset ? fitScene(v) : undefined;
|
|
420
|
+
if (placement) {
|
|
421
|
+
v.controls.orbit.target.set(0, 0, 0);
|
|
422
|
+
Object.assign(v.gizmo!, { _distance: placement.distance });
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
function onPointerMove<Content>(v: Viewer<Content>, event: PointerEvent): void {
|
|
427
|
+
v.pointer.last = event;
|
|
428
|
+
v.pointer.frame ||= requestAnimationFrame(() => hoverAtPointer(v));
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
/** One pick per frame at most, paused while the camera is dragged, as the r3f renderer's ControlsWrapper paused pointer events */
|
|
432
|
+
function hoverAtPointer<Content>(v: Viewer<Content>): void {
|
|
433
|
+
v.pointer.frame = 0;
|
|
434
|
+
if (v.pointer.last && !v.controls.drag.moved) {
|
|
435
|
+
setHovered(v, pick(v, v.pointer.last), v.pointer.last);
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
function onClick<Content>(v: Viewer<Content>, event: MouseEvent): void {
|
|
440
|
+
const { down } = v.pointer;
|
|
441
|
+
const clicked = !!down && Math.hypot(event.clientX - down.x, event.clientY - down.y) <= clickTolerance;
|
|
442
|
+
const hit = clicked ? pick(v, event) : undefined;
|
|
443
|
+
const target = hit && v.compiled!.targets[hit.target];
|
|
444
|
+
const args = hit && getGroupArgs(v, hit);
|
|
445
|
+
if (hit && target?.kind === "relation") {
|
|
446
|
+
v.options.onClickRelation?.(target.relation, { nativeEvent: event, point: hit.point });
|
|
447
|
+
} else if (hit && args) {
|
|
448
|
+
v.options.onClickGroup?.(...args, { nativeEvent: event, point: hit.point });
|
|
449
|
+
} else if (clicked) {
|
|
450
|
+
v.options.onPointerMissed?.(event);
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
function onContextMenu<Content>(v: Viewer<Content>, event: MouseEvent): void {
|
|
455
|
+
const hit = pick(v, event);
|
|
456
|
+
const args = hit && getGroupArgs(v, hit);
|
|
457
|
+
if (hit && args) {
|
|
458
|
+
v.options.onContextMenuGroup?.(...args, event.x, event.y, { nativeEvent: event, point: hit.point });
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
function setHovered<Content>(v: Viewer<Content>, hit: Hit | undefined, event: PointerEvent): void {
|
|
463
|
+
const previous = v.pointer.hovered;
|
|
464
|
+
v.pointer.hovered = hit;
|
|
465
|
+
v.canvas.style.cursor = hit ? "pointer" : "";
|
|
466
|
+
if (hit?.target === previous?.target) {
|
|
467
|
+
return;
|
|
468
|
+
}
|
|
469
|
+
if (previous && getGroupArgs(v, previous)) {
|
|
470
|
+
v.options.onHoverGroup?.(undefined, undefined, undefined, { nativeEvent: event, point: previous.point });
|
|
471
|
+
}
|
|
472
|
+
const args = hit && getGroupArgs(v, hit);
|
|
473
|
+
if (hit && args) {
|
|
474
|
+
v.options.onHoverGroup?.(...args, { nativeEvent: event, point: hit.point });
|
|
475
|
+
}
|
|
476
|
+
v.pending.add("paint").add("overlays");
|
|
477
|
+
invalidate(v);
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
/** The first interactive item under the pointer: groups with an id that aren't disabled, clickable dimensions and active relations */
|
|
481
|
+
function pick<Content>(v: Viewer<Content>, event: MouseEvent): Hit | undefined {
|
|
482
|
+
const { built } = v;
|
|
483
|
+
if (!built) {
|
|
484
|
+
return undefined;
|
|
485
|
+
}
|
|
486
|
+
const start = performance.now();
|
|
487
|
+
const rect = v.canvas.getBoundingClientRect();
|
|
488
|
+
raycaster.setFromCamera(ndc.set(((event.clientX - rect.left) / rect.width) * 2 - 1, -((event.clientY - rect.top) / rect.height) * 2 + 1), v.camera);
|
|
489
|
+
const hits = raycaster.intersectObjects(getPickables(built), false);
|
|
490
|
+
v.stats.pickMs = performance.now() - start;
|
|
491
|
+
for (const hit of hits) {
|
|
492
|
+
const item = getPickedItem(built, hit);
|
|
493
|
+
const target = item === undefined ? undefined : built.compiled.items[item]!.target;
|
|
494
|
+
const kind = target === undefined ? undefined : built.compiled.targets[target]!;
|
|
495
|
+
const interactive =
|
|
496
|
+
kind?.kind === "group"
|
|
497
|
+
? kind.id !== "" && !built.targetStates[target!]!.disabled
|
|
498
|
+
: kind?.kind === "dimension"
|
|
499
|
+
? kind.id !== undefined && v.options.activeRelations === undefined && v.options.onClickGroup !== undefined
|
|
500
|
+
: kind !== undefined;
|
|
501
|
+
if (interactive) {
|
|
502
|
+
return { item: item!, target: target!, point: hit.point.clone() };
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
return undefined;
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
/** The id, top-level group's data and the hit mesh's group's data of a group or dimension hit */
|
|
509
|
+
function getGroupArgs<Content>(
|
|
510
|
+
v: Viewer<Content>,
|
|
511
|
+
hit: Hit
|
|
512
|
+
): readonly [string, Record<string, string> | undefined, Record<string, string> | undefined] | undefined {
|
|
513
|
+
const target = v.compiled!.targets[hit.target]!;
|
|
514
|
+
switch (target.kind) {
|
|
515
|
+
case "group":
|
|
516
|
+
return [target.id, target.rootData, v.compiled!.items[hit.item]!.data];
|
|
517
|
+
case "dimension":
|
|
518
|
+
return [target.id!, undefined, undefined];
|
|
519
|
+
default:
|
|
520
|
+
return undefined;
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
/** The scene is lit by a photographed studio as its environment, or without it by a plain sky and floor light; apps can add their own lights */
|
|
525
|
+
function updateLights<Content>(v: Viewer<Content>): void {
|
|
526
|
+
const { scene, lights, environmentIntensity = studioIntensity, studio = true } = v.options;
|
|
527
|
+
v.lights.clear();
|
|
528
|
+
(typeof lights === "function" ? (scene ? lights(scene) : []) : (lights ?? [])).forEach((o) => v.lights.add(o));
|
|
529
|
+
v.scene.environmentIntensity = environmentIntensity;
|
|
530
|
+
v.scene.environment = studio ? (v.studio.texture ?? null) : null;
|
|
531
|
+
if (studio) {
|
|
532
|
+
loadStudio(v).catch(() => undefined);
|
|
533
|
+
} else {
|
|
534
|
+
// The studio emulated by three-point lighting: a weak sky and floor light, so faces differ by which way they turn, the key light, a fill from
|
|
535
|
+
// the other side, so the key light's shadow side isn't flat, and a rim light from behind, which outlines edges
|
|
536
|
+
const [fill, rim] = [
|
|
537
|
+
new DirectionalLight(0xffffff, environmentIntensity * fillIntensity),
|
|
538
|
+
new DirectionalLight(0xffffff, environmentIntensity * rimIntensity),
|
|
539
|
+
];
|
|
540
|
+
fill.position.copy(fillDirection);
|
|
541
|
+
rim.position.copy(rimDirection);
|
|
542
|
+
v.lights.add(new HemisphereLight(0xffffff, floorColor, environmentIntensity * skyIntensity), fill, rim);
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
/** The studio, loaded once, its own chunk; set when it arrives, so the scene's shaders are compiled after it, with it */
|
|
547
|
+
function loadStudio<Content>(v: Viewer<Content>): Promise<unknown> {
|
|
548
|
+
v.studio.loading ??= import("../environment/studio.js")
|
|
549
|
+
.then(({ loadStudio: load }) => load())
|
|
550
|
+
.then((texture) => {
|
|
551
|
+
if (v.disposed) {
|
|
552
|
+
texture.dispose();
|
|
553
|
+
return texture;
|
|
554
|
+
}
|
|
555
|
+
v.studio.texture = texture;
|
|
556
|
+
v.scene.environment = v.options.studio === false ? null : texture;
|
|
557
|
+
invalidate(v);
|
|
558
|
+
return texture;
|
|
559
|
+
});
|
|
560
|
+
return v.studio.loading;
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
function updateOverlays<Content>(v: Viewer<Content>): void {
|
|
564
|
+
const { compiled, options } = v;
|
|
565
|
+
const target = v.pointer.hovered && compiled?.targets[v.pointer.hovered.target];
|
|
566
|
+
const overlays: Array<Overlay<Content>> = [];
|
|
567
|
+
options.popovers?.forEach(
|
|
568
|
+
(p) =>
|
|
569
|
+
compiled &&
|
|
570
|
+
overlays.push({ key: `popover:${p.id}`, position: new Vector3(p.pos.x, p.pos.y, p.pos.z).applyMatrix4(compiled.matrix), content: p.content })
|
|
571
|
+
);
|
|
572
|
+
if (target?.kind === "group") {
|
|
573
|
+
options.getTooltips?.(target.id).forEach((p) =>
|
|
574
|
+
overlays.push({
|
|
575
|
+
key: `tooltip:${p.id}`,
|
|
576
|
+
position: new Vector3(p.pos.x, p.pos.y, p.pos.z).applyMatrix4(compiled!.roots[target.root]!),
|
|
577
|
+
content: p.content,
|
|
578
|
+
})
|
|
579
|
+
);
|
|
580
|
+
}
|
|
581
|
+
setOverlays(v.overlays, overlays, options.onOverlays);
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
/** The studio's share of the light, the key light giving the rest, so the key light's shadows show */
|
|
585
|
+
const studioIntensity = 0.6;
|
|
586
|
+
/** Without the studio, of its three-point lighting, by the environment's intensity; the key light is the studio's, the floor as the studio's */
|
|
587
|
+
const skyIntensity = 1.5;
|
|
588
|
+
const fillIntensity = 1.5;
|
|
589
|
+
const rimIntensity = 2;
|
|
590
|
+
const floorColor = 0xe6e6e6;
|
|
591
|
+
/** From the right, opposite the key light from the upper left front, and lower; the rim light from behind and above */
|
|
592
|
+
const fillDirection = new Vector3(1.5, 0.5, 1);
|
|
593
|
+
const rimDirection = new Vector3(0.5, 1.5, -1.5);
|
|
594
|
+
|
|
595
|
+
const raycaster = new Raycaster();
|
|
596
|
+
const ndc = new Vector2();
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import type { ThreeEvent } from "@react-three/fiber";
|
|
2
|
-
import React from "react";
|
|
3
|
-
import type { HotSpot } from "../../abstract-3d.js";
|
|
4
|
-
export interface HotSpotInfo {
|
|
5
|
-
readonly replaceId: string;
|
|
6
|
-
readonly replaceFromId: string;
|
|
7
|
-
readonly replaceOutlet: string;
|
|
8
|
-
readonly replaceToId: string;
|
|
9
|
-
readonly replaceInlet: string;
|
|
10
|
-
}
|
|
11
|
-
export declare const ReactHotSpots: React.MemoExoticComponent<({ hotSpots, showHotSpotTexts, hotSpotTexts, hotSpotZAdjPos, activeHotSpots, onClickHotSpot, }: {
|
|
12
|
-
readonly hotSpots?: ReadonlyArray<HotSpot>;
|
|
13
|
-
readonly showHotSpotTexts: boolean;
|
|
14
|
-
readonly hotSpotZAdjPos: number;
|
|
15
|
-
readonly hotSpotTexts?: Record<string, string>;
|
|
16
|
-
readonly activeHotSpots: Record<string, HotSpotInfo> | undefined;
|
|
17
|
-
readonly onClickHotSpot?: (hotSpot: HotSpotInfo, e: ThreeEvent<MouseEvent>) => void;
|
|
18
|
-
}) => React.JSX.Element>;
|
|
19
|
-
export declare function ReactHotSpot({ h, hotSpotZAdjPos, showHotSpotTexts, hotSpotTexts, activeHotSpots, onClickHotSpot, }: {
|
|
20
|
-
readonly h: HotSpot;
|
|
21
|
-
readonly hotSpotZAdjPos: number;
|
|
22
|
-
readonly showHotSpotTexts: boolean;
|
|
23
|
-
readonly hotSpotTexts?: Record<string, string>;
|
|
24
|
-
readonly activeHotSpots: Record<string, HotSpotInfo> | undefined;
|
|
25
|
-
readonly onClickHotSpot?: (hotSpot: HotSpotInfo, e: ThreeEvent<MouseEvent>) => void;
|
|
26
|
-
}): React.JSX.Element;
|
|
27
|
-
//# sourceMappingURL=react-hotspot.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"react-hotspot.d.ts","sourceRoot":"","sources":["../../../src/renderers/react/react-hotspot.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,KAAK,EAAO,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAKzD,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;CAC/B;AAED,eAAO,MAAM,aAAa,4HAQrB;IACD,QAAQ,CAAC,QAAQ,CAAC,EAAE,aAAa,CAAC,OAAO,CAAC,CAAC;IAC3C,QAAQ,CAAC,gBAAgB,EAAE,OAAO,CAAC;IACnC,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/C,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,GAAG,SAAS,CAAC;IACjE,QAAQ,CAAC,cAAc,CAAC,EAAE,CAAC,OAAO,EAAE,WAAW,EAAE,CAAC,EAAE,UAAU,CAAC,UAAU,CAAC,KAAK,IAAI,CAAC;CACrF,KAAG,KAAK,CAAC,GAAG,CAAC,OAAO,CAiBtB,CAAC;AAEF,wBAAgB,YAAY,CAAC,EAC3B,CAAC,EACD,cAAc,EACd,gBAAgB,EAChB,YAAY,EACZ,cAAc,EACd,cAAc,GACf,EAAE;IACD,QAAQ,CAAC,CAAC,EAAE,OAAO,CAAC;IACpB,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,gBAAgB,EAAE,OAAO,CAAC;IACnC,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/C,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,GAAG,SAAS,CAAC;IACjE,QAAQ,CAAC,cAAc,CAAC,EAAE,CAAC,OAAO,EAAE,WAAW,EAAE,CAAC,EAAE,UAAU,CAAC,UAAU,CAAC,KAAK,IAAI,CAAC;CACrF,GAAG,KAAK,CAAC,GAAG,CAAC,OAAO,CA+CpB"}
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { Html } from "@react-three/drei";
|
|
3
|
-
import React from "react";
|
|
4
|
-
import { vec3Scale, vec3Zero } from "../../abstract-3d.js";
|
|
5
|
-
import { ReactMaterial } from "./react-material.js";
|
|
6
|
-
import { ReactMesh } from "./react-mesh.js";
|
|
7
|
-
export const ReactHotSpots = React.memo(({ hotSpots, showHotSpotTexts, hotSpotTexts, hotSpotZAdjPos, activeHotSpots, onClickHotSpot, }) => {
|
|
8
|
-
return (_jsx(_Fragment, { children: hotSpots?.map((h) => (_jsx(ReactHotSpot, { h: h, hotSpotZAdjPos: hotSpotZAdjPos, activeHotSpots: activeHotSpots, hotSpotTexts: hotSpotTexts, onClickHotSpot: onClickHotSpot, showHotSpotTexts: showHotSpotTexts }, h.id))) }));
|
|
9
|
-
});
|
|
10
|
-
export function ReactHotSpot({ h, hotSpotZAdjPos, showHotSpotTexts, hotSpotTexts, activeHotSpots, onClickHotSpot, }) {
|
|
11
|
-
const [hovered, setHovered] = React.useState(false);
|
|
12
|
-
const hotSpot = activeHotSpots ? activeHotSpots[h.id] : undefined;
|
|
13
|
-
const hsPos = h.mesh.geometry.type === "Box" ? h.mesh.geometry.pos : vec3Zero;
|
|
14
|
-
const text = hotSpotTexts?.[h.id];
|
|
15
|
-
const geo = h.mesh.geometry;
|
|
16
|
-
return (_jsxs(_Fragment, { children: [_jsxs("group", { visible: hotSpot !== undefined, ...(hotSpot && {
|
|
17
|
-
onClick: (e) => {
|
|
18
|
-
if (onClickHotSpot) {
|
|
19
|
-
e.stopPropagation();
|
|
20
|
-
onClickHotSpot(hotSpot, e);
|
|
21
|
-
}
|
|
22
|
-
},
|
|
23
|
-
onPointerOver: (e) => {
|
|
24
|
-
e.stopPropagation();
|
|
25
|
-
document.body.style.cursor = "pointer";
|
|
26
|
-
setHovered(true);
|
|
27
|
-
},
|
|
28
|
-
onPointerOut: (_e) => {
|
|
29
|
-
document.body.style.cursor = "auto";
|
|
30
|
-
setHovered(false);
|
|
31
|
-
},
|
|
32
|
-
onContextMenu: (e) => {
|
|
33
|
-
e.stopPropagation();
|
|
34
|
-
},
|
|
35
|
-
}), children: [_jsx(ReactMesh, { mesh: h.mesh, children: _jsx(ReactMaterial, { isText: false, isHotSpot: true, material: h.mesh.material, hovered: hovered }) }), _jsx(ReactMesh, { mesh: { ...h.mesh, geometry: { ...geo, size: vec3Scale(geo.size, 1.0125) } }, children: _jsx(ReactMaterial, { isText: false, isHotSpot: true, drawBackOnly: true, material: { normal: "rgb(0, 0, 0)", opacity: 1.0 }, hovered: hovered }) })] }), hotSpotTexts && text && (_jsx(Html, { position: [hsPos.x, hsPos.y, hotSpotZAdjPos], center: true, children: _jsx("div", { className: `air-states-container ${showHotSpotTexts ? "" : "air-states-container-hidden"}`, children: _jsx("span", { className: "air-states-text", children: text }) }) }))] }));
|
|
36
|
-
}
|
|
37
|
-
//# sourceMappingURL=react-hotspot.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"react-hotspot.js","sourceRoot":"","sources":["../../../src/renderers/react/react-hotspot.tsx"],"names":[],"mappings":"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"}
|