@marble.fun/track-catalog 3.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/LICENSE +21 -0
- package/README.md +15 -0
- package/assets.json +346 -0
- package/package.json +32 -0
- package/release.json +135 -0
- package/src/audio/game-audio-preference.ts +9 -0
- package/src/authoritative.ts +6 -0
- package/src/catalog.ts +1 -0
- package/src/index.ts +14 -0
- package/src/legacy-stock-catalog.ts +130 -0
- package/src/pages/lobby/lobby-types.ts +140 -0
- package/src/pages/lobby/marble-factory.ts +931 -0
- package/src/pages/play/race/egypt-elevator-blender.ts +159 -0
- package/src/pages/play/race/egypt-portal-frame.ts +382 -0
- package/src/pages/play/race/finish-detection.ts +1 -0
- package/src/pages/play/race/japanese-sakura-castle-support-blender.ts +510 -0
- package/src/pages/play/race/japanese-sakura-kawara-roof.ts +201 -0
- package/src/pages/play/race/japanese-sakura-portal-frame.ts +325 -0
- package/src/pages/play/race/jungle-portal-frame.ts +308 -0
- package/src/pages/play/race/jungle-temple-lift.ts +366 -0
- package/src/pages/play/race/marble-entity.ts +257 -0
- package/src/pages/play/race/marble-material.ts +20 -0
- package/src/pages/play/race/marble-race-engine.ts +2652 -0
- package/src/pages/play/race/path-pattern-layout.ts +142 -0
- package/src/pages/play/race/peg-obstacle-visual.ts +171 -0
- package/src/pages/play/race/physics-debug.ts +1 -0
- package/src/pages/play/race/physics-dry-run.ts +90 -0
- package/src/pages/play/race/portal-derived-elevator-skins.ts +729 -0
- package/src/pages/play/race/portal-pair-color.ts +20 -0
- package/src/pages/play/race/portal-proportions.ts +25 -0
- package/src/pages/play/race/portal-visual.ts +1108 -0
- package/src/pages/play/race/race-camera-director.ts +586 -0
- package/src/pages/play/race/race-camera-obstruction.ts +37 -0
- package/src/pages/play/race/race-camera-tour-planner.ts +274 -0
- package/src/pages/play/race/race-camera.ts +206 -0
- package/src/pages/play/race/race-completion.ts +5 -0
- package/src/pages/play/race/race-configuration.ts +57 -0
- package/src/pages/play/race/race-marble-focus-renderer.ts +134 -0
- package/src/pages/play/race/race-physics-profile.ts +7 -0
- package/src/pages/play/race/race-physics.ts +16 -0
- package/src/pages/play/race/race-playback.ts +41 -0
- package/src/pages/play/race/race-rolling-audio.ts +616 -0
- package/src/pages/play/race/race-simulation-renderer.ts +221 -0
- package/src/pages/play/race/race-terminal-checker-strip.ts +226 -0
- package/src/pages/play/race/race-transform-interpolation.ts +50 -0
- package/src/pages/play/race/race-types.ts +12 -0
- package/src/pages/play/race/rebound-visual.ts +70 -0
- package/src/pages/play/race/seeded-random.ts +23 -0
- package/src/pages/play/race/space-station-portal-frame.ts +275 -0
- package/src/pages/play/race/track-appearance-canopy.ts +946 -0
- package/src/pages/play/race/track-appearance-floor-runner.ts +206 -0
- package/src/pages/play/race/track-appearance-jungle-flower-hedge.ts +483 -0
- package/src/pages/play/race/track-appearance-rope-wall.ts +443 -0
- package/src/pages/play/race/track-appearance-sakura-bridge-fence.ts +305 -0
- package/src/pages/play/race/track-appearance-sakura-castle-wall.ts +1031 -0
- package/src/pages/play/race/track-appearance-space-station-wall.ts +497 -0
- package/src/pages/play/race/track-appearance-stone-block-shell.ts +517 -0
- package/src/pages/play/race/track-appearance-support.ts +983 -0
- package/src/pages/play/race/track-appearance-vampire-carved-wall.ts +545 -0
- package/src/pages/play/race/track-appearance-wall-terminal.ts +167 -0
- package/src/pages/play/race/track-course.ts +271 -0
- package/src/pages/play/race/track-definition.ts +443 -0
- package/src/pages/play/race/track-fence-architecture.ts +33 -0
- package/src/pages/play/race/track-floor-material-rules.ts +51 -0
- package/src/pages/play/race/track-mechanisms.ts +1350 -0
- package/src/pages/play/race/track-physics-surface.ts +6 -0
- package/src/pages/play/race/track-route-network.ts +9 -0
- package/src/pages/play/race/track-shadow-batching.ts +760 -0
- package/src/pages/play/race/track-surface-basis.ts +1 -0
- package/src/pages/play/race/track-surface-topology.ts +326 -0
- package/src/pages/play/race/track-world.ts +10181 -0
- package/src/pages/play/race/under-track-support-architecture.ts +122 -0
- package/src/pages/play/race/vortex-profile.ts +5 -0
- package/src/pages/play/race/vortex-water-effect.ts +863 -0
- package/src/pages/play/race/wild-west-mine-lift.ts +281 -0
- package/src/pages/play/race/wild-west-vortex-housing.ts +572 -0
- package/src/pages/track-lab/atlas-vortex-layout.ts +1 -0
- package/src/pages/track-lab/cone-field-layout.ts +1 -0
- package/src/pages/track-lab/conveyor-layout.ts +1 -0
- package/src/pages/track-lab/engine/canonical-track-assembly.ts +64 -0
- package/src/pages/track-lab/engine/editor-object-geometry.ts +170 -0
- package/src/pages/track-lab/engine/track-lab-connection-state.ts +123 -0
- package/src/pages/track-lab/engine/track-piece-visual.ts +1420 -0
- package/src/pages/track-lab/flipper-bank-layout.ts +1 -0
- package/src/pages/track-lab/flipper-component.ts +127 -0
- package/src/pages/track-lab/rising-wall-layout.ts +1 -0
- package/src/pages/track-lab/simple-obstacle-layout.ts +1 -0
- package/src/pages/track-lab/track-piece-layout.ts +28 -0
- package/src/pages/track-lab/track-runtime-adapter.ts +1152 -0
- package/src/three/geometry-attribute-aliases.ts +24 -0
- package/src/three/geometry-detail.ts +36 -0
- package/src/three/homepage-marble-geometry.ts +15 -0
- package/src/three/japanese-sakura-appearance.ts +252 -0
- package/src/three/jungle-flower-hedge-appearance.ts +7 -0
- package/src/three/jungle-temple-appearance.ts +5 -0
- package/src/three/low-quality-scene-renderer.ts +132 -0
- package/src/three/marble-sphere-geometry.ts +13 -0
- package/src/three/mobile-rendering-budget.ts +11 -0
- package/src/three/painted-valley-appearance.ts +270 -0
- package/src/three/railroad-appearance.ts +71 -0
- package/src/three/renderer-resize.ts +32 -0
- package/src/three/rope-wall-appearance.ts +91 -0
- package/src/three/rugged-track-appearance.ts +2 -0
- package/src/three/scene-lighting.ts +183 -0
- package/src/three/scene-shader-preparation.ts +64 -0
- package/src/three/scene-theme-controller.ts +461 -0
- package/src/three/shadow-shader-preparation.ts +162 -0
- package/src/three/shared-obstacle-metal.ts +44 -0
- package/src/three/soft-additive-particles.ts +57 -0
- package/src/three/space-station-appearance.ts +213 -0
- package/src/three/static-track-batched-mesh.ts +132 -0
- package/src/three/stone-track-shell-appearance.ts +12 -0
- package/src/three/theme-ambience-controller.ts +226 -0
- package/src/three/theme-asset-cache.ts +120 -0
- package/src/three/theme-audio-preference.ts +43 -0
- package/src/three/theme-catalog.ts +1457 -0
- package/src/three/theme-material-assets.ts +258 -0
- package/src/three/theme-material-ktx2-manifest.ts +190 -0
- package/src/three/theme-material-registry.ts +1952 -0
- package/src/three/theme-world-factory.ts +590 -0
- package/src/three/track-connector-batch.ts +125 -0
- package/src/three/track-material-continuity.ts +442 -0
- package/src/three/track-theme.ts +535 -0
- package/src/three/vampire-castle-appearance.ts +258 -0
- package/src/three/visual-frame-pacing.ts +19 -0
- package/src/three/visual-geometry.ts +120 -0
- package/src/three/visual-quality.ts +83 -0
- package/src/three/water-appearance.ts +29 -0
- package/src/three/wild-west-sand-appearance.ts +2 -0
|
@@ -0,0 +1,535 @@
|
|
|
1
|
+
import type { MaterialSlot } from '@marble.fun/physics-engine'
|
|
2
|
+
|
|
3
|
+
import type { TrackSkin } from '../pages/play/race/track-definition'
|
|
4
|
+
|
|
5
|
+
export type ThemeId =
|
|
6
|
+
| 'neutral-studio'
|
|
7
|
+
| 'material-slot-debug'
|
|
8
|
+
| 'rocky-mountain-wild-west'
|
|
9
|
+
| 'ancient-egypt'
|
|
10
|
+
| 'japanese-sakura-lake'
|
|
11
|
+
| 'intergalactic-space-station'
|
|
12
|
+
| 'painted-valley'
|
|
13
|
+
| 'low-poly-jungle'
|
|
14
|
+
| 'vampire-castle'
|
|
15
|
+
|
|
16
|
+
export type WorldMaterialSlot =
|
|
17
|
+
'world.ground' | 'world.primary' | 'world.secondary' | 'world.foliage' | 'world.accent' | 'world.dark'
|
|
18
|
+
|
|
19
|
+
export type ThemeOnlyMaterialSlot =
|
|
20
|
+
| 'lane.guard.backing'
|
|
21
|
+
| 'obstacle.primary'
|
|
22
|
+
| 'obstacle.secondary'
|
|
23
|
+
| 'obstacle.base'
|
|
24
|
+
| 'mechanism.flipper.primary'
|
|
25
|
+
| 'mechanism.flipper.secondary'
|
|
26
|
+
|
|
27
|
+
export type ThemeMaterialSlot = MaterialSlot | ThemeOnlyMaterialSlot | WorldMaterialSlot
|
|
28
|
+
|
|
29
|
+
export type MaterialPatternKind =
|
|
30
|
+
| 'wood'
|
|
31
|
+
| 'rough-wood'
|
|
32
|
+
| 'railroad-planks'
|
|
33
|
+
| 'bridge-planks'
|
|
34
|
+
| 'fence-timber'
|
|
35
|
+
| 'sand'
|
|
36
|
+
| 'rock'
|
|
37
|
+
| 'gunmetal-silver'
|
|
38
|
+
| 'weathered-sheet-metal'
|
|
39
|
+
| 'braided-rope'
|
|
40
|
+
| 'painted-wood'
|
|
41
|
+
| 'painted-stone'
|
|
42
|
+
| 'watercolor'
|
|
43
|
+
| 'panels'
|
|
44
|
+
| 'grid'
|
|
45
|
+
| 'neon-net'
|
|
46
|
+
| 'holographic-hover-grid'
|
|
47
|
+
| 'waves'
|
|
48
|
+
| 'bark'
|
|
49
|
+
| 'jungle-vines'
|
|
50
|
+
| 'leaf-canopy'
|
|
51
|
+
| 'moss-bark'
|
|
52
|
+
| 'ancient-bricks'
|
|
53
|
+
| 'stick-fence'
|
|
54
|
+
| 'debug'
|
|
55
|
+
|
|
56
|
+
export interface MaterialPatternRecipe {
|
|
57
|
+
kind: MaterialPatternKind
|
|
58
|
+
primary: string
|
|
59
|
+
secondary: string
|
|
60
|
+
/** Physical surface size, in track-world units, occupied by one texture tile. */
|
|
61
|
+
worldSize: readonly [number, number]
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export interface MaterialSurfaceTreatment {
|
|
65
|
+
kind: 'floor-perimeter-overlay'
|
|
66
|
+
materialId: string
|
|
67
|
+
bandWidth: number
|
|
68
|
+
/** Physical width, in track-world units, over which the shoulder fades into the floor. */
|
|
69
|
+
featherWidth: number
|
|
70
|
+
normalOffset: number
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
interface MaterialRecipeBase {
|
|
74
|
+
color: string
|
|
75
|
+
roughness: number
|
|
76
|
+
metalness: number
|
|
77
|
+
emissive?: string
|
|
78
|
+
emissiveIntensity?: number
|
|
79
|
+
transparent?: boolean
|
|
80
|
+
opacity?: number
|
|
81
|
+
alphaTest?: number
|
|
82
|
+
depthWrite?: boolean
|
|
83
|
+
netSpacing?: number
|
|
84
|
+
netLineWidth?: number
|
|
85
|
+
netFillOpacity?: number
|
|
86
|
+
/** Hot-core half-width in track-world units for the holographic hover grid. */
|
|
87
|
+
hoverGridCoreWidth?: number
|
|
88
|
+
/** Exponential falloff coefficient for the hover-grid aura. */
|
|
89
|
+
hoverGridAuraFalloff?: number
|
|
90
|
+
/** Aura multiplier for the hover-grid shader. */
|
|
91
|
+
hoverGridAuraStrength?: number
|
|
92
|
+
/** Extra energy at line intersections without adding node geometry. */
|
|
93
|
+
hoverGridJunctionStrength?: number
|
|
94
|
+
envMapIntensity?: number
|
|
95
|
+
pattern?: MaterialPatternRecipe
|
|
96
|
+
textureUrl?: string
|
|
97
|
+
emissiveTextureUrl?: string
|
|
98
|
+
emissiveSurface?: 'all' | 'upward'
|
|
99
|
+
normalTextureUrl?: string
|
|
100
|
+
heightTextureUrl?: string
|
|
101
|
+
roughnessTextureUrl?: string
|
|
102
|
+
aoTextureUrl?: string
|
|
103
|
+
/** Physical surface size, in track-world units, occupied by one image tile. */
|
|
104
|
+
textureWorldSize?: readonly [number, number]
|
|
105
|
+
bumpScale?: number
|
|
106
|
+
normalScale?: readonly [number, number]
|
|
107
|
+
aoMapIntensity?: number
|
|
108
|
+
/** Declarative visual treatment applied to every canonical surface using this material. */
|
|
109
|
+
surfaceTreatment?: MaterialSurfaceTreatment
|
|
110
|
+
/**
|
|
111
|
+
* Optional height-map-driven macro relief for tessellated visual track shells.
|
|
112
|
+
* This never changes the corresponding Rapier collider.
|
|
113
|
+
*/
|
|
114
|
+
visualRelief?: {
|
|
115
|
+
scale: number
|
|
116
|
+
bias: number
|
|
117
|
+
targetEdgeLength: number
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export type MaterialRecipe =
|
|
122
|
+
| (MaterialRecipeBase & {
|
|
123
|
+
kind: 'standard'
|
|
124
|
+
})
|
|
125
|
+
| (MaterialRecipeBase & {
|
|
126
|
+
kind: 'physical'
|
|
127
|
+
clearcoat?: number
|
|
128
|
+
clearcoatRoughness?: number
|
|
129
|
+
transmission?: number
|
|
130
|
+
thickness?: number
|
|
131
|
+
})
|
|
132
|
+
| (MaterialRecipeBase & {
|
|
133
|
+
kind: 'toon'
|
|
134
|
+
gradientSteps?: number
|
|
135
|
+
inkColor?: string
|
|
136
|
+
inkStrength?: number
|
|
137
|
+
})
|
|
138
|
+
|
|
139
|
+
export type TrackAppearanceSurface = 'floor' | 'wall' | 'rail' | 'support'
|
|
140
|
+
|
|
141
|
+
export type TrackAppearanceDetailStyle =
|
|
142
|
+
| 'wild-west-railroad'
|
|
143
|
+
| 'centered-floor-runner'
|
|
144
|
+
| 'wild-west-short-ranch-fence'
|
|
145
|
+
| 'wild-west-tall-stockade'
|
|
146
|
+
| 'shared-rope-wall'
|
|
147
|
+
| 'jungle-flower-hedge'
|
|
148
|
+
| 'shared-stone-block-shell'
|
|
149
|
+
| 'japanese-sakura-castle-wall'
|
|
150
|
+
| 'japanese-sakura-vermilion-bridge-fence'
|
|
151
|
+
| 'space-station-armored-wall'
|
|
152
|
+
|
|
153
|
+
export type TrackAppearanceCanopyStyle = 'wild-west-covered-mine' | 'japanese-sakura-roofed-gate'
|
|
154
|
+
|
|
155
|
+
export type TrackAppearanceSupportStyle =
|
|
156
|
+
| 'wild-west-timber-trestle'
|
|
157
|
+
| 'wild-west-canyon-buttress'
|
|
158
|
+
| 'japanese-sakura-vermilion-underbridge'
|
|
159
|
+
| 'japanese-sakura-dragon-stone'
|
|
160
|
+
|
|
161
|
+
export interface TrackAppearancePreset {
|
|
162
|
+
id: string
|
|
163
|
+
label: string
|
|
164
|
+
material: MaterialRecipe
|
|
165
|
+
/** The real theme default. Selecting it clears the saved override. */
|
|
166
|
+
default?: boolean
|
|
167
|
+
detailStyle?: TrackAppearanceDetailStyle
|
|
168
|
+
/** Visual-only overhead structure paired with this explicit wall choice. */
|
|
169
|
+
canopyStyle?: TrackAppearanceCanopyStyle
|
|
170
|
+
/** Visual-only structure beneath a compatible lane. It never adds collision geometry. */
|
|
171
|
+
supportStyle?: TrackAppearanceSupportStyle
|
|
172
|
+
/** Retained for loading older documents but omitted from new inspector choices. */
|
|
173
|
+
hidden?: boolean
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
export interface TrackAppearancePresets {
|
|
177
|
+
floor?: readonly TrackAppearancePreset[]
|
|
178
|
+
wall?: readonly TrackAppearancePreset[]
|
|
179
|
+
rail?: readonly TrackAppearancePreset[]
|
|
180
|
+
support?: readonly TrackAppearancePreset[]
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
export type ThemeWorldKit =
|
|
184
|
+
'studio' | 'wild-west' | 'ancient-egypt' | 'japanese-sakura-lake' | 'space-station' | 'jungle'
|
|
185
|
+
export type ThemeParticleStyle = 'dust' | 'space-drift' | 'fireflies' | 'petals' | 'none'
|
|
186
|
+
export type ThemeAmbienceStyle = 'studio' | 'desert' | 'space' | 'jungle' | 'meadow' | 'sakura-lake'
|
|
187
|
+
|
|
188
|
+
export interface TrackThemeDefinition {
|
|
189
|
+
id: ThemeId | (string & {})
|
|
190
|
+
version: number
|
|
191
|
+
label: string
|
|
192
|
+
shortLabel: string
|
|
193
|
+
description: string
|
|
194
|
+
/** Small square crop for compact controls and history rows. */
|
|
195
|
+
thumbnailUrl?: string
|
|
196
|
+
/** Portrait crop used by the Track Lab theme carousel. */
|
|
197
|
+
pickerPreviewUrl?: string
|
|
198
|
+
/** Full-frame compressed background used while a world is loading. */
|
|
199
|
+
loadingPreviewUrl?: string
|
|
200
|
+
/** Full Open Graph composition retained for social and legacy consumers. */
|
|
201
|
+
previewUrl?: string
|
|
202
|
+
panoramaUrl?: string
|
|
203
|
+
materials: Readonly<Record<ThemeMaterialSlot, MaterialRecipe>>
|
|
204
|
+
appearancePresets?: TrackAppearancePresets
|
|
205
|
+
/** Theme-private materials used by decorative geometry rather than inspector choices. */
|
|
206
|
+
detailMaterials?: Readonly<Record<string, MaterialRecipe>>
|
|
207
|
+
environment: {
|
|
208
|
+
background: string
|
|
209
|
+
fog: string
|
|
210
|
+
fogNear: number
|
|
211
|
+
fogFar: number
|
|
212
|
+
environmentIntensity: number
|
|
213
|
+
}
|
|
214
|
+
lighting: {
|
|
215
|
+
skyColor: string
|
|
216
|
+
groundColor: string
|
|
217
|
+
ambientIntensity: number
|
|
218
|
+
keyColor: string
|
|
219
|
+
keyIntensity: number
|
|
220
|
+
keyPosition: readonly [number, number, number]
|
|
221
|
+
rimColor: string
|
|
222
|
+
rimIntensity: number
|
|
223
|
+
rimPosition: readonly [number, number, number]
|
|
224
|
+
exposure: number
|
|
225
|
+
practical?: {
|
|
226
|
+
color: string
|
|
227
|
+
intensity: number
|
|
228
|
+
distance: number
|
|
229
|
+
decay: number
|
|
230
|
+
count: number
|
|
231
|
+
heightOffset: number
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
world: {
|
|
235
|
+
kit: ThemeWorldKit
|
|
236
|
+
density: number
|
|
237
|
+
}
|
|
238
|
+
particles: {
|
|
239
|
+
style: ThemeParticleStyle
|
|
240
|
+
color: string
|
|
241
|
+
secondaryColor: string
|
|
242
|
+
count: number
|
|
243
|
+
size: number
|
|
244
|
+
speed: number
|
|
245
|
+
opacity: number
|
|
246
|
+
}
|
|
247
|
+
ambience: {
|
|
248
|
+
style: ThemeAmbienceStyle
|
|
249
|
+
volume: number
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
export function defaultAppearancePreset(
|
|
254
|
+
theme: TrackThemeDefinition,
|
|
255
|
+
surface: TrackAppearanceSurface,
|
|
256
|
+
): TrackAppearancePreset | undefined {
|
|
257
|
+
return theme.appearancePresets?.[surface]?.find(preset => preset.default === true && !preset.hidden)
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
interface ThemePalette {
|
|
261
|
+
laneSurface: string
|
|
262
|
+
laneEdge: string
|
|
263
|
+
laneGuard: string
|
|
264
|
+
structure: string
|
|
265
|
+
structureSecondary: string
|
|
266
|
+
mechanism: string
|
|
267
|
+
moving: string
|
|
268
|
+
route: string
|
|
269
|
+
hazard: string
|
|
270
|
+
finish: string
|
|
271
|
+
energy: string
|
|
272
|
+
portal: string
|
|
273
|
+
glass: string
|
|
274
|
+
worldGround: string
|
|
275
|
+
worldPrimary: string
|
|
276
|
+
worldSecondary: string
|
|
277
|
+
foliage: string
|
|
278
|
+
accent: string
|
|
279
|
+
dark: string
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
interface MaterialSetOptions {
|
|
283
|
+
surfacePattern?: MaterialPatternRecipe
|
|
284
|
+
surfaceEmissive?: string
|
|
285
|
+
surfaceBumpScale?: number
|
|
286
|
+
guardPattern?: MaterialPatternRecipe
|
|
287
|
+
guardEmissive?: string
|
|
288
|
+
transparentGuardBacking?: boolean
|
|
289
|
+
structurePattern?: MaterialPatternRecipe
|
|
290
|
+
trackRoughness?: number
|
|
291
|
+
trackMetalness?: number
|
|
292
|
+
guardMetalness?: number
|
|
293
|
+
clearcoat?: number
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
export function createThemeMaterialSet(
|
|
297
|
+
palette: ThemePalette,
|
|
298
|
+
options: MaterialSetOptions = {},
|
|
299
|
+
): Record<ThemeMaterialSlot, MaterialRecipe> {
|
|
300
|
+
const standard = (
|
|
301
|
+
color: string,
|
|
302
|
+
roughness: number,
|
|
303
|
+
metalness: number,
|
|
304
|
+
emissive?: string,
|
|
305
|
+
pattern?: MaterialPatternRecipe,
|
|
306
|
+
): MaterialRecipe => ({
|
|
307
|
+
kind: 'standard',
|
|
308
|
+
color,
|
|
309
|
+
roughness,
|
|
310
|
+
metalness,
|
|
311
|
+
envMapIntensity: 0.9,
|
|
312
|
+
...(emissive ? { emissive, emissiveIntensity: 0.52 } : {}),
|
|
313
|
+
...(pattern ? { pattern } : {}),
|
|
314
|
+
})
|
|
315
|
+
const guardMaterial = standard(
|
|
316
|
+
palette.laneGuard,
|
|
317
|
+
options.guardPattern ? 0.86 : 0.3,
|
|
318
|
+
options.guardMetalness ?? 0.66,
|
|
319
|
+
options.guardEmissive,
|
|
320
|
+
options.guardPattern,
|
|
321
|
+
)
|
|
322
|
+
const laneEdgeMaterial = standard(
|
|
323
|
+
palette.laneEdge,
|
|
324
|
+
0.76,
|
|
325
|
+
Math.max(0.18, options.trackMetalness ?? 0.12),
|
|
326
|
+
)
|
|
327
|
+
const movingMaterial: MaterialRecipe = {
|
|
328
|
+
...standard(palette.moving, 0.38, 0.28),
|
|
329
|
+
...(options.clearcoat
|
|
330
|
+
? {
|
|
331
|
+
kind: 'physical' as const,
|
|
332
|
+
clearcoat: options.clearcoat,
|
|
333
|
+
clearcoatRoughness: 0.2,
|
|
334
|
+
}
|
|
335
|
+
: {}),
|
|
336
|
+
}
|
|
337
|
+
const routeMaterial = standard(palette.route, 0.32, 0.12, palette.route)
|
|
338
|
+
const hazardMaterial = standard(palette.hazard, 0.38, 0.24, palette.hazard)
|
|
339
|
+
|
|
340
|
+
return {
|
|
341
|
+
'lane.surface': {
|
|
342
|
+
...standard(
|
|
343
|
+
palette.laneSurface,
|
|
344
|
+
options.trackRoughness ?? 0.68,
|
|
345
|
+
options.trackMetalness ?? 0.12,
|
|
346
|
+
options.surfaceEmissive,
|
|
347
|
+
options.surfacePattern,
|
|
348
|
+
),
|
|
349
|
+
...(options.surfaceBumpScale ? { bumpScale: options.surfaceBumpScale } : {}),
|
|
350
|
+
},
|
|
351
|
+
'lane.edge': laneEdgeMaterial,
|
|
352
|
+
'lane.guard': guardMaterial,
|
|
353
|
+
'lane.guard.backing': options.transparentGuardBacking
|
|
354
|
+
? { ...guardMaterial, transparent: true, opacity: 0, depthWrite: false }
|
|
355
|
+
: guardMaterial,
|
|
356
|
+
'structure.primary': standard(palette.structure, 0.72, 0.24, undefined, options.structurePattern),
|
|
357
|
+
'structure.secondary': standard(palette.structureSecondary, 0.74, 0.28),
|
|
358
|
+
'mechanism.body': standard(palette.mechanism, 0.56, 0.42, undefined, options.structurePattern),
|
|
359
|
+
'mechanism.moving': movingMaterial,
|
|
360
|
+
'mechanism.flipper.primary': movingMaterial,
|
|
361
|
+
'mechanism.flipper.secondary': movingMaterial,
|
|
362
|
+
'signal.route': routeMaterial,
|
|
363
|
+
'signal.hazard': hazardMaterial,
|
|
364
|
+
'obstacle.primary': hazardMaterial,
|
|
365
|
+
'obstacle.secondary': laneEdgeMaterial,
|
|
366
|
+
'obstacle.base': routeMaterial,
|
|
367
|
+
'signal.finish': standard(palette.finish, 0.28, 0.14, palette.finish),
|
|
368
|
+
'effect.energy': standard(palette.energy, 0.22, 0.12, palette.energy),
|
|
369
|
+
'effect.portal': standard(palette.portal, 0.18, 0.12, palette.portal),
|
|
370
|
+
glass: {
|
|
371
|
+
kind: 'physical',
|
|
372
|
+
color: palette.glass,
|
|
373
|
+
roughness: 0.1,
|
|
374
|
+
metalness: 0,
|
|
375
|
+
clearcoat: 1,
|
|
376
|
+
clearcoatRoughness: 0.08,
|
|
377
|
+
transparent: true,
|
|
378
|
+
opacity: 0.38,
|
|
379
|
+
depthWrite: false,
|
|
380
|
+
envMapIntensity: 1.35,
|
|
381
|
+
},
|
|
382
|
+
'world.ground': standard(
|
|
383
|
+
palette.worldGround,
|
|
384
|
+
options.trackRoughness ?? 0.78,
|
|
385
|
+
options.trackMetalness ?? 0.04,
|
|
386
|
+
undefined,
|
|
387
|
+
options.surfacePattern,
|
|
388
|
+
),
|
|
389
|
+
'world.primary': standard(palette.worldPrimary, 0.72, 0.16, undefined, options.structurePattern),
|
|
390
|
+
'world.secondary': standard(palette.worldSecondary, 0.58, 0.28),
|
|
391
|
+
'world.foliage': standard(palette.foliage, 0.88, 0),
|
|
392
|
+
'world.accent': standard(palette.accent, 0.3, 0.18, palette.accent),
|
|
393
|
+
'world.dark': standard(palette.dark, 0.9, 0.08),
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
const neutralMaterials = createThemeMaterialSet({
|
|
398
|
+
laneSurface: '#aeb9be',
|
|
399
|
+
laneEdge: '#27313a',
|
|
400
|
+
laneGuard: '#e3eaeb',
|
|
401
|
+
structure: '#35414a',
|
|
402
|
+
structureSecondary: '#1d252c',
|
|
403
|
+
mechanism: '#55636d',
|
|
404
|
+
moving: '#ff765f',
|
|
405
|
+
route: '#dfff65',
|
|
406
|
+
hazard: '#ff765f',
|
|
407
|
+
finish: '#dfff65',
|
|
408
|
+
energy: '#61ddff',
|
|
409
|
+
portal: '#b173ff',
|
|
410
|
+
glass: '#c6ecff',
|
|
411
|
+
worldGround: '#161d22',
|
|
412
|
+
worldPrimary: '#3a474f',
|
|
413
|
+
worldSecondary: '#8b9ba4',
|
|
414
|
+
foliage: '#50665a',
|
|
415
|
+
accent: '#dfff65',
|
|
416
|
+
dark: '#0a0e12',
|
|
417
|
+
})
|
|
418
|
+
|
|
419
|
+
export const NEUTRAL_STUDIO_THEME: TrackThemeDefinition = {
|
|
420
|
+
id: 'neutral-studio',
|
|
421
|
+
version: 2,
|
|
422
|
+
label: 'Neutral Studio',
|
|
423
|
+
shortLabel: 'Studio',
|
|
424
|
+
description: 'A clean graphite studio for focused track construction.',
|
|
425
|
+
materials: neutralMaterials,
|
|
426
|
+
environment: {
|
|
427
|
+
background: '#080b10',
|
|
428
|
+
fog: '#0b1016',
|
|
429
|
+
fogNear: 45,
|
|
430
|
+
fogFar: 220,
|
|
431
|
+
environmentIntensity: 0.95,
|
|
432
|
+
},
|
|
433
|
+
lighting: {
|
|
434
|
+
skyColor: '#cbd7df',
|
|
435
|
+
groundColor: '#11161a',
|
|
436
|
+
ambientIntensity: 1.8,
|
|
437
|
+
keyColor: '#ffffff',
|
|
438
|
+
keyIntensity: 3.1,
|
|
439
|
+
keyPosition: [18, 30, 16],
|
|
440
|
+
rimColor: '#8eb7ff',
|
|
441
|
+
rimIntensity: 0.8,
|
|
442
|
+
rimPosition: [-24, 18, -20],
|
|
443
|
+
exposure: 1,
|
|
444
|
+
},
|
|
445
|
+
world: { kit: 'studio', density: 0.35 },
|
|
446
|
+
particles: {
|
|
447
|
+
style: 'none',
|
|
448
|
+
color: '#ffffff',
|
|
449
|
+
secondaryColor: '#ffffff',
|
|
450
|
+
count: 0,
|
|
451
|
+
size: 0.1,
|
|
452
|
+
speed: 0,
|
|
453
|
+
opacity: 0,
|
|
454
|
+
},
|
|
455
|
+
ambience: { style: 'studio', volume: 0.035 },
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
export const MATERIAL_SLOT_DEBUG_THEME: TrackThemeDefinition = {
|
|
459
|
+
...NEUTRAL_STUDIO_THEME,
|
|
460
|
+
id: 'material-slot-debug',
|
|
461
|
+
version: 2,
|
|
462
|
+
label: 'Material Slot Debug',
|
|
463
|
+
shortLabel: 'Debug',
|
|
464
|
+
description: 'Every semantic surface role rendered as a distinct diagnostic colour.',
|
|
465
|
+
materials: createThemeMaterialSet({
|
|
466
|
+
laneSurface: '#00d9ff',
|
|
467
|
+
laneEdge: '#1747ff',
|
|
468
|
+
laneGuard: '#f7ff00',
|
|
469
|
+
structure: '#8038ff',
|
|
470
|
+
structureSecondary: '#de32ff',
|
|
471
|
+
mechanism: '#ff8a00',
|
|
472
|
+
moving: '#ff174d',
|
|
473
|
+
route: '#26ff72',
|
|
474
|
+
hazard: '#ff0033',
|
|
475
|
+
finish: '#ffffff',
|
|
476
|
+
energy: '#00fff0',
|
|
477
|
+
portal: '#ff40d6',
|
|
478
|
+
glass: '#89e9ff',
|
|
479
|
+
worldGround: '#242424',
|
|
480
|
+
worldPrimary: '#7c00ff',
|
|
481
|
+
worldSecondary: '#ff8a00',
|
|
482
|
+
foliage: '#00ff3c',
|
|
483
|
+
accent: '#fff000',
|
|
484
|
+
dark: '#050505',
|
|
485
|
+
}),
|
|
486
|
+
environment: {
|
|
487
|
+
background: '#050508',
|
|
488
|
+
fog: '#090913',
|
|
489
|
+
fogNear: 55,
|
|
490
|
+
fogFar: 220,
|
|
491
|
+
environmentIntensity: 1,
|
|
492
|
+
},
|
|
493
|
+
lighting: {
|
|
494
|
+
...NEUTRAL_STUDIO_THEME.lighting,
|
|
495
|
+
ambientIntensity: 1.9,
|
|
496
|
+
keyIntensity: 3.4,
|
|
497
|
+
},
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
export function createTrackSkinTheme(skin: TrackSkin): TrackThemeDefinition {
|
|
501
|
+
const color = (value: number): string => `#${value.toString(16).padStart(6, '0')}`
|
|
502
|
+
|
|
503
|
+
return {
|
|
504
|
+
...NEUTRAL_STUDIO_THEME,
|
|
505
|
+
id: skin.id,
|
|
506
|
+
label: skin.label,
|
|
507
|
+
shortLabel: skin.label,
|
|
508
|
+
materials: createThemeMaterialSet({
|
|
509
|
+
laneSurface: color(skin.trackTop),
|
|
510
|
+
laneEdge: color(skin.trackEdge),
|
|
511
|
+
laneGuard: color(skin.rail),
|
|
512
|
+
structure: color(skin.structure),
|
|
513
|
+
structureSecondary: color(skin.trackEdge),
|
|
514
|
+
mechanism: color(skin.structure),
|
|
515
|
+
moving: color(skin.hazard),
|
|
516
|
+
route: color(skin.signal),
|
|
517
|
+
hazard: color(skin.hazard),
|
|
518
|
+
finish: color(skin.signal),
|
|
519
|
+
energy: color(skin.signal),
|
|
520
|
+
portal: '#b173ff',
|
|
521
|
+
glass: '#c6ecff',
|
|
522
|
+
worldGround: color(skin.fog),
|
|
523
|
+
worldPrimary: color(skin.structure),
|
|
524
|
+
worldSecondary: color(skin.rail),
|
|
525
|
+
foliage: '#50665a',
|
|
526
|
+
accent: color(skin.signal),
|
|
527
|
+
dark: color(skin.background),
|
|
528
|
+
}),
|
|
529
|
+
environment: {
|
|
530
|
+
...NEUTRAL_STUDIO_THEME.environment,
|
|
531
|
+
background: color(skin.background),
|
|
532
|
+
fog: color(skin.fog),
|
|
533
|
+
},
|
|
534
|
+
}
|
|
535
|
+
}
|