@rydr/game-sdk 9.9.0 → 9.11.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 -1
- package/dist/client/PlatformClient.d.ts +19 -0
- package/dist/client/PlatformClient.d.ts.map +1 -1
- package/dist/client/PlatformClient.js +1 -0
- package/dist/client/PlatformClient.js.map +1 -1
- package/dist/host/PlatformHost.d.ts +4 -0
- package/dist/host/PlatformHost.d.ts.map +1 -1
- package/dist/host/PlatformHost.js +4 -0
- package/dist/host/PlatformHost.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/protocol/index.d.ts +1 -0
- package/dist/protocol/index.d.ts.map +1 -1
- package/dist/protocol/index.js +1 -0
- package/dist/protocol/index.js.map +1 -1
- package/dist/protocol/messages.d.ts +8 -1
- package/dist/protocol/messages.d.ts.map +1 -1
- package/dist/protocol/rideHud.d.ts +33 -0
- package/dist/protocol/rideHud.d.ts.map +1 -0
- package/dist/protocol/rideHud.js +34 -0
- package/dist/protocol/rideHud.js.map +1 -0
- package/dist/protocol/version.d.ts +2 -2
- package/dist/protocol/version.d.ts.map +1 -1
- package/dist/protocol/version.js +11 -2
- package/dist/protocol/version.js.map +1 -1
- package/dist/protocol/worlds.d.ts +181 -0
- package/dist/protocol/worlds.d.ts.map +1 -1
- package/dist/protocol/worlds.js +9 -0
- package/dist/protocol/worlds.js.map +1 -1
- package/dist/three/README.md +25 -0
- package/dist/three/baked-lighting.d.ts +144 -0
- package/dist/three/baked-lighting.d.ts.map +1 -0
- package/dist/three/baked-lighting.js +289 -0
- package/dist/three/baked-lighting.js.map +1 -0
- package/dist/three/index.d.ts +2 -0
- package/dist/three/index.d.ts.map +1 -1
- package/dist/three/index.js +2 -0
- package/dist/three/index.js.map +1 -1
- package/dist/three/preset-lighting.d.ts +65 -0
- package/dist/three/preset-lighting.d.ts.map +1 -0
- package/dist/three/preset-lighting.js +163 -0
- package/dist/three/preset-lighting.js.map +1 -0
- package/dist/world-lighting.d.ts +105 -0
- package/dist/world-lighting.d.ts.map +1 -0
- package/dist/world-lighting.js +230 -0
- package/dist/world-lighting.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* preset-lighting — light a three.js scene LIVE from a {@link WorldLightingPreset}, and turn a world's
|
|
3
|
+
* placed lights into real three.js lights (opt-in, capped).
|
|
4
|
+
*
|
|
5
|
+
* The rig is the one a preset describes, field for field: a shadow-casting sun, an unshadowed fill
|
|
6
|
+
* opposite it, a hemisphere, a flat ambient, and a gradient sky that is both `scene.background` and
|
|
7
|
+
* the PMREM environment. The platform's bake lights the same scene in Blender from the same fields,
|
|
8
|
+
* with the same gradient ({@link buildSkyTexture}), which is what makes a baked scene and a live one
|
|
9
|
+
* of the same preset look alike.
|
|
10
|
+
*
|
|
11
|
+
* A game with its own rig (racing's `installSceneLighting`) does not need this module: it reads the
|
|
12
|
+
* preset's fields into its own. This is for games and tools that don't have one.
|
|
13
|
+
*/
|
|
14
|
+
import * as THREE from "three";
|
|
15
|
+
import { effectiveLights, sunDirection } from "../world-lighting.js";
|
|
16
|
+
/**
|
|
17
|
+
* The sky gradient, as a small equirect canvas texture (sRGB). The stops are the contract the bake's
|
|
18
|
+
* world shader reproduces: zenith down to 14.4° above the horizon, horizon haze AT the horizon, ground
|
|
19
|
+
* from 3.6° below it. Tiny on purpose — PMREM blurs it anyway.
|
|
20
|
+
*/
|
|
21
|
+
export function buildSkyTexture(sky) {
|
|
22
|
+
const canvas = document.createElement("canvas");
|
|
23
|
+
canvas.width = 32;
|
|
24
|
+
canvas.height = 128;
|
|
25
|
+
const ctx = canvas.getContext("2d");
|
|
26
|
+
if (!ctx)
|
|
27
|
+
throw new Error("[rydr-sdk] buildSkyTexture: 2D context unavailable");
|
|
28
|
+
const grad = ctx.createLinearGradient(0, 0, 0, canvas.height);
|
|
29
|
+
for (const [at, colour] of skyStops(sky))
|
|
30
|
+
grad.addColorStop(at, colour);
|
|
31
|
+
ctx.fillStyle = grad;
|
|
32
|
+
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
|
33
|
+
const tex = new THREE.CanvasTexture(canvas);
|
|
34
|
+
tex.mapping = THREE.EquirectangularReflectionMapping;
|
|
35
|
+
tex.colorSpace = THREE.SRGBColorSpace;
|
|
36
|
+
tex.needsUpdate = true;
|
|
37
|
+
return tex;
|
|
38
|
+
}
|
|
39
|
+
/** The gradient stops (0 = straight up, 0.5 = horizon, 1 = straight down). Mirrored by the bake script. */
|
|
40
|
+
export function skyStops(sky) {
|
|
41
|
+
return [
|
|
42
|
+
[0, sky.zenith],
|
|
43
|
+
[0.42, sky.zenith],
|
|
44
|
+
[0.5, sky.horizon],
|
|
45
|
+
[0.52, sky.ground],
|
|
46
|
+
[1, sky.ground],
|
|
47
|
+
];
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Light `scene` from `preset`: adds the rig's lights, sets background / environment / fog, and the
|
|
51
|
+
* renderer's exposure (ACES filmic) when a renderer is given.
|
|
52
|
+
*/
|
|
53
|
+
export function applyLightingPreset(scene, preset, opts = {}) {
|
|
54
|
+
const dist = opts.sunDistance ?? 200;
|
|
55
|
+
const sun = new THREE.DirectionalLight();
|
|
56
|
+
sun.name = "preset-sun";
|
|
57
|
+
sun.castShadow = !!opts.shadows;
|
|
58
|
+
const fill = new THREE.DirectionalLight();
|
|
59
|
+
fill.name = "preset-fill";
|
|
60
|
+
const hemisphere = new THREE.HemisphereLight();
|
|
61
|
+
const ambient = new THREE.AmbientLight();
|
|
62
|
+
scene.add(sun, sun.target, fill, fill.target, hemisphere, ambient);
|
|
63
|
+
let sky = null;
|
|
64
|
+
let env = null;
|
|
65
|
+
const apply = (p) => {
|
|
66
|
+
const d = sunDirection(p.sun);
|
|
67
|
+
sun.color.set(p.sun.color);
|
|
68
|
+
sun.intensity = p.sun.intensity;
|
|
69
|
+
sun.position.set(d[0] * dist, d[1] * dist, d[2] * dist);
|
|
70
|
+
fill.color.set(p.fill?.color ?? p.hemisphere?.sky ?? "#ffffff");
|
|
71
|
+
fill.intensity = p.fill?.intensity ?? 0;
|
|
72
|
+
fill.position.set(-d[0] * dist, Math.max(0.2, d[1] * 0.4) * dist, -d[2] * dist);
|
|
73
|
+
hemisphere.color.set(p.hemisphere?.sky ?? "#ffffff");
|
|
74
|
+
hemisphere.groundColor.set(p.hemisphere?.ground ?? "#000000");
|
|
75
|
+
hemisphere.intensity = p.hemisphere?.intensity ?? 0;
|
|
76
|
+
ambient.color.set(p.ambient?.color ?? "#ffffff");
|
|
77
|
+
ambient.intensity = p.ambient?.intensity ?? 0;
|
|
78
|
+
sky?.dispose();
|
|
79
|
+
env?.dispose();
|
|
80
|
+
sky = buildSkyTexture(p.sky);
|
|
81
|
+
scene.background = sky;
|
|
82
|
+
if (opts.renderer) {
|
|
83
|
+
const pmrem = new THREE.PMREMGenerator(opts.renderer);
|
|
84
|
+
env = pmrem.fromEquirectangular(sky);
|
|
85
|
+
pmrem.dispose();
|
|
86
|
+
scene.environment = env.texture;
|
|
87
|
+
scene.environmentIntensity = p.sky.intensity;
|
|
88
|
+
opts.renderer.toneMapping = THREE.ACESFilmicToneMapping;
|
|
89
|
+
opts.renderer.toneMappingExposure = p.exposure ?? 1;
|
|
90
|
+
}
|
|
91
|
+
scene.fog = opts.fog !== false && p.fog ? new THREE.Fog(p.fog.color, p.fog.near, p.fog.far) : null;
|
|
92
|
+
};
|
|
93
|
+
apply(preset);
|
|
94
|
+
return {
|
|
95
|
+
sun,
|
|
96
|
+
fill,
|
|
97
|
+
hemisphere,
|
|
98
|
+
ambient,
|
|
99
|
+
apply,
|
|
100
|
+
dispose: () => {
|
|
101
|
+
for (const l of [sun, sun.target, fill, fill.target, hemisphere, ambient])
|
|
102
|
+
l.removeFromParent();
|
|
103
|
+
sky?.dispose();
|
|
104
|
+
env?.dispose();
|
|
105
|
+
if (scene.background === sky)
|
|
106
|
+
scene.background = null;
|
|
107
|
+
if (env && scene.environment === env.texture)
|
|
108
|
+
scene.environment = null;
|
|
109
|
+
},
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Real three.js lights for a world's placed {@link WorldLight}s, under a preset's groups. Area lights
|
|
114
|
+
* are skipped (bake-only). For a LIVE scene only — a baked one already has their light.
|
|
115
|
+
*/
|
|
116
|
+
export function createWorldLights(world, preset, opts = {}) {
|
|
117
|
+
const group = new THREE.Group();
|
|
118
|
+
group.name = "world-lights";
|
|
119
|
+
const max = opts.max ?? 8;
|
|
120
|
+
const candidates = effectiveLights(world, preset)
|
|
121
|
+
.filter((l) => l.type !== "area")
|
|
122
|
+
.map((l) => ({ l, i: l.intensity }));
|
|
123
|
+
const near = opts.near;
|
|
124
|
+
candidates.sort((a, b) => (near ? dist2(a.l, near) - dist2(b.l, near) : b.i - a.i));
|
|
125
|
+
for (const { l, i } of candidates.slice(0, max)) {
|
|
126
|
+
const light = threeLight(l, i);
|
|
127
|
+
light.castShadow = !!opts.shadows && l.castShadow !== false;
|
|
128
|
+
group.add(light);
|
|
129
|
+
if (light.target)
|
|
130
|
+
group.add(light.target);
|
|
131
|
+
}
|
|
132
|
+
return group;
|
|
133
|
+
}
|
|
134
|
+
/** One {@link WorldLight} as a three.js light (an area light is approximated as a point). Intensity already includes the preset. */
|
|
135
|
+
export function threeLight(l, intensity) {
|
|
136
|
+
const pos = new THREE.Vector3(...l.position);
|
|
137
|
+
const q = l.rotation ? new THREE.Quaternion(...l.rotation) : new THREE.Quaternion();
|
|
138
|
+
// A light shines down its local -Y (see WorldLight.rotation): aim targets one metre along it.
|
|
139
|
+
const aim = new THREE.Vector3(0, -1, 0).applyQuaternion(q).add(pos);
|
|
140
|
+
let light;
|
|
141
|
+
if (l.type === "directional") {
|
|
142
|
+
const d = new THREE.DirectionalLight(l.color, intensity);
|
|
143
|
+
d.target.position.copy(aim);
|
|
144
|
+
light = d;
|
|
145
|
+
}
|
|
146
|
+
else if (l.type === "spot") {
|
|
147
|
+
const s = new THREE.SpotLight(l.color, intensity, l.range ?? 0, l.angle ?? Math.PI / 6, l.penumbra ?? 0.2, 2);
|
|
148
|
+
s.target.position.copy(aim);
|
|
149
|
+
light = s;
|
|
150
|
+
}
|
|
151
|
+
else {
|
|
152
|
+
light = new THREE.PointLight(l.color, intensity, l.range ?? 0, 2);
|
|
153
|
+
}
|
|
154
|
+
light.position.copy(pos);
|
|
155
|
+
light.name = l.name ?? l.id;
|
|
156
|
+
light.userData.worldLightId = l.id;
|
|
157
|
+
return light;
|
|
158
|
+
}
|
|
159
|
+
function dist2(l, p) {
|
|
160
|
+
const [x, y, z] = l.position;
|
|
161
|
+
return (x - p.x) ** 2 + (y - p.y) ** 2 + (z - p.z) ** 2;
|
|
162
|
+
}
|
|
163
|
+
//# sourceMappingURL=preset-lighting.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"preset-lighting.js","sourceRoot":"","sources":["../../src/three/preset-lighting.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* world-lighting — the renderer-agnostic half of a world's lighting: its presets, the fingerprints that
|
|
3
|
+
* say whether a bake still matches, and the choice between a BAKED scene and a LIVE one.
|
|
4
|
+
*
|
|
5
|
+
* Zero dependency on `three`, like {@link applyWorld}: the editor, a game and a test all compute the
|
|
6
|
+
* same fingerprint from the same JSON, which is the whole point — the editor stamps a bake with it at
|
|
7
|
+
* export time and a game recomputes it at load time. The three.js half (loading a bake, lighting a
|
|
8
|
+
* scene from a preset) is `@rydr/game-sdk/three`.
|
|
9
|
+
*
|
|
10
|
+
* ## How a game chooses
|
|
11
|
+
*
|
|
12
|
+
* const preset = resolvePreset(world, track.lightingPresetId);
|
|
13
|
+
* const bake = pickBake(track.bakes, { world, presetId: preset?.id, layer: { game, docId, fingerprint } });
|
|
14
|
+
* bake ? loadBakedScene(bake) : loadWorld(world) // + light the live one from `preset`
|
|
15
|
+
*
|
|
16
|
+
* A bake is used only while it is FRESH (both fingerprints match). A stale one is ignored rather than
|
|
17
|
+
* shown, because a bake that disagrees with the scene — a road re-routed under old shadows — is worse
|
|
18
|
+
* than no bake.
|
|
19
|
+
*/
|
|
20
|
+
import type { CoreWorld, SceneBake, WorldEnv, WorldLight, WorldLightingPreset } from "./protocol/worlds.js";
|
|
21
|
+
/** The id `lightingPresets` gives the preset it derives from a legacy `env`. */
|
|
22
|
+
export declare const LEGACY_ENV_PRESET_ID = "default";
|
|
23
|
+
/** Every preset a world offers. A world with only a legacy `env` offers one, derived from it. */
|
|
24
|
+
export declare function lightingPresets(world: Pick<CoreWorld, "lighting" | "env">): WorldLightingPreset[];
|
|
25
|
+
/**
|
|
26
|
+
* The preset a game should light with: the one asked for, else the world's default, else its first.
|
|
27
|
+
* `null` when the world has no lighting at all — the game keeps its own look.
|
|
28
|
+
*/
|
|
29
|
+
export declare function resolvePreset(world: Pick<CoreWorld, "lighting" | "env">, presetId?: string | null): WorldLightingPreset | null;
|
|
30
|
+
/** The legacy `WorldEnv` read as a preset, so older worlds need no migration to be lit or baked. */
|
|
31
|
+
export declare function presetFromEnv(env: WorldEnv): WorldLightingPreset;
|
|
32
|
+
/**
|
|
33
|
+
* Unit vector pointing FROM the scene TOWARD the sun (three.js / glTF axes, +Y up). Multiply by a
|
|
34
|
+
* distance for a `DirectionalLight.position`. Same convention as the editor's key light: azimuth from
|
|
35
|
+
* +X toward +Z, elevation above the horizon.
|
|
36
|
+
*/
|
|
37
|
+
export declare function sunDirection(sun: Pick<WorldLightingPreset["sun"], "azimuthDeg" | "elevationDeg">): [number, number, number];
|
|
38
|
+
/** The inverse of {@link sunDirection}: azimuth/elevation (degrees) of a direction toward the sun. */
|
|
39
|
+
export declare function sunAngles(dir: {
|
|
40
|
+
x: number;
|
|
41
|
+
y: number;
|
|
42
|
+
z: number;
|
|
43
|
+
}): {
|
|
44
|
+
azimuthDeg: number;
|
|
45
|
+
elevationDeg: number;
|
|
46
|
+
};
|
|
47
|
+
/**
|
|
48
|
+
* The world's lights as they are actually lit under `preset`: the world-root transform applied (the same
|
|
49
|
+
* `p * s + t` `applyWorld` gives placed objects, with `range`/`size` scaled), intensity multiplied by
|
|
50
|
+
* the light's group, and lights that end up at 0 dropped. What a live rig creates and what a bake
|
|
51
|
+
* manifest carries — so both light the same scene.
|
|
52
|
+
*/
|
|
53
|
+
export declare function effectiveLights(world: Pick<CoreWorld, "map">, preset: WorldLightingPreset | null): WorldLight[];
|
|
54
|
+
/** A light's intensity under a preset: its own × its group's multiplier (1 when unlisted). */
|
|
55
|
+
export declare function lightIntensityUnder(light: WorldLight, preset: WorldLightingPreset | null): number;
|
|
56
|
+
/**
|
|
57
|
+
* Fingerprint of everything in a world that a bake depends on, under one preset.
|
|
58
|
+
*
|
|
59
|
+
* In: the base map, every modification and placement, the world transform, the lights, and the
|
|
60
|
+
* CONTENT of the preset the bake used. Out, on purpose: the name, the object groups (editor metadata),
|
|
61
|
+
* the other presets (adding a Night preset must not invalidate the Day bake), the bakes themselves,
|
|
62
|
+
* and timestamps (a save that changes nothing must not either).
|
|
63
|
+
*
|
|
64
|
+
* URLs are compared by PATH: platform assets are served presigned, so the same glb arrives with a
|
|
65
|
+
* different query string every time. The world should be the one a game RENDERS — for a game that
|
|
66
|
+
* layers its own delta on the platform world, the merged one.
|
|
67
|
+
*/
|
|
68
|
+
export declare function worldFingerprint(world: CoreWorld, presetId?: string | null): string;
|
|
69
|
+
/**
|
|
70
|
+
* Fingerprint of a GAME's layer — whatever of the game's own data changes what its baked dress looks
|
|
71
|
+
* like (racing: the course's rails, barriers, kerbs and the track's own world delta). The game decides
|
|
72
|
+
* what goes in; this only guarantees it hashes the same way everywhere.
|
|
73
|
+
*/
|
|
74
|
+
export declare function layerFingerprint(value: unknown): string;
|
|
75
|
+
/** Whether a bake still matches the scene it would be shown in. */
|
|
76
|
+
export declare function isBakeFresh(bake: SceneBake, ctx: {
|
|
77
|
+
world: CoreWorld;
|
|
78
|
+
presetId?: string | null;
|
|
79
|
+
layerFingerprint?: string;
|
|
80
|
+
}): boolean;
|
|
81
|
+
/** What an editor shows next to a preset: is there a bake, and is it still good. */
|
|
82
|
+
export type BakeStatus = "fresh" | "stale" | "missing";
|
|
83
|
+
/** {@link BakeStatus} of the newest bake for a preset. */
|
|
84
|
+
export declare function bakeStatus(bakes: readonly SceneBake[] | undefined, ctx: {
|
|
85
|
+
world: CoreWorld;
|
|
86
|
+
presetId?: string | null;
|
|
87
|
+
layerFingerprint?: string;
|
|
88
|
+
}): BakeStatus;
|
|
89
|
+
/** The newest FRESH bake for the scene, or `null` — in which case the game renders live. */
|
|
90
|
+
export declare function pickBake(bakes: readonly SceneBake[] | undefined, ctx: {
|
|
91
|
+
world: CoreWorld;
|
|
92
|
+
presetId?: string | null;
|
|
93
|
+
layerFingerprint?: string;
|
|
94
|
+
}): SceneBake | null;
|
|
95
|
+
/**
|
|
96
|
+
* Replace the bake for `bake.presetId` (+ layer) in a list, keeping the others. What an editor calls
|
|
97
|
+
* when an Import bake lands, so a list never grows a stale twin per preset.
|
|
98
|
+
*/
|
|
99
|
+
export declare function withBake(bakes: readonly SceneBake[] | undefined, bake: SceneBake): SceneBake[];
|
|
100
|
+
/**
|
|
101
|
+
* Stable content hash: key order, float noise and presigned query strings never change it.
|
|
102
|
+
* cyrb53 over a canonical JSON — not cryptographic, and does not need to be.
|
|
103
|
+
*/
|
|
104
|
+
export declare function fingerprint(value: unknown): string;
|
|
105
|
+
//# sourceMappingURL=world-lighting.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"world-lighting.d.ts","sourceRoot":"","sources":["../src/world-lighting.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* world-lighting — the renderer-agnostic half of a world's lighting: its presets, the fingerprints that
|
|
3
|
+
* say whether a bake still matches, and the choice between a BAKED scene and a LIVE one.
|
|
4
|
+
*
|
|
5
|
+
* Zero dependency on `three`, like {@link applyWorld}: the editor, a game and a test all compute the
|
|
6
|
+
* same fingerprint from the same JSON, which is the whole point — the editor stamps a bake with it at
|
|
7
|
+
* export time and a game recomputes it at load time. The three.js half (loading a bake, lighting a
|
|
8
|
+
* scene from a preset) is `@rydr/game-sdk/three`.
|
|
9
|
+
*
|
|
10
|
+
* ## How a game chooses
|
|
11
|
+
*
|
|
12
|
+
* const preset = resolvePreset(world, track.lightingPresetId);
|
|
13
|
+
* const bake = pickBake(track.bakes, { world, presetId: preset?.id, layer: { game, docId, fingerprint } });
|
|
14
|
+
* bake ? loadBakedScene(bake) : loadWorld(world) // + light the live one from `preset`
|
|
15
|
+
*
|
|
16
|
+
* A bake is used only while it is FRESH (both fingerprints match). A stale one is ignored rather than
|
|
17
|
+
* shown, because a bake that disagrees with the scene — a road re-routed under old shadows — is worse
|
|
18
|
+
* than no bake.
|
|
19
|
+
*/
|
|
20
|
+
/** The id `lightingPresets` gives the preset it derives from a legacy `env`. */
|
|
21
|
+
export const LEGACY_ENV_PRESET_ID = "default";
|
|
22
|
+
/** Every preset a world offers. A world with only a legacy `env` offers one, derived from it. */
|
|
23
|
+
export function lightingPresets(world) {
|
|
24
|
+
if (world.lighting?.presets.length)
|
|
25
|
+
return world.lighting.presets;
|
|
26
|
+
return world.env ? [presetFromEnv(world.env)] : [];
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* The preset a game should light with: the one asked for, else the world's default, else its first.
|
|
30
|
+
* `null` when the world has no lighting at all — the game keeps its own look.
|
|
31
|
+
*/
|
|
32
|
+
export function resolvePreset(world, presetId) {
|
|
33
|
+
const presets = lightingPresets(world);
|
|
34
|
+
if (!presets.length)
|
|
35
|
+
return null;
|
|
36
|
+
return ((presetId ? presets.find((p) => p.id === presetId) : undefined) ??
|
|
37
|
+
presets.find((p) => p.id === world.lighting?.defaultPresetId) ??
|
|
38
|
+
presets[0] ??
|
|
39
|
+
null);
|
|
40
|
+
}
|
|
41
|
+
/** The legacy `WorldEnv` read as a preset, so older worlds need no migration to be lit or baked. */
|
|
42
|
+
export function presetFromEnv(env) {
|
|
43
|
+
return {
|
|
44
|
+
id: LEGACY_ENV_PRESET_ID,
|
|
45
|
+
name: "Default",
|
|
46
|
+
sun: {
|
|
47
|
+
azimuthDeg: env.keyAzimuthDeg,
|
|
48
|
+
elevationDeg: env.keyElevationDeg,
|
|
49
|
+
color: env.keyColor,
|
|
50
|
+
intensity: env.keyIntensity,
|
|
51
|
+
},
|
|
52
|
+
sky: { zenith: env.background, horizon: env.fogColor, ground: env.ambientColor, intensity: 0.6 },
|
|
53
|
+
hemisphere: { sky: env.ambientColor, ground: env.background, intensity: env.ambientIntensity },
|
|
54
|
+
fog: { color: env.fogColor, near: env.fogNear, far: env.fogFar },
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Unit vector pointing FROM the scene TOWARD the sun (three.js / glTF axes, +Y up). Multiply by a
|
|
59
|
+
* distance for a `DirectionalLight.position`. Same convention as the editor's key light: azimuth from
|
|
60
|
+
* +X toward +Z, elevation above the horizon.
|
|
61
|
+
*/
|
|
62
|
+
export function sunDirection(sun) {
|
|
63
|
+
const az = (sun.azimuthDeg * Math.PI) / 180;
|
|
64
|
+
const el = (sun.elevationDeg * Math.PI) / 180;
|
|
65
|
+
return [Math.cos(el) * Math.cos(az), Math.sin(el), Math.cos(el) * Math.sin(az)];
|
|
66
|
+
}
|
|
67
|
+
/** The inverse of {@link sunDirection}: azimuth/elevation (degrees) of a direction toward the sun. */
|
|
68
|
+
export function sunAngles(dir) {
|
|
69
|
+
const len = Math.hypot(dir.x, dir.y, dir.z) || 1;
|
|
70
|
+
const elevationDeg = (Math.asin(dir.y / len) * 180) / Math.PI;
|
|
71
|
+
const azimuthDeg = (Math.atan2(dir.z, dir.x) * 180) / Math.PI;
|
|
72
|
+
return { azimuthDeg, elevationDeg };
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* The world's lights as they are actually lit under `preset`: the world-root transform applied (the same
|
|
76
|
+
* `p * s + t` `applyWorld` gives placed objects, with `range`/`size` scaled), intensity multiplied by
|
|
77
|
+
* the light's group, and lights that end up at 0 dropped. What a live rig creates and what a bake
|
|
78
|
+
* manifest carries — so both light the same scene.
|
|
79
|
+
*/
|
|
80
|
+
export function effectiveLights(world, preset) {
|
|
81
|
+
const s = world.map.worldTransform?.scale ?? 1;
|
|
82
|
+
const t = world.map.worldTransform?.position ?? [0, 0, 0];
|
|
83
|
+
const out = [];
|
|
84
|
+
for (const l of world.map.lights ?? []) {
|
|
85
|
+
const intensity = lightIntensityUnder(l, preset);
|
|
86
|
+
if (!(intensity > 0))
|
|
87
|
+
continue;
|
|
88
|
+
const e = {
|
|
89
|
+
...l,
|
|
90
|
+
intensity,
|
|
91
|
+
position: [l.position[0] * s + t[0], l.position[1] * s + t[1], l.position[2] * s + t[2]],
|
|
92
|
+
};
|
|
93
|
+
if (l.range)
|
|
94
|
+
e.range = l.range * s;
|
|
95
|
+
if (l.size)
|
|
96
|
+
e.size = l.size * s;
|
|
97
|
+
out.push(e);
|
|
98
|
+
}
|
|
99
|
+
return out;
|
|
100
|
+
}
|
|
101
|
+
/** A light's intensity under a preset: its own × its group's multiplier (1 when unlisted). */
|
|
102
|
+
export function lightIntensityUnder(light, preset) {
|
|
103
|
+
const g = light.group && preset?.groups ? preset.groups[light.group] : undefined;
|
|
104
|
+
return light.intensity * (g ?? 1);
|
|
105
|
+
}
|
|
106
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
107
|
+
// Fingerprints
|
|
108
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
109
|
+
/**
|
|
110
|
+
* Fingerprint of everything in a world that a bake depends on, under one preset.
|
|
111
|
+
*
|
|
112
|
+
* In: the base map, every modification and placement, the world transform, the lights, and the
|
|
113
|
+
* CONTENT of the preset the bake used. Out, on purpose: the name, the object groups (editor metadata),
|
|
114
|
+
* the other presets (adding a Night preset must not invalidate the Day bake), the bakes themselves,
|
|
115
|
+
* and timestamps (a save that changes nothing must not either).
|
|
116
|
+
*
|
|
117
|
+
* URLs are compared by PATH: platform assets are served presigned, so the same glb arrives with a
|
|
118
|
+
* different query string every time. The world should be the one a game RENDERS — for a game that
|
|
119
|
+
* layers its own delta on the platform world, the merged one.
|
|
120
|
+
*/
|
|
121
|
+
export function worldFingerprint(world, presetId) {
|
|
122
|
+
const m = world.map;
|
|
123
|
+
// Sorted by identity: the editor regenerates these lists from its own object order, which is not the
|
|
124
|
+
// order they were saved in after a reload (duplicates restore before catalog placements). The same
|
|
125
|
+
// world must fingerprint the same whichever order its lists come back in.
|
|
126
|
+
const by = (key) => (a, b) => (key(a) < key(b) ? -1 : key(a) > key(b) ? 1 : 0);
|
|
127
|
+
return fingerprint({
|
|
128
|
+
baseMap: m.baseMap,
|
|
129
|
+
modifications: [...m.modifications].sort(by((x) => x.name)),
|
|
130
|
+
addedObjects: [...m.addedObjects].sort(by((x) => x.id)),
|
|
131
|
+
worldTransform: m.worldTransform ?? null,
|
|
132
|
+
lights: [...(m.lights ?? [])].sort(by((x) => x.id)),
|
|
133
|
+
preset: resolvePreset(world, presetId),
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Fingerprint of a GAME's layer — whatever of the game's own data changes what its baked dress looks
|
|
138
|
+
* like (racing: the course's rails, barriers, kerbs and the track's own world delta). The game decides
|
|
139
|
+
* what goes in; this only guarantees it hashes the same way everywhere.
|
|
140
|
+
*/
|
|
141
|
+
export function layerFingerprint(value) {
|
|
142
|
+
return fingerprint(value);
|
|
143
|
+
}
|
|
144
|
+
/** Whether a bake still matches the scene it would be shown in. */
|
|
145
|
+
export function isBakeFresh(bake, ctx) {
|
|
146
|
+
if (bake.format !== 1)
|
|
147
|
+
return false;
|
|
148
|
+
if (bake.worldId !== ctx.world.id)
|
|
149
|
+
return false;
|
|
150
|
+
const preset = resolvePreset(ctx.world, ctx.presetId);
|
|
151
|
+
if ((preset?.id ?? null) !== bake.presetId)
|
|
152
|
+
return false;
|
|
153
|
+
if (bake.worldFingerprint !== worldFingerprint(ctx.world, bake.presetId))
|
|
154
|
+
return false;
|
|
155
|
+
// A layer bake is only fresh against the same layer, and a world-only bake is never used for a
|
|
156
|
+
// scene that has a layer the bake does not contain (the layer's meshes would be unlit by it).
|
|
157
|
+
if ((bake.layer?.fingerprint ?? undefined) !== ctx.layerFingerprint)
|
|
158
|
+
return false;
|
|
159
|
+
return true;
|
|
160
|
+
}
|
|
161
|
+
/** {@link BakeStatus} of the newest bake for a preset. */
|
|
162
|
+
export function bakeStatus(bakes, ctx) {
|
|
163
|
+
const presetId = resolvePreset(ctx.world, ctx.presetId)?.id;
|
|
164
|
+
const mine = (bakes ?? []).filter((b) => b.presetId === presetId);
|
|
165
|
+
if (!mine.length)
|
|
166
|
+
return "missing";
|
|
167
|
+
return mine.some((b) => isBakeFresh(b, ctx)) ? "fresh" : "stale";
|
|
168
|
+
}
|
|
169
|
+
/** The newest FRESH bake for the scene, or `null` — in which case the game renders live. */
|
|
170
|
+
export function pickBake(bakes, ctx) {
|
|
171
|
+
const fresh = (bakes ?? []).filter((b) => isBakeFresh(b, ctx));
|
|
172
|
+
if (!fresh.length)
|
|
173
|
+
return null;
|
|
174
|
+
return fresh.reduce((a, b) => (b.bakedAt > a.bakedAt ? b : a));
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Replace the bake for `bake.presetId` (+ layer) in a list, keeping the others. What an editor calls
|
|
178
|
+
* when an Import bake lands, so a list never grows a stale twin per preset.
|
|
179
|
+
*/
|
|
180
|
+
export function withBake(bakes, bake) {
|
|
181
|
+
const same = (b) => b.presetId === bake.presetId && (b.layer?.docId ?? null) === (bake.layer?.docId ?? null);
|
|
182
|
+
return [...(bakes ?? []).filter((b) => !same(b)), bake];
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Stable content hash: key order, float noise and presigned query strings never change it.
|
|
186
|
+
* cyrb53 over a canonical JSON — not cryptographic, and does not need to be.
|
|
187
|
+
*/
|
|
188
|
+
export function fingerprint(value) {
|
|
189
|
+
return cyrb53(canonical(value)).toString(16).padStart(14, "0");
|
|
190
|
+
}
|
|
191
|
+
function canonical(v) {
|
|
192
|
+
if (v === null || v === undefined)
|
|
193
|
+
return "null";
|
|
194
|
+
if (typeof v === "number") {
|
|
195
|
+
if (!Number.isFinite(v))
|
|
196
|
+
return "null";
|
|
197
|
+
// 1e-4 m / 1e-4 rad: far below anything visible, far above a float's round-trip noise.
|
|
198
|
+
const r = Math.round(v * 1e4) / 1e4;
|
|
199
|
+
return Object.is(r, -0) ? "0" : String(r);
|
|
200
|
+
}
|
|
201
|
+
if (typeof v === "string") {
|
|
202
|
+
const s = /^https?:\/\//.test(v) ? v.replace(/[?#].*$/, "") : v;
|
|
203
|
+
return JSON.stringify(s);
|
|
204
|
+
}
|
|
205
|
+
if (typeof v === "boolean")
|
|
206
|
+
return v ? "true" : "false";
|
|
207
|
+
if (Array.isArray(v))
|
|
208
|
+
return `[${v.map(canonical).join(",")}]`;
|
|
209
|
+
if (typeof v === "object") {
|
|
210
|
+
const o = v;
|
|
211
|
+
const keys = Object.keys(o)
|
|
212
|
+
.filter((k) => o[k] !== undefined)
|
|
213
|
+
.sort();
|
|
214
|
+
return `{${keys.map((k) => `${JSON.stringify(k)}:${canonical(o[k])}`).join(",")}}`;
|
|
215
|
+
}
|
|
216
|
+
return "null";
|
|
217
|
+
}
|
|
218
|
+
function cyrb53(str, seed = 0) {
|
|
219
|
+
let h1 = 0xdeadbeef ^ seed;
|
|
220
|
+
let h2 = 0x41c6ce57 ^ seed;
|
|
221
|
+
for (let i = 0; i < str.length; i++) {
|
|
222
|
+
const ch = str.charCodeAt(i);
|
|
223
|
+
h1 = Math.imul(h1 ^ ch, 2654435761);
|
|
224
|
+
h2 = Math.imul(h2 ^ ch, 1597334677);
|
|
225
|
+
}
|
|
226
|
+
h1 = Math.imul(h1 ^ (h1 >>> 16), 2246822507) ^ Math.imul(h2 ^ (h2 >>> 13), 3266489909);
|
|
227
|
+
h2 = Math.imul(h2 ^ (h2 >>> 16), 2246822507) ^ Math.imul(h1 ^ (h1 >>> 13), 3266489909);
|
|
228
|
+
return 4294967296 * (2097151 & h2) + (h1 >>> 0);
|
|
229
|
+
}
|
|
230
|
+
//# sourceMappingURL=world-lighting.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"world-lighting.js","sourceRoot":"","sources":["../src/world-lighting.ts"],"names":[],"mappings":"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"}
|