@volter/threejs-runtime 0.5.65
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/LICENSE +202 -0
- package/NOTICE +12 -0
- package/package.json +62 -0
- package/src/adapter/body-marks.ts +69 -0
- package/src/adapter/constraint.ts +78 -0
- package/src/adapter/first-party-navigation-system.ts +48 -0
- package/src/adapter/hierarchy-marks.ts +156 -0
- package/src/adapter/ingest/scene-capture.ts +865 -0
- package/src/adapter/ingest/structural-ids.ts +139 -0
- package/src/adapter/ingest/visible-capture-window.ts +302 -0
- package/src/adapter/object3d-authoring-subject.ts +50 -0
- package/src/adapter/rapier-physics-adapter.ts +226 -0
- package/src/adapter/reflection-probe.ts +75 -0
- package/src/adapter/renderer-config.ts +116 -0
- package/src/adapter/trigger-volume.ts +29 -0
- package/src/ai/navigation.ts +627 -0
- package/src/animation/animation-clock.ts +479 -0
- package/src/animation/runtime-inspection.ts +45 -0
- package/src/animation/xstate-animation-binding.ts +655 -0
- package/src/animation/xstate-animation-meta.ts +360 -0
- package/src/asset-formats/camera.ts +18 -0
- package/src/asset-formats/collider.ts +66 -0
- package/src/asset-formats/index.ts +45 -0
- package/src/asset-formats/light.ts +50 -0
- package/src/asset-formats/material.ts +93 -0
- package/src/asset-formats/mesh.ts +78 -0
- package/src/asset-formats/particles.ts +426 -0
- package/src/asset-formats/render-env.ts +318 -0
- package/src/asset-formats/tuples.ts +21 -0
- package/src/asset-loaders.ts +242 -0
- package/src/asset-parse-error.ts +29 -0
- package/src/assets.ts +68 -0
- package/src/behavior/xstate-inspection.ts +51 -0
- package/src/defaults.ts +135 -0
- package/src/ecs/object-marks.ts +75 -0
- package/src/ecs/scene-index.ts +439 -0
- package/src/ecs/scene-query.ts +43 -0
- package/src/ecs/user-data.ts +272 -0
- package/src/loader.ts +134 -0
- package/src/physics/collider-dimensions.ts +121 -0
- package/src/physics/collision-system.ts +76 -0
- package/src/physics/physics-registry.ts +83 -0
- package/src/physics/transform-writer.ts +41 -0
- package/src/render/directional-shadow-fit.ts +158 -0
- package/src/render/environment-capture.ts +133 -0
- package/src/render/ibl-override-material.ts +202 -0
- package/src/render/light-camera-factory.ts +103 -0
- package/src/render/matcap-texture.ts +92 -0
- package/src/render/material-factory.ts +201 -0
- package/src/render/mesh-shadow.ts +18 -0
- package/src/render/particles-factory.ts +589 -0
- package/src/render/render-batch-system.ts +260 -0
- package/src/render/render-features.ts +128 -0
- package/src/render/render-settings.ts +71 -0
- package/src/render/soft-particle-depth.ts +360 -0
- package/src/render/spark-renderer-lifecycle.ts +64 -0
- package/src/render/structural-signature.ts +231 -0
- package/src/render/viewport-shading.ts +163 -0
- package/src/render/voxel-surface.ts +358 -0
- package/src/setup/setup-particles.ts +32 -0
- package/src/setup/setup-physics.ts +67 -0
- package/src/setup/setup-renderer.ts +524 -0
- package/src/types-n8ao.d.ts +37 -0
- package/src/types-realism-effects.d.ts +61 -0
- package/vendor/realism-effects/LICENSE.md +21 -0
- package/vendor/realism-effects/UPSTREAM.md +19 -0
- package/vendor/realism-effects/dist/index.cjs +3447 -0
- package/vendor/realism-effects/dist/index.d.ts +59 -0
- package/vendor/realism-effects/dist/index.js +3434 -0
- package/vendor/realism-effects/package.json +23 -0
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared light + camera factory — the ONE place light/camera fallbacks are
|
|
3
|
+
* decided, and it decides them by READING `DEFAULTS.light`/`DEFAULTS.camera`.
|
|
4
|
+
*
|
|
5
|
+
* A second copy hardcoding the same literals (color '#ffffff', fov 60, near
|
|
6
|
+
* 0.1, far 1000) would agree by luck rather than by construction, and any
|
|
7
|
+
* edit to either copy — or to `DEFAULTS` — would silently diverge.
|
|
8
|
+
*
|
|
9
|
+
* This module is now the ONE place that builds a `THREE.Light`/`THREE.Camera`
|
|
10
|
+
* from a `LightDescriptor`/`CameraDescriptor` descriptor, reading its fallbacks from
|
|
11
|
+
* `DEFAULTS` (the single source of truth for scene default values — see
|
|
12
|
+
* `defaults.ts`). Both the loader and the editor import it, so they
|
|
13
|
+
* can no longer disagree.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import * as THREE from 'three';
|
|
17
|
+
// Area lights. `RectAreaLight` requires its LTC lookup-texture uniforms
|
|
18
|
+
// initialized once before the first instance is constructed; three.js ships
|
|
19
|
+
// that setup as an addon, not part of core. Imported via the
|
|
20
|
+
// `three/addons/*` alias (package.json `exports`: `"./addons/*":
|
|
21
|
+
// "./examples/jsm/*"` — the identical file as
|
|
22
|
+
// `three/examples/jsm/lights/RectAreaLightUniformsLib.js`) because a literal
|
|
23
|
+
// `/examples/` specifier trips this repo's game/editor-example import guard —
|
|
24
|
+
// a same-substring false positive against our own `src/examples/` convention,
|
|
25
|
+
// unrelated to three.js's addon folder naming.
|
|
26
|
+
import { RectAreaLightUniformsLib } from 'three/addons/lights/RectAreaLightUniformsLib.js';
|
|
27
|
+
import type { CameraDescriptor } from '../asset-formats/camera';
|
|
28
|
+
import type { LightDescriptor } from '../asset-formats/light';
|
|
29
|
+
import { DEFAULTS } from '../defaults';
|
|
30
|
+
|
|
31
|
+
let rectAreaLightUniformsInitialized = false;
|
|
32
|
+
|
|
33
|
+
/** Build a Three.js light from a `LightDescriptor` descriptor. */
|
|
34
|
+
export function createLight(def: LightDescriptor): THREE.Light {
|
|
35
|
+
const d = DEFAULTS.light;
|
|
36
|
+
const color = def.color ?? d.color;
|
|
37
|
+
const intensity = def.intensity ?? d.intensity;
|
|
38
|
+
|
|
39
|
+
switch (def.type) {
|
|
40
|
+
case 'directional':
|
|
41
|
+
return new THREE.DirectionalLight(color, intensity);
|
|
42
|
+
case 'point':
|
|
43
|
+
return new THREE.PointLight(
|
|
44
|
+
color,
|
|
45
|
+
intensity,
|
|
46
|
+
def.distance ?? d.distance,
|
|
47
|
+
def.decay ?? d.decay,
|
|
48
|
+
);
|
|
49
|
+
case 'spot': {
|
|
50
|
+
const light = new THREE.SpotLight(
|
|
51
|
+
color,
|
|
52
|
+
intensity,
|
|
53
|
+
def.distance ?? d.distance,
|
|
54
|
+
def.angle ?? d.angle,
|
|
55
|
+
def.penumbra ?? d.penumbra,
|
|
56
|
+
);
|
|
57
|
+
light.decay = def.decay ?? d.decay;
|
|
58
|
+
return light;
|
|
59
|
+
}
|
|
60
|
+
case 'hemisphere':
|
|
61
|
+
return new THREE.HemisphereLight(color, def.groundColor ?? d.groundColor, intensity);
|
|
62
|
+
case 'area': {
|
|
63
|
+
// No shadow map is ever wired for this light (RectAreaLight does not
|
|
64
|
+
// support shadows in three.js — see the honesty note on the schema
|
|
65
|
+
// field's .describe(); degrade loudly, not silently).
|
|
66
|
+
if (!rectAreaLightUniformsInitialized) {
|
|
67
|
+
RectAreaLightUniformsLib.init();
|
|
68
|
+
rectAreaLightUniformsInitialized = true;
|
|
69
|
+
}
|
|
70
|
+
return new THREE.RectAreaLight(
|
|
71
|
+
color,
|
|
72
|
+
intensity,
|
|
73
|
+
def.width ?? d.width,
|
|
74
|
+
def.height ?? d.height,
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
default:
|
|
78
|
+
return new THREE.DirectionalLight(color, intensity);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/** Build a Three.js camera from a `CameraDescriptor` descriptor. */
|
|
83
|
+
export function createCamera(def: CameraDescriptor): THREE.Camera {
|
|
84
|
+
const d = DEFAULTS.camera;
|
|
85
|
+
const aspect = (def.width ?? d.width) / (def.height ?? d.height);
|
|
86
|
+
if (def.type === 'orthographic') {
|
|
87
|
+
const size = def.orthoSize ?? d.orthoSize;
|
|
88
|
+
return new THREE.OrthographicCamera(
|
|
89
|
+
-size * aspect,
|
|
90
|
+
size * aspect,
|
|
91
|
+
size,
|
|
92
|
+
-size,
|
|
93
|
+
def.near ?? d.near,
|
|
94
|
+
def.far ?? d.far,
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
return new THREE.PerspectiveCamera(
|
|
98
|
+
def.fov ?? d.fov,
|
|
99
|
+
aspect,
|
|
100
|
+
def.near ?? d.near,
|
|
101
|
+
def.far ?? d.far,
|
|
102
|
+
);
|
|
103
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import * as THREE from 'three';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The neutral matcap sphere, DRAWN rather than shipped.
|
|
5
|
+
*
|
|
6
|
+
* A matcap is just a picture of a lit sphere sampled by view-space normal, so
|
|
7
|
+
* there is nothing to vendor: the same three numbers a shader would use
|
|
8
|
+
* (key, fill, rim) evaluated once per texel produce the image. Keeping it
|
|
9
|
+
* procedural means the diagnostic look has no binary asset, no license, no
|
|
10
|
+
* provenance row, and no way to go missing from a build.
|
|
11
|
+
*
|
|
12
|
+
* The look is deliberately CLAY: one warm key from the upper left, a cool
|
|
13
|
+
* fill from the lower right, a tight rim, and a broad soft highlight — the
|
|
14
|
+
* neutral sculpting material whose whole job is to let form read without
|
|
15
|
+
* colour, texture or authored lighting getting a vote.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
const SIZE = 256;
|
|
19
|
+
|
|
20
|
+
/** sRGB transfer curve — the canvas holds display-referred bytes. */
|
|
21
|
+
function encode(value: number): number {
|
|
22
|
+
const c = Math.min(1, Math.max(0, value));
|
|
23
|
+
return c <= 0.0031308 ? c * 12.92 : 1.055 * c ** (1 / 2.4) - 0.055;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function normalize(x: number, y: number, z: number): [number, number, number] {
|
|
27
|
+
const length = Math.hypot(x, y, z) || 1;
|
|
28
|
+
return [x / length, y / length, z / length];
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function drawNeutralMatcap(size = SIZE): HTMLCanvasElement {
|
|
32
|
+
const canvas = document.createElement('canvas');
|
|
33
|
+
canvas.width = size;
|
|
34
|
+
canvas.height = size;
|
|
35
|
+
const context = canvas.getContext('2d');
|
|
36
|
+
if (!context) return canvas;
|
|
37
|
+
const image = context.createImageData(size, size);
|
|
38
|
+
|
|
39
|
+
const key = normalize(-0.45, 0.62, 0.64);
|
|
40
|
+
const fill = normalize(0.65, -0.35, 0.5);
|
|
41
|
+
// View direction is +Z for a matcap: the sphere is drawn facing the camera.
|
|
42
|
+
const half = normalize(key[0], key[1], key[2] + 1);
|
|
43
|
+
const base = [0.7, 0.69, 0.68];
|
|
44
|
+
const fillColor = [0.34, 0.4, 0.52];
|
|
45
|
+
const rimColor = [0.85, 0.88, 1];
|
|
46
|
+
|
|
47
|
+
for (let py = 0; py < size; py++) {
|
|
48
|
+
for (let px = 0; px < size; px++) {
|
|
49
|
+
const nx = (px + 0.5) / size / 0.5 - 1;
|
|
50
|
+
const ny = 1 - (py + 0.5) / size / 0.5;
|
|
51
|
+
const r2 = nx * nx + ny * ny;
|
|
52
|
+
// Outside the unit disc a matcap is never sampled by a facing surface,
|
|
53
|
+
// but bilinear filtering reaches one texel past the silhouette. Clamp
|
|
54
|
+
// to the rim normal so the edge stays the rim colour instead of
|
|
55
|
+
// bleeding whatever happened to be there.
|
|
56
|
+
const clamped = r2 > 1;
|
|
57
|
+
const scale = clamped ? 1 / Math.sqrt(r2) : 1;
|
|
58
|
+
const x = nx * scale;
|
|
59
|
+
const y = ny * scale;
|
|
60
|
+
const z = clamped ? 0 : Math.sqrt(Math.max(0, 1 - r2));
|
|
61
|
+
|
|
62
|
+
const diffuse = Math.max(0, x * key[0] + y * key[1] + z * key[2]);
|
|
63
|
+
const fillTerm = Math.max(0, x * fill[0] + y * fill[1] + z * fill[2]);
|
|
64
|
+
const specular = Math.max(0, x * half[0] + y * half[1] + z * half[2]) ** 42 * 0.5;
|
|
65
|
+
const rim = (1 - z) ** 3.2 * 0.4;
|
|
66
|
+
|
|
67
|
+
const offset = (py * size + px) * 4;
|
|
68
|
+
for (let channel = 0; channel < 3; channel++) {
|
|
69
|
+
const lit =
|
|
70
|
+
base[channel]! * (0.16 + 0.8 * diffuse) +
|
|
71
|
+
fillColor[channel]! * 0.3 * fillTerm +
|
|
72
|
+
rimColor[channel]! * rim +
|
|
73
|
+
specular;
|
|
74
|
+
image.data[offset + channel] = Math.round(encode(lit) * 255);
|
|
75
|
+
}
|
|
76
|
+
image.data[offset + 3] = 255;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
context.putImageData(image, 0, 0);
|
|
80
|
+
return canvas;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/** The drawn sphere as a ready-to-sample texture. Callers own disposal. */
|
|
84
|
+
export function createNeutralMatcapTexture(size = SIZE): THREE.CanvasTexture {
|
|
85
|
+
const texture = new THREE.CanvasTexture(drawNeutralMatcap(size));
|
|
86
|
+
texture.colorSpace = THREE.SRGBColorSpace;
|
|
87
|
+
texture.minFilter = THREE.LinearFilter;
|
|
88
|
+
texture.magFilter = THREE.LinearFilter;
|
|
89
|
+
texture.generateMipmaps = false;
|
|
90
|
+
texture.needsUpdate = true;
|
|
91
|
+
return texture;
|
|
92
|
+
}
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
import * as THREE from 'three';
|
|
2
|
+
import type { MaterialDescriptor } from '../asset-formats/material';
|
|
3
|
+
import type { MeshDescriptor } from '../asset-formats/mesh';
|
|
4
|
+
import { loadTexture } from '../asset-loaders';
|
|
5
|
+
import { DEFAULTS } from '../defaults';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Single source of truth for turning a `MeshDescriptor` / `MaterialDescriptor` descriptor
|
|
9
|
+
* into Three.js geometry/material. Both the runtime scene-loader and the editor's
|
|
10
|
+
* entity-factory import these, so the editor preview renders exactly what the game
|
|
11
|
+
* renders (the WYSIWYG guarantee). Previously these were
|
|
12
|
+
* copy-pasted in two places and had to be hand-synced — any new material feature
|
|
13
|
+
* added to one and not the other silently broke that guarantee.
|
|
14
|
+
*
|
|
15
|
+
*/
|
|
16
|
+
// biome-ignore lint/complexity/noExcessiveCognitiveComplexity: straightforward type-based switch with fallback args
|
|
17
|
+
export function createGeometry(mesh: MeshDescriptor): THREE.BufferGeometry {
|
|
18
|
+
const args = mesh.args ?? [];
|
|
19
|
+
switch (mesh.type) {
|
|
20
|
+
case 'box':
|
|
21
|
+
return new THREE.BoxGeometry(args[0] ?? 1, args[1] ?? 1, args[2] ?? 1);
|
|
22
|
+
case 'sphere':
|
|
23
|
+
return new THREE.SphereGeometry(args[0] ?? 0.5, args[1] ?? 32, args[2] ?? 16);
|
|
24
|
+
case 'plane':
|
|
25
|
+
// args[2]/args[3] (width/height segments) matter for vertex-displacing
|
|
26
|
+
// materials (e.g. the Water shader) — they used to be silently dropped,
|
|
27
|
+
// which collapsed the water demo to a 2-triangle quad.
|
|
28
|
+
return new THREE.PlaneGeometry(args[0] ?? 1, args[1] ?? 1, args[2] ?? 1, args[3] ?? 1);
|
|
29
|
+
case 'cylinder':
|
|
30
|
+
return new THREE.CylinderGeometry(
|
|
31
|
+
args[0] ?? 0.5,
|
|
32
|
+
args[1] ?? 0.5,
|
|
33
|
+
args[2] ?? 1,
|
|
34
|
+
args[3] ?? 32,
|
|
35
|
+
);
|
|
36
|
+
case 'capsule':
|
|
37
|
+
return new THREE.CapsuleGeometry(args[0] ?? 0.5, args[1] ?? 1, args[2] ?? 4, args[3] ?? 16);
|
|
38
|
+
default:
|
|
39
|
+
return new THREE.BoxGeometry(1, 1, 1);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// biome-ignore lint/complexity/noExcessiveCognitiveComplexity: flat type-based switch building Three materials
|
|
44
|
+
export function createMaterial(mat?: MaterialDescriptor): THREE.Material {
|
|
45
|
+
if (!mat) return new THREE.MeshStandardMaterial({ color: DEFAULTS.material.color });
|
|
46
|
+
|
|
47
|
+
// Only include texture props when actually defined (Three.js warns on undefined values)
|
|
48
|
+
const textures: {
|
|
49
|
+
map?: THREE.Texture;
|
|
50
|
+
normalMap?: THREE.Texture;
|
|
51
|
+
emissiveMap?: THREE.Texture;
|
|
52
|
+
aoMap?: THREE.Texture;
|
|
53
|
+
lightMap?: THREE.Texture;
|
|
54
|
+
roughnessMap?: THREE.Texture;
|
|
55
|
+
metalnessMap?: THREE.Texture;
|
|
56
|
+
} = {};
|
|
57
|
+
if (mat.map) textures.map = loadTexture(mat.map);
|
|
58
|
+
if (mat.normalMap) textures.normalMap = loadTexture(mat.normalMap);
|
|
59
|
+
if (mat.emissiveMap) textures.emissiveMap = loadTexture(mat.emissiveMap);
|
|
60
|
+
if (mat.aoMap) textures.aoMap = loadTexture(mat.aoMap);
|
|
61
|
+
if (mat.lightMap) textures.lightMap = loadTexture(mat.lightMap);
|
|
62
|
+
if (mat.roughnessMap) textures.roughnessMap = loadTexture(mat.roughnessMap);
|
|
63
|
+
if (mat.metalnessMap) textures.metalnessMap = loadTexture(mat.metalnessMap);
|
|
64
|
+
|
|
65
|
+
// R1c — every fallback below reads DEFAULTS.material.* instead of
|
|
66
|
+
// duplicating its value as a bare literal, so DEFAULTS stays the single
|
|
67
|
+
// source of truth for what an omitted field renders as (see
|
|
68
|
+
// `material-factory.test.ts`'s
|
|
69
|
+
// "omitted-field" locks, which fail if either copy drifts from the other).
|
|
70
|
+
const dMat = DEFAULTS.material;
|
|
71
|
+
const sideValue = mat.side ?? dMat.side;
|
|
72
|
+
const side =
|
|
73
|
+
sideValue === 'back'
|
|
74
|
+
? THREE.BackSide
|
|
75
|
+
: sideValue === 'double'
|
|
76
|
+
? THREE.DoubleSide
|
|
77
|
+
: THREE.FrontSide;
|
|
78
|
+
const opacity = mat.opacity ?? dMat.opacity;
|
|
79
|
+
const transparent = mat.transparent ?? dMat.transparent;
|
|
80
|
+
|
|
81
|
+
switch (mat.type) {
|
|
82
|
+
case 'basic':
|
|
83
|
+
return new THREE.MeshBasicMaterial({
|
|
84
|
+
color: mat.color ?? dMat.color,
|
|
85
|
+
...textures,
|
|
86
|
+
lightMapIntensity: mat.lightMapIntensity ?? 1,
|
|
87
|
+
opacity,
|
|
88
|
+
transparent,
|
|
89
|
+
side,
|
|
90
|
+
});
|
|
91
|
+
case 'toon':
|
|
92
|
+
return new THREE.MeshToonMaterial({
|
|
93
|
+
color: mat.color ?? dMat.color,
|
|
94
|
+
...textures,
|
|
95
|
+
lightMapIntensity: mat.lightMapIntensity ?? 1,
|
|
96
|
+
emissive: mat.emissive ?? dMat.emissive,
|
|
97
|
+
emissiveIntensity: mat.emissiveIntensity ?? dMat.emissiveIntensity,
|
|
98
|
+
opacity,
|
|
99
|
+
transparent,
|
|
100
|
+
side,
|
|
101
|
+
});
|
|
102
|
+
case 'physical':
|
|
103
|
+
return new THREE.MeshPhysicalMaterial({
|
|
104
|
+
color: mat.color ?? dMat.color,
|
|
105
|
+
metalness: mat.metalness ?? dMat.metalness,
|
|
106
|
+
roughness: mat.roughness ?? dMat.roughness,
|
|
107
|
+
...textures,
|
|
108
|
+
lightMapIntensity: mat.lightMapIntensity ?? 1,
|
|
109
|
+
emissive: mat.emissive ?? dMat.emissive,
|
|
110
|
+
emissiveIntensity: mat.emissiveIntensity ?? dMat.emissiveIntensity,
|
|
111
|
+
opacity,
|
|
112
|
+
transparent,
|
|
113
|
+
side,
|
|
114
|
+
flatShading: mat.flatShading ?? dMat.flatShading,
|
|
115
|
+
// Clearcoat
|
|
116
|
+
...(mat.clearcoat
|
|
117
|
+
? {
|
|
118
|
+
clearcoat: mat.clearcoat.clearcoat,
|
|
119
|
+
clearcoatRoughness:
|
|
120
|
+
mat.clearcoat.clearcoatRoughness ?? dMat.clearcoat.clearcoatRoughness,
|
|
121
|
+
...(mat.clearcoat.clearcoatMap
|
|
122
|
+
? { clearcoatMap: loadTexture(mat.clearcoat.clearcoatMap) }
|
|
123
|
+
: {}),
|
|
124
|
+
...(mat.clearcoat.clearcoatRoughnessMap
|
|
125
|
+
? { clearcoatRoughnessMap: loadTexture(mat.clearcoat.clearcoatRoughnessMap) }
|
|
126
|
+
: {}),
|
|
127
|
+
}
|
|
128
|
+
: {}),
|
|
129
|
+
// Transmission — `thickness` used to hardcode a literal `0` here,
|
|
130
|
+
// silently diverging from DEFAULTS.material.transmission.thickness
|
|
131
|
+
// (0.5, per the editor's Transmission inspector default). Fixed:
|
|
132
|
+
// read DEFAULTS directly, like every other fallback in this file.
|
|
133
|
+
...(mat.transmission
|
|
134
|
+
? {
|
|
135
|
+
transmission: mat.transmission.transmission,
|
|
136
|
+
ior: mat.transmission.ior ?? dMat.transmission.ior,
|
|
137
|
+
thickness: mat.transmission.thickness ?? dMat.transmission.thickness,
|
|
138
|
+
...(mat.transmission.attenuationColor
|
|
139
|
+
? { attenuationColor: new THREE.Color(mat.transmission.attenuationColor) }
|
|
140
|
+
: {}),
|
|
141
|
+
attenuationDistance: mat.transmission.attenuationDistance ?? Infinity,
|
|
142
|
+
...(mat.transmission.transmissionMap
|
|
143
|
+
? { transmissionMap: loadTexture(mat.transmission.transmissionMap) }
|
|
144
|
+
: {}),
|
|
145
|
+
}
|
|
146
|
+
: {}),
|
|
147
|
+
// Sheen
|
|
148
|
+
...(mat.sheen
|
|
149
|
+
? {
|
|
150
|
+
sheen: mat.sheen.sheen,
|
|
151
|
+
sheenColor: new THREE.Color(mat.sheen.sheenColor ?? dMat.sheen.sheenColor),
|
|
152
|
+
sheenRoughness: mat.sheen.sheenRoughness ?? dMat.sheen.sheenRoughness,
|
|
153
|
+
...(mat.sheen.sheenColorMap
|
|
154
|
+
? { sheenColorMap: loadTexture(mat.sheen.sheenColorMap) }
|
|
155
|
+
: {}),
|
|
156
|
+
...(mat.sheen.sheenRoughnessMap
|
|
157
|
+
? { sheenRoughnessMap: loadTexture(mat.sheen.sheenRoughnessMap) }
|
|
158
|
+
: {}),
|
|
159
|
+
}
|
|
160
|
+
: {}),
|
|
161
|
+
// Iridescence
|
|
162
|
+
...(mat.iridescence
|
|
163
|
+
? {
|
|
164
|
+
iridescence: mat.iridescence.iridescence,
|
|
165
|
+
iridescenceIOR: mat.iridescence.iridescenceIOR ?? dMat.iridescence.iridescenceIOR,
|
|
166
|
+
iridescenceThicknessRange:
|
|
167
|
+
mat.iridescence.iridescenceThicknessRange ??
|
|
168
|
+
dMat.iridescence.iridescenceThicknessRange,
|
|
169
|
+
...(mat.iridescence.iridescenceMap
|
|
170
|
+
? { iridescenceMap: loadTexture(mat.iridescence.iridescenceMap) }
|
|
171
|
+
: {}),
|
|
172
|
+
...(mat.iridescence.iridescenceThicknessMap
|
|
173
|
+
? {
|
|
174
|
+
iridescenceThicknessMap: loadTexture(mat.iridescence.iridescenceThicknessMap),
|
|
175
|
+
}
|
|
176
|
+
: {}),
|
|
177
|
+
}
|
|
178
|
+
: {}),
|
|
179
|
+
// Displacement
|
|
180
|
+
...(mat.displacementMap ? { displacementMap: loadTexture(mat.displacementMap) } : {}),
|
|
181
|
+
...(mat.displacementScale !== undefined
|
|
182
|
+
? { displacementScale: mat.displacementScale }
|
|
183
|
+
: {}),
|
|
184
|
+
...(mat.displacementBias !== undefined ? { displacementBias: mat.displacementBias } : {}),
|
|
185
|
+
});
|
|
186
|
+
default:
|
|
187
|
+
return new THREE.MeshStandardMaterial({
|
|
188
|
+
color: mat.color ?? dMat.color,
|
|
189
|
+
metalness: mat.metalness ?? dMat.metalness,
|
|
190
|
+
roughness: mat.roughness ?? dMat.roughness,
|
|
191
|
+
...textures,
|
|
192
|
+
lightMapIntensity: mat.lightMapIntensity ?? 1,
|
|
193
|
+
emissive: mat.emissive ?? dMat.emissive,
|
|
194
|
+
emissiveIntensity: mat.emissiveIntensity ?? dMat.emissiveIntensity,
|
|
195
|
+
opacity,
|
|
196
|
+
transparent,
|
|
197
|
+
side,
|
|
198
|
+
flatShading: mat.flatShading ?? dMat.flatShading,
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import * as THREE from 'three';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Apply the authored mesh-shadow contract to every renderable in a model.
|
|
5
|
+
*
|
|
6
|
+
* Primitive scene entities contain one Mesh, while glTF entities usually
|
|
7
|
+
* contain a Group with many Mesh/SkinnedMesh descendants. Keeping this walk
|
|
8
|
+
* shared prevents editor preview and runtime Play from disagreeing by source
|
|
9
|
+
* format.
|
|
10
|
+
*/
|
|
11
|
+
export function setMeshShadowEnabled(root: THREE.Object3D, enabled: boolean | undefined): void {
|
|
12
|
+
if (enabled === undefined) return;
|
|
13
|
+
root.traverse((object) => {
|
|
14
|
+
if (!(object instanceof THREE.Mesh)) return;
|
|
15
|
+
object.castShadow = enabled;
|
|
16
|
+
object.receiveShadow = enabled;
|
|
17
|
+
});
|
|
18
|
+
}
|