abstract-3d 4.0.0-beta.8 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/lib/abstract-3d.d.ts +6 -1
- package/lib/abstract-3d.d.ts.map +1 -1
- package/lib/abstract-3d.js +24 -2
- package/lib/abstract-3d.js.map +1 -1
- package/lib/renderers/react/react.d.ts.map +1 -1
- package/lib/renderers/react/react.js +9 -3
- package/lib/renderers/react/react.js.map +1 -1
- package/lib/renderers/svg/index.d.ts +1 -1
- package/lib/renderers/svg/index.d.ts.map +1 -1
- package/lib/renderers/svg/svg-geometries/shared.d.ts +14 -2
- package/lib/renderers/svg/svg-geometries/shared.d.ts.map +1 -1
- package/lib/renderers/svg/svg-geometries/shared.js +15 -1
- package/lib/renderers/svg/svg-geometries/shared.js.map +1 -1
- package/lib/renderers/svg/svg-geometries/svg-box.d.ts.map +1 -1
- package/lib/renderers/svg/svg-geometries/svg-box.js +41 -30
- package/lib/renderers/svg/svg-geometries/svg-box.js.map +1 -1
- package/lib/renderers/svg/svg-geometries/svg-cone.d.ts.map +1 -1
- package/lib/renderers/svg/svg-geometries/svg-cone.js +3 -3
- package/lib/renderers/svg/svg-geometries/svg-cone.js.map +1 -1
- package/lib/renderers/svg/svg-geometries/svg-cylinder.d.ts.map +1 -1
- package/lib/renderers/svg/svg-geometries/svg-cylinder.js +18 -6
- package/lib/renderers/svg/svg-geometries/svg-cylinder.js.map +1 -1
- package/lib/renderers/svg/svg-geometries/svg-image.d.ts.map +1 -1
- package/lib/renderers/svg/svg-geometries/svg-image.js +3 -2
- package/lib/renderers/svg/svg-geometries/svg-image.js.map +1 -1
- package/lib/renderers/svg/svg-geometries/svg-line.d.ts.map +1 -1
- package/lib/renderers/svg/svg-geometries/svg-line.js +3 -1
- package/lib/renderers/svg/svg-geometries/svg-line.js.map +1 -1
- package/lib/renderers/svg/svg-geometries/svg-plane.d.ts.map +1 -1
- package/lib/renderers/svg/svg-geometries/svg-plane.js +4 -2
- package/lib/renderers/svg/svg-geometries/svg-plane.js.map +1 -1
- package/lib/renderers/svg/svg-geometries/svg-polygon.d.ts.map +1 -1
- package/lib/renderers/svg/svg-geometries/svg-polygon.js +4 -2
- package/lib/renderers/svg/svg-geometries/svg-polygon.js.map +1 -1
- package/lib/renderers/svg/svg-geometries/svg-shape.d.ts.map +1 -1
- package/lib/renderers/svg/svg-geometries/svg-shape.js +4 -2
- package/lib/renderers/svg/svg-geometries/svg-shape.js.map +1 -1
- package/lib/renderers/svg/svg-geometries/svg-text.js +1 -1
- package/lib/renderers/svg/svg-geometries/svg-text.js.map +1 -1
- package/lib/renderers/svg/svg-order.d.ts +9 -0
- package/lib/renderers/svg/svg-order.d.ts.map +1 -0
- package/lib/renderers/svg/svg-order.js +148 -0
- package/lib/renderers/svg/svg-order.js.map +1 -0
- package/lib/renderers/svg/svg.d.ts.map +1 -1
- package/lib/renderers/svg/svg.js +13 -9
- package/lib/renderers/svg/svg.js.map +1 -1
- package/lib/renderers/three/build/build.d.ts +2 -0
- package/lib/renderers/three/build/build.d.ts.map +1 -1
- package/lib/renderers/three/build/build.js +1 -1
- package/lib/renderers/three/build/build.js.map +1 -1
- package/lib/renderers/three/build/materials.d.ts +5 -1
- package/lib/renderers/three/build/materials.d.ts.map +1 -1
- package/lib/renderers/three/build/materials.js +53 -17
- package/lib/renderers/three/build/materials.js.map +1 -1
- package/lib/renderers/three/environment/studio.d.ts +3 -2
- package/lib/renderers/three/environment/studio.d.ts.map +1 -1
- package/lib/renderers/three/environment/studio.js +4 -3
- package/lib/renderers/three/environment/studio.js.map +1 -1
- package/lib/renderers/three/geometry/geometry.d.ts.map +1 -1
- package/lib/renderers/three/geometry/geometry.js +11 -5
- package/lib/renderers/three/geometry/geometry.js.map +1 -1
- package/lib/renderers/three/shadow/key-light.js +1 -1
- package/lib/renderers/three/viewer/options.d.ts +7 -1
- package/lib/renderers/three/viewer/options.d.ts.map +1 -1
- package/lib/renderers/three/viewer/options.js +1 -0
- package/lib/renderers/three/viewer/options.js.map +1 -1
- package/lib/renderers/three/viewer/viewer.d.ts +5 -0
- package/lib/renderers/three/viewer/viewer.d.ts.map +1 -1
- package/lib/renderers/three/viewer/viewer.js +54 -11
- package/lib/renderers/three/viewer/viewer.js.map +1 -1
- package/package.json +1 -1
- package/src/abstract-3d.ts +30 -2
- package/src/renderers/react/react.tsx +14 -4
- package/src/renderers/svg/index.ts +1 -1
- package/src/renderers/svg/svg-geometries/shared.ts +26 -3
- package/src/renderers/svg/svg-geometries/svg-box.ts +42 -36
- package/src/renderers/svg/svg-geometries/svg-cone.ts +17 -3
- package/src/renderers/svg/svg-geometries/svg-cylinder.ts +25 -6
- package/src/renderers/svg/svg-geometries/svg-image.ts +3 -2
- package/src/renderers/svg/svg-geometries/svg-line.ts +3 -1
- package/src/renderers/svg/svg-geometries/svg-plane.ts +9 -2
- package/src/renderers/svg/svg-geometries/svg-polygon.ts +9 -2
- package/src/renderers/svg/svg-geometries/svg-shape.ts +9 -2
- package/src/renderers/svg/svg-geometries/svg-text.ts +1 -1
- package/src/renderers/svg/svg-order.test.ts +35 -0
- package/src/renderers/svg/svg-order.ts +172 -0
- package/src/renderers/svg/svg.ts +13 -9
- package/src/renderers/three/build/build.ts +3 -1
- package/src/renderers/three/build/materials.ts +57 -17
- package/src/renderers/three/environment/studio.ts +4 -3
- package/src/renderers/three/geometry/geometry.test.ts +17 -5
- package/src/renderers/three/geometry/geometry.ts +12 -5
- package/src/renderers/three/shadow/key-light.ts +1 -1
- package/src/renderers/three/viewer/options.ts +8 -1
- package/src/renderers/three/viewer/viewer.ts +57 -10
|
@@ -17,7 +17,12 @@ const bounds = (mesh: Mesh): ReadonlyArray<number> => {
|
|
|
17
17
|
return [...geometry.boundingBox!.min.toArray(), ...geometry.boundingBox!.max.toArray()].map((v) => Math.round(v * 1000) / 1000 + 0);
|
|
18
18
|
};
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
/** The radius and joined sides' bits, the same for every vertex */
|
|
21
|
+
const bevel = (mesh: Mesh): ReadonlyArray<number> => {
|
|
22
|
+
const attribute = createSolidGeometry(mesh.geometry).getAttribute("bevel");
|
|
23
|
+
const pairs = new Set(Array.from({ length: attribute.count }, (_, i) => `${attribute.getX(i)} ${attribute.getY(i)}`));
|
|
24
|
+
return [...pairs].flatMap((pair) => pair.split(" ").map(Number));
|
|
25
|
+
};
|
|
21
26
|
|
|
22
27
|
describe("getSolidShape", () => {
|
|
23
28
|
it("shares unit geometries between primitives of any size, scaled by the local matrix", () => {
|
|
@@ -81,15 +86,22 @@ describe("createSolidGeometry", () => {
|
|
|
81
86
|
});
|
|
82
87
|
|
|
83
88
|
it("gives boxes with a radius it as the radius their edges are shaded rounded by, with a geometry per radius", () => {
|
|
84
|
-
expect(bevel(box(vec3(1, 1, 1), m, vec3Zero, vec3Zero, [], 4))).toEqual([4]);
|
|
89
|
+
expect(bevel(box(vec3(1, 1, 1), m, vec3Zero, vec3Zero, [], 4))).toEqual([4, 2 ** 12 - 1]);
|
|
85
90
|
expect(key(box(vec3(1, 1, 1), m, vec3Zero, vec3Zero, [], 4))).toBe(key(box(vec3(9, 9, 9), m, vec3Zero, vec3Zero, [], 4)));
|
|
86
91
|
expect(key(box(vec3(1, 1, 1), m, vec3Zero, vec3Zero, [], 4))).not.toBe(key(box(vec3(1, 1, 1), m)));
|
|
87
92
|
});
|
|
88
93
|
|
|
94
|
+
it("rounds all edges of boxes with a radius, or only their rounded edges, a bit each in the order of boxEdges, with a geometry per edges", () => {
|
|
95
|
+
expect(bevel(box(vec3(1, 1, 1), m, vec3Zero, vec3Zero, [], 4, ["BottomBack", "FrontRight", "TopRight"]))).toEqual([4, 1 + 2 ** 7 + 2 ** 11]);
|
|
96
|
+
expect(bevel(box(vec3(1, 1, 1), m, vec3Zero, vec3Zero, [], 4, []))).toEqual([4, 0]);
|
|
97
|
+
expect(key(box(vec3(1, 1, 1), m, vec3Zero, vec3Zero, [], 4, ["TopFront"]))).not.toBe(key(box(vec3(1, 1, 1), m, vec3Zero, vec3Zero, [], 4)));
|
|
98
|
+
});
|
|
99
|
+
|
|
89
100
|
it("keeps boxes without a radius, with holes, and other shapes sharp", () => {
|
|
90
|
-
expect(bevel(box(vec3(1, 1, 1), m))).toEqual([0]);
|
|
91
|
-
expect(bevel(box(vec3(1, 1, 1), m, vec3Zero, vec3Zero, [
|
|
92
|
-
expect(bevel(
|
|
101
|
+
expect(bevel(box(vec3(1, 1, 1), m))).toEqual([0, 0]);
|
|
102
|
+
expect(bevel(box(vec3(1, 1, 1), m, vec3Zero, vec3Zero, [], undefined, ["TopFront"]))).toEqual([0, 0]);
|
|
103
|
+
expect(bevel(box(vec3(1, 1, 1), m, vec3Zero, vec3Zero, [roundHole(vec2(0, 0), 0.1)], 4))).toEqual([0, 0]);
|
|
104
|
+
expect(bevel(sphere(1, m))).toEqual([0, 0]);
|
|
93
105
|
});
|
|
94
106
|
});
|
|
95
107
|
|
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
} from "three";
|
|
17
17
|
import { mergeGeometries } from "three/addons/utils/BufferGeometryUtils.js";
|
|
18
18
|
import type { Hole, Mesh } from "../../../abstract-3d.js";
|
|
19
|
-
import { isZero, vec3, vec3Zero } from "../../../abstract-3d.js";
|
|
19
|
+
import { boxEdges, isZero, vec3, vec3Zero } from "../../../abstract-3d.js";
|
|
20
20
|
import { addHoles, CircleCurve, CYLINDER_SEGMENTS, cylinderCutPositions, holeIsZero } from "../../shared/three-geometry.js";
|
|
21
21
|
import { transform } from "../compile/compile.js";
|
|
22
22
|
|
|
@@ -42,7 +42,10 @@ export function getSolidShape(g: Mesh["geometry"]): SolidShape | undefined {
|
|
|
42
42
|
const size = g.type === "Box" ? g.size : vec3(g.size.x, g.size.y, 1);
|
|
43
43
|
return g.holes?.some((h) => !holeIsZero(h))
|
|
44
44
|
? { key: JSON.stringify([g.type, g.size, g.holes]), local: transform(g.pos, g.rot) }
|
|
45
|
-
: {
|
|
45
|
+
: {
|
|
46
|
+
key: g.type === "Box" && g.radius ? `Box ${g.radius} ${g.roundedEdges?.join() ?? ""}` : g.type,
|
|
47
|
+
local: transform(g.pos, g.rot, undefined, size),
|
|
48
|
+
};
|
|
46
49
|
}
|
|
47
50
|
case "Image":
|
|
48
51
|
return { key: "Plane", local: transform(g.pos, g.rot, undefined, vec3(g.size.x, g.size.y, 1)) };
|
|
@@ -86,9 +89,13 @@ export function createSolidGeometry(g: Mesh["geometry"]): BufferGeometry {
|
|
|
86
89
|
if (!geometry.hasAttribute("uv")) {
|
|
87
90
|
geometry.setAttribute("uv", new Float32BufferAttribute(new Float32Array(count * 2), 2));
|
|
88
91
|
}
|
|
89
|
-
// The radius a unit box's edges are shaded as rounded by, 0 for sharp
|
|
90
|
-
|
|
91
|
-
|
|
92
|
+
// The radius a unit box's edges are shaded as rounded by, 0 for sharp, and a bit per rounded edge, in the order of `boxEdges`; every geometry has
|
|
93
|
+
// it, as a batch's geometries share their attributes
|
|
94
|
+
const bevel =
|
|
95
|
+
geometry instanceof BoxGeometry && g.type === "Box" && g.radius
|
|
96
|
+
? [g.radius, boxEdges.reduce((edges, edge, i) => ((g.roundedEdges?.includes(edge) ?? true) ? edges + 2 ** i : edges), 0)]
|
|
97
|
+
: [0, 0];
|
|
98
|
+
geometry.setAttribute("bevel", new Float32BufferAttribute(Array.from({ length: count }, () => bevel).flat(), 2));
|
|
92
99
|
Object.keys(geometry.attributes).forEach((name) => {
|
|
93
100
|
if (name !== "position" && name !== "normal" && name !== "uv" && name !== "bevel") {
|
|
94
101
|
geometry.deleteAttribute(name);
|
|
@@ -8,7 +8,7 @@ import { getShadowCasters } from "./shadow.js";
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
const direction = new Vector3(-1, 2, 1.2).normalize();
|
|
11
|
-
const intensity =
|
|
11
|
+
const intensity = 1.6;
|
|
12
12
|
const mapSize = 2048;
|
|
13
13
|
/** Of the key light kept out of its shadows, so faces under an overhang, e.g. a recessed base frame, aren't black */
|
|
14
14
|
const shadowIntensity = 0.6;
|
|
@@ -43,8 +43,14 @@ export type ViewerOptions<Content> = {
|
|
|
43
43
|
readonly gizmo?: ControlsHelper;
|
|
44
44
|
/** Lights added to the photographed studio and key light that light the scene; none by default */
|
|
45
45
|
readonly lights?: ReadonlyArray<Object3D> | ((scene: Scene) => ReadonlyArray<Object3D>);
|
|
46
|
-
/** Of the studio's light, 0.
|
|
46
|
+
/** Of the studio's light, or without the studio of its plain light, 0.6 by default, the key light giving the rest */
|
|
47
47
|
readonly environmentIntensity?: number;
|
|
48
|
+
/**
|
|
49
|
+
* The photographed studio's light and reflections, on by default. Off, three-point lighting emulates it, a weak sky and floor light, the key light,
|
|
50
|
+
* a fill and a rim light, without reflections, so metals are satin at most; a first visit then shows the scene about 400 ms sooner, as its shaders
|
|
51
|
+
* compile without the reflection code. Changing it rebuilds the scene
|
|
52
|
+
*/
|
|
53
|
+
readonly studio?: boolean;
|
|
48
54
|
/** A soft shadow on the ground under the scene, rendered once per build; on by default */
|
|
49
55
|
readonly groundShadow?: boolean;
|
|
50
56
|
/** The solids' shadows on themselves, e.g. under a roof, cast by the key light from the upper left front; on by default */
|
|
@@ -90,6 +96,7 @@ export const effects: { readonly [K in keyof ViewerOptions<unknown>]-?: Readonly
|
|
|
90
96
|
gizmo: ["gizmo"],
|
|
91
97
|
lights: ["lights"],
|
|
92
98
|
environmentIntensity: ["lights"],
|
|
99
|
+
studio: ["build", "lights"],
|
|
93
100
|
groundShadow: ["shadow"],
|
|
94
101
|
shadows: ["shadow"],
|
|
95
102
|
quality: ["quality"],
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import {
|
|
2
2
|
type BufferGeometry,
|
|
3
|
+
DirectionalLight,
|
|
3
4
|
Group,
|
|
5
|
+
HemisphereLight,
|
|
4
6
|
NeutralToneMapping,
|
|
5
7
|
OrthographicCamera,
|
|
6
8
|
PerspectiveCamera,
|
|
@@ -71,6 +73,8 @@ export type Viewer<Content> = {
|
|
|
71
73
|
structure: StructureState;
|
|
72
74
|
compiled?: CompiledScene;
|
|
73
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 };
|
|
74
78
|
gizmo?: ViewportGizmo;
|
|
75
79
|
shadow?: GroundShadow;
|
|
76
80
|
/** The key light that casts the solids' shadows on themselves */
|
|
@@ -127,13 +131,16 @@ export function createViewer<Content>(container: HTMLElement, options: ViewerOpt
|
|
|
127
131
|
stats: { frames: 0, frameMs: 0, drawCalls: 0, pickMs: 0, paintMs: 0 },
|
|
128
132
|
structure: { activeComponents: options.activeComponents, relationsActive: options.activeRelations !== undefined },
|
|
129
133
|
studio: {},
|
|
134
|
+
retired: {},
|
|
130
135
|
loaded: false,
|
|
131
136
|
fitted: false,
|
|
132
137
|
disposed: false,
|
|
133
138
|
};
|
|
134
139
|
v.scene.add(v.lights);
|
|
135
140
|
// Loaded while the first frame builds the scene
|
|
136
|
-
|
|
141
|
+
if (options.studio !== false) {
|
|
142
|
+
loadStudio(v).catch(() => undefined);
|
|
143
|
+
}
|
|
137
144
|
const signal = { signal: v.listeners.signal };
|
|
138
145
|
canvas.addEventListener("pointermove", (e) => onPointerMove(v, e), signal);
|
|
139
146
|
canvas.addEventListener("pointerleave", (e) => setHovered(v, undefined, e), signal);
|
|
@@ -166,6 +173,7 @@ export function disposeViewer<Content>(v: Viewer<Content>): void {
|
|
|
166
173
|
v.resizeObserver.disconnect();
|
|
167
174
|
v.controls.orbit.dispose();
|
|
168
175
|
v.gizmo?.dispose();
|
|
176
|
+
disposeRetired(v);
|
|
169
177
|
if (v.built) {
|
|
170
178
|
disposeScene(v.built);
|
|
171
179
|
}
|
|
@@ -197,9 +205,12 @@ function apply<Content>(v: Viewer<Content>, changes: ReadonlySet<Change>): void
|
|
|
197
205
|
rebuild(v);
|
|
198
206
|
}
|
|
199
207
|
if (changes.has("shadow") || changes.has("build")) {
|
|
200
|
-
if (v.shadow) {
|
|
201
|
-
disposeGroundShadow(v.shadow);
|
|
208
|
+
if (v.retired.shadow) {
|
|
209
|
+
disposeGroundShadow(v.retired.shadow);
|
|
202
210
|
}
|
|
211
|
+
v.retired.shadow = v.shadow;
|
|
212
|
+
v.shadow?.mesh.removeFromParent();
|
|
213
|
+
v.shadow?.floor.removeFromParent();
|
|
203
214
|
v.shadow = v.built && options.groundShadow !== false ? createGroundShadow(v.renderer, v.scene, v.built) : undefined;
|
|
204
215
|
if (v.keyLight) {
|
|
205
216
|
disposeKeyLight(v.keyLight);
|
|
@@ -255,9 +266,11 @@ function apply<Content>(v: Viewer<Content>, changes: ReadonlySet<Change>): void
|
|
|
255
266
|
}
|
|
256
267
|
|
|
257
268
|
function rebuild<Content>(v: Viewer<Content>): void {
|
|
258
|
-
if (v.built) {
|
|
259
|
-
disposeScene(v.built);
|
|
269
|
+
if (v.retired.built) {
|
|
270
|
+
disposeScene(v.retired.built);
|
|
260
271
|
}
|
|
272
|
+
v.retired.built = v.built;
|
|
273
|
+
v.built?.root.removeFromParent();
|
|
261
274
|
const { options, compiled } = v;
|
|
262
275
|
v.built =
|
|
263
276
|
compiled &&
|
|
@@ -273,7 +286,7 @@ function rebuild<Content>(v: Viewer<Content>): void {
|
|
|
273
286
|
v.scene.add(v.built.root);
|
|
274
287
|
v.built.dimensions.visible = options.showDimensions ?? true;
|
|
275
288
|
// Compiled in parallel where the driver can, as three.js's compileAsync example, and after the studio arrived, which they use
|
|
276
|
-
loadStudio(v)
|
|
289
|
+
(options.studio === false ? Promise.resolve() : loadStudio(v))
|
|
277
290
|
.catch(() => undefined)
|
|
278
291
|
.then(() => v.renderer.compileAsync(v.scene, v.camera))
|
|
279
292
|
.then(() => onLoaded(v))
|
|
@@ -281,6 +294,17 @@ function rebuild<Content>(v: Viewer<Content>): void {
|
|
|
281
294
|
}
|
|
282
295
|
}
|
|
283
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
|
+
|
|
284
308
|
function onLoaded<Content>(v: Viewer<Content>): void {
|
|
285
309
|
if (!v.disposed) {
|
|
286
310
|
v.loaded = true;
|
|
@@ -317,6 +341,7 @@ function renderFrame<Content>(v: Viewer<Content>, now: number): void {
|
|
|
317
341
|
}
|
|
318
342
|
if (v.loaded) {
|
|
319
343
|
renderer.render(v.scene, camera);
|
|
344
|
+
disposeRetired(v);
|
|
320
345
|
}
|
|
321
346
|
gizmo?.render();
|
|
322
347
|
v.overlays.renderer.render(v.overlays.scene, camera);
|
|
@@ -496,12 +521,26 @@ function getGroupArgs<Content>(
|
|
|
496
521
|
}
|
|
497
522
|
}
|
|
498
523
|
|
|
499
|
-
/** The scene is lit by a photographed studio as its environment; apps can add their own lights */
|
|
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 */
|
|
500
525
|
function updateLights<Content>(v: Viewer<Content>): void {
|
|
501
|
-
const { scene, lights, environmentIntensity = studioIntensity } = v.options;
|
|
526
|
+
const { scene, lights, environmentIntensity = studioIntensity, studio = true } = v.options;
|
|
502
527
|
v.lights.clear();
|
|
503
528
|
(typeof lights === "function" ? (scene ? lights(scene) : []) : (lights ?? [])).forEach((o) => v.lights.add(o));
|
|
504
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
|
+
}
|
|
505
544
|
}
|
|
506
545
|
|
|
507
546
|
/** The studio, loaded once, its own chunk; set when it arrives, so the scene's shaders are compiled after it, with it */
|
|
@@ -514,7 +553,7 @@ function loadStudio<Content>(v: Viewer<Content>): Promise<unknown> {
|
|
|
514
553
|
return texture;
|
|
515
554
|
}
|
|
516
555
|
v.studio.texture = texture;
|
|
517
|
-
v.scene.environment = texture;
|
|
556
|
+
v.scene.environment = v.options.studio === false ? null : texture;
|
|
518
557
|
invalidate(v);
|
|
519
558
|
return texture;
|
|
520
559
|
});
|
|
@@ -543,7 +582,15 @@ function updateOverlays<Content>(v: Viewer<Content>): void {
|
|
|
543
582
|
}
|
|
544
583
|
|
|
545
584
|
/** The studio's share of the light, the key light giving the rest, so the key light's shadows show */
|
|
546
|
-
const studioIntensity = 0.
|
|
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);
|
|
547
594
|
|
|
548
595
|
const raycaster = new Raycaster();
|
|
549
596
|
const ndc = new Vector2();
|