@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,729 @@
|
|
|
1
|
+
import * as THREE from 'three'
|
|
2
|
+
|
|
3
|
+
import { VisualSphereGeometry, VisualExtrudeGeometry, VisualBoxGeometry } from '../../../three/visual-geometry'
|
|
4
|
+
import { mergeGeometries } from 'three/examples/jsm/utils/BufferGeometryUtils.js'
|
|
5
|
+
|
|
6
|
+
import { addJapaneseSakuraKawaraRoof } from './japanese-sakura-kawara-roof'
|
|
7
|
+
import {
|
|
8
|
+
addEgyptElevatorBlenderPlatform,
|
|
9
|
+
addEgyptElevatorBlenderShell,
|
|
10
|
+
applyEgyptElevatorBlenderMaterials,
|
|
11
|
+
type EgyptElevatorBlenderMaterials,
|
|
12
|
+
} from './egypt-elevator-blender'
|
|
13
|
+
import { addSpaceStationWallVisual, type SpaceStationWallPathRun } from './track-appearance-space-station-wall'
|
|
14
|
+
import { createCanonicalWallRun, type RawWallFrame } from './track-surface-topology'
|
|
15
|
+
|
|
16
|
+
export const EGYPT_ELEVATOR_THEME_ID = 'ancient-egypt'
|
|
17
|
+
export const JAPAN_ELEVATOR_THEME_ID = 'japanese-sakura-lake'
|
|
18
|
+
export const SPACE_ELEVATOR_THEME_ID = 'intergalactic-space-station'
|
|
19
|
+
|
|
20
|
+
export interface PortalDerivedElevatorMaterials {
|
|
21
|
+
egypt: {
|
|
22
|
+
sandstone: THREE.Material
|
|
23
|
+
carvedStone: THREE.Material
|
|
24
|
+
pyramidStone: THREE.Material
|
|
25
|
+
pyramidGold: THREE.Material
|
|
26
|
+
bronze: THREE.Material
|
|
27
|
+
gold: THREE.Material
|
|
28
|
+
paving: THREE.Material
|
|
29
|
+
signal: THREE.Material
|
|
30
|
+
}
|
|
31
|
+
japan: {
|
|
32
|
+
vermilion: THREE.Material
|
|
33
|
+
timber: THREE.Material
|
|
34
|
+
plaster: THREE.Material
|
|
35
|
+
stone: THREE.Material
|
|
36
|
+
kawara: THREE.Material
|
|
37
|
+
hardware: THREE.Material
|
|
38
|
+
signal: THREE.Material
|
|
39
|
+
}
|
|
40
|
+
space: {
|
|
41
|
+
chassis: THREE.Material
|
|
42
|
+
panel: THREE.Material
|
|
43
|
+
rib: THREE.Material
|
|
44
|
+
trim: THREE.Material
|
|
45
|
+
light: THREE.Material
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
interface ElevatorSkinOptions {
|
|
50
|
+
activeThemeId?: string
|
|
51
|
+
id: string
|
|
52
|
+
bottom: THREE.Vector3
|
|
53
|
+
rotation: THREE.Quaternion
|
|
54
|
+
width: number
|
|
55
|
+
height: number
|
|
56
|
+
depth: number
|
|
57
|
+
releaseHeight: number
|
|
58
|
+
materials: PortalDerivedElevatorMaterials
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
interface Batch {
|
|
62
|
+
name: string
|
|
63
|
+
material: THREE.Material
|
|
64
|
+
parts: THREE.BufferGeometry[]
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
interface RearEnclosureLayout {
|
|
68
|
+
panelZ: number
|
|
69
|
+
panelBottom: number
|
|
70
|
+
panelTop: number
|
|
71
|
+
openingBottom: number
|
|
72
|
+
openingTop: number
|
|
73
|
+
lowerHeight: number
|
|
74
|
+
upperHeight: number
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const UNIT = new THREE.Vector3(1, 1, 1)
|
|
78
|
+
const IDENTITY = new THREE.Quaternion()
|
|
79
|
+
const LOCAL_UP = new THREE.Vector3(0, 1, 0)
|
|
80
|
+
|
|
81
|
+
function rearEnclosureLayout(height: number, depth: number, releaseHeight: number): RearEnclosureLayout {
|
|
82
|
+
const panelBottom = -0.12
|
|
83
|
+
const panelTop = height + 0.68
|
|
84
|
+
const openingBottom = THREE.MathUtils.clamp(releaseHeight - 0.34, 0.9, height - 1.5)
|
|
85
|
+
const openingTop = Math.min(height + 0.42, releaseHeight + 1.48)
|
|
86
|
+
return {
|
|
87
|
+
panelZ: -depth / 2 - 0.05,
|
|
88
|
+
panelBottom,
|
|
89
|
+
panelTop,
|
|
90
|
+
openingBottom,
|
|
91
|
+
openingTop,
|
|
92
|
+
lowerHeight: openingBottom - panelBottom,
|
|
93
|
+
upperHeight: Math.max(0.24, panelTop - openingTop),
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function markRearEnclosure(root: THREE.Group, layout: RearEnclosureLayout, construction: string): void {
|
|
98
|
+
root.userData.hasBackWall = true
|
|
99
|
+
root.userData.releaseOpeningBottom = layout.openingBottom
|
|
100
|
+
root.userData.releaseOpeningTop = layout.openingTop
|
|
101
|
+
root.userData.rearEnclosureConstruction = construction
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function metricPlanarUvs(geometry: THREE.BufferGeometry): THREE.BufferGeometry {
|
|
105
|
+
const prepared = geometry.index ? geometry.toNonIndexed() : geometry
|
|
106
|
+
if (prepared !== geometry) geometry.dispose()
|
|
107
|
+
const position = prepared.getAttribute('position')
|
|
108
|
+
const normal = prepared.getAttribute('normal')
|
|
109
|
+
const uv = new Float32Array(position.count * 2)
|
|
110
|
+
const themeNetUv = new Float32Array(position.count * 4)
|
|
111
|
+
for (let index = 0; index < position.count; index += 1) {
|
|
112
|
+
const x = position.getX(index)
|
|
113
|
+
const y = position.getY(index)
|
|
114
|
+
const z = position.getZ(index)
|
|
115
|
+
const nx = Math.abs(normal.getX(index))
|
|
116
|
+
const ny = Math.abs(normal.getY(index))
|
|
117
|
+
const nz = Math.abs(normal.getZ(index))
|
|
118
|
+
if (ny >= nx && ny >= nz) {
|
|
119
|
+
uv[index * 2] = x
|
|
120
|
+
uv[index * 2 + 1] = z
|
|
121
|
+
} else if (nx >= nz) {
|
|
122
|
+
uv[index * 2] = z
|
|
123
|
+
uv[index * 2 + 1] = y
|
|
124
|
+
} else {
|
|
125
|
+
uv[index * 2] = x
|
|
126
|
+
uv[index * 2 + 1] = y
|
|
127
|
+
}
|
|
128
|
+
themeNetUv[index * 4 + 2] = 1
|
|
129
|
+
}
|
|
130
|
+
prepared.setAttribute('uv', new THREE.BufferAttribute(uv, 2))
|
|
131
|
+
prepared.setAttribute('themeNetUv', new THREE.BufferAttribute(themeNetUv, 4))
|
|
132
|
+
return prepared
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function transformed(
|
|
136
|
+
geometry: THREE.BufferGeometry,
|
|
137
|
+
position: THREE.Vector3,
|
|
138
|
+
rotation = IDENTITY,
|
|
139
|
+
scale = UNIT,
|
|
140
|
+
): THREE.BufferGeometry {
|
|
141
|
+
geometry.applyMatrix4(new THREE.Matrix4().compose(position, rotation, scale))
|
|
142
|
+
return metricPlanarUvs(geometry)
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function roundedBox(size: THREE.Vector3, position: THREE.Vector3, radius = 0.045): THREE.BufferGeometry {
|
|
146
|
+
return transformed(
|
|
147
|
+
new VisualBoxGeometry(size.x, size.y, size.z, 2, Math.min(radius, size.x / 4, size.y / 4, size.z / 4)),
|
|
148
|
+
position,
|
|
149
|
+
)
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function beam(start: THREE.Vector3, end: THREE.Vector3, width: number, depth: number): THREE.BufferGeometry {
|
|
153
|
+
const direction = end.clone().sub(start)
|
|
154
|
+
return transformed(
|
|
155
|
+
new VisualBoxGeometry(width, direction.length(), depth, 2, Math.min(width, depth) * 0.16),
|
|
156
|
+
start.clone().lerp(end, 0.5),
|
|
157
|
+
new THREE.Quaternion().setFromUnitVectors(LOCAL_UP, direction.clone().normalize()),
|
|
158
|
+
)
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function addBatch(group: THREE.Group, id: string, batch: Batch): THREE.Mesh {
|
|
162
|
+
const geometry = mergeGeometries(batch.parts, false)
|
|
163
|
+
batch.parts.forEach(part => part.dispose())
|
|
164
|
+
if (!geometry) throw new Error(`Unable to merge elevator batch "${batch.name}".`)
|
|
165
|
+
geometry.computeBoundingBox()
|
|
166
|
+
geometry.computeBoundingSphere()
|
|
167
|
+
geometry.userData.visualOnly = true
|
|
168
|
+
const mesh = new THREE.Mesh(geometry, batch.material)
|
|
169
|
+
mesh.name = `${id}-${batch.name}`
|
|
170
|
+
mesh.castShadow = true
|
|
171
|
+
mesh.receiveShadow = true
|
|
172
|
+
mesh.userData.decorative = true
|
|
173
|
+
mesh.userData.visualOnly = true
|
|
174
|
+
group.add(mesh)
|
|
175
|
+
return mesh
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function markTheme(group: THREE.Group, themeId: string, style: string): void {
|
|
179
|
+
group.userData.themeOnly = themeId
|
|
180
|
+
group.userData.decorative = true
|
|
181
|
+
group.userData.visualOnly = true
|
|
182
|
+
group.userData.elevatorStyle = style
|
|
183
|
+
group.traverse(object => {
|
|
184
|
+
object.userData.themeOnly = themeId
|
|
185
|
+
object.userData.decorative = true
|
|
186
|
+
object.userData.visualOnly = true
|
|
187
|
+
object.userData.elevatorStyle = style
|
|
188
|
+
if (object instanceof THREE.Mesh || object instanceof THREE.InstancedMesh) {
|
|
189
|
+
object.castShadow = true
|
|
190
|
+
object.receiveShadow = true
|
|
191
|
+
}
|
|
192
|
+
})
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
function liveEgyptBlenderMaterials(materials: PortalDerivedElevatorMaterials): EgyptElevatorBlenderMaterials {
|
|
196
|
+
return {
|
|
197
|
+
get sandstone() {
|
|
198
|
+
return materials.egypt.sandstone
|
|
199
|
+
},
|
|
200
|
+
get carvedStone() {
|
|
201
|
+
return materials.egypt.carvedStone
|
|
202
|
+
},
|
|
203
|
+
get pyramidStone() {
|
|
204
|
+
return materials.egypt.pyramidStone
|
|
205
|
+
},
|
|
206
|
+
get pyramidGold() {
|
|
207
|
+
return materials.egypt.pyramidGold
|
|
208
|
+
},
|
|
209
|
+
get bronze() {
|
|
210
|
+
return materials.egypt.bronze
|
|
211
|
+
},
|
|
212
|
+
get paving() {
|
|
213
|
+
return materials.egypt.paving
|
|
214
|
+
},
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
function addEgyptSkin(group: THREE.Group, options: ElevatorSkinOptions): THREE.Group {
|
|
219
|
+
const { id, width, height, depth, releaseHeight, materials } = options
|
|
220
|
+
const root = new THREE.Group()
|
|
221
|
+
root.name = `${id}-egypt-temple-hoist`
|
|
222
|
+
const rear = rearEnclosureLayout(height, depth, releaseHeight)
|
|
223
|
+
markRearEnclosure(root, rear, 'blender-authored-recessed-ashlar-chamber')
|
|
224
|
+
root.userData.roofConstruction = 'six-course-tura-limestone-pyramid-with-hammered-gold-pyramidion'
|
|
225
|
+
root.userData.platformCount = 4
|
|
226
|
+
root.userData.approvedReference = '/docs/material-contracts/theme-elevator-egypt-approved-v3.png'
|
|
227
|
+
const blenderMaterials = liveEgyptBlenderMaterials(materials)
|
|
228
|
+
root.userData.applySemanticTheme = () => applyEgyptElevatorBlenderMaterials(root, blenderMaterials)
|
|
229
|
+
addEgyptElevatorBlenderShell(root, id, blenderMaterials, new THREE.Vector3(width / 5.5, height / 8.5, depth / 4))
|
|
230
|
+
|
|
231
|
+
root.position.copy(options.bottom)
|
|
232
|
+
root.quaternion.copy(options.rotation)
|
|
233
|
+
markTheme(root, EGYPT_ELEVATOR_THEME_ID, 'approved-egypt-blender-pylon-hoist-v5')
|
|
234
|
+
group.add(root)
|
|
235
|
+
return root
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
function addJapanSkin(group: THREE.Group, options: ElevatorSkinOptions): THREE.Group {
|
|
239
|
+
const { id, width, height, depth, releaseHeight, materials } = options
|
|
240
|
+
const root = new THREE.Group()
|
|
241
|
+
root.name = `${id}-japanese-gatehouse-hoist`
|
|
242
|
+
const vermilion: THREE.BufferGeometry[] = []
|
|
243
|
+
const timber: THREE.BufferGeometry[] = []
|
|
244
|
+
const plaster: THREE.BufferGeometry[] = []
|
|
245
|
+
const plasterGables: THREE.BufferGeometry[] = []
|
|
246
|
+
const rearPlaster: THREE.BufferGeometry[] = []
|
|
247
|
+
const stone: THREE.BufferGeometry[] = []
|
|
248
|
+
const hardware: THREE.BufferGeometry[] = []
|
|
249
|
+
const postX = width / 2 + 0.2
|
|
250
|
+
const postZ = depth / 2 + 0.18
|
|
251
|
+
const roofHalfDepth = depth / 2 + 0.46
|
|
252
|
+
const ridgeY = height + 1.7
|
|
253
|
+
const eaveY = ridgeY - 0.82 * (roofHalfDepth / 1.32)
|
|
254
|
+
|
|
255
|
+
for (const x of [-postX, postX]) {
|
|
256
|
+
for (const z of [-postZ, postZ]) {
|
|
257
|
+
vermilion.push(
|
|
258
|
+
roundedBox(new THREE.Vector3(0.54, height + 0.5, 0.54), new THREE.Vector3(x, height / 2 + 0.02, z), 0.07),
|
|
259
|
+
)
|
|
260
|
+
stone.push(
|
|
261
|
+
roundedBox(new THREE.Vector3(0.78, 0.34, 0.78), new THREE.Vector3(x, -0.19, z), 0.055),
|
|
262
|
+
roundedBox(new THREE.Vector3(0.64, 0.2, 0.66), new THREE.Vector3(x, 0.05, z), 0.04),
|
|
263
|
+
)
|
|
264
|
+
hardware.push(
|
|
265
|
+
roundedBox(
|
|
266
|
+
new THREE.Vector3(0.15, 0.34, 0.055),
|
|
267
|
+
new THREE.Vector3(x - 0.14, 0.3, z + Math.sign(z) * 0.28),
|
|
268
|
+
0.018,
|
|
269
|
+
),
|
|
270
|
+
roundedBox(
|
|
271
|
+
new THREE.Vector3(0.15, 0.34, 0.055),
|
|
272
|
+
new THREE.Vector3(x + 0.14, 0.3, z + Math.sign(z) * 0.28),
|
|
273
|
+
0.018,
|
|
274
|
+
),
|
|
275
|
+
)
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
for (const z of [-postZ, postZ]) {
|
|
280
|
+
vermilion.push(
|
|
281
|
+
roundedBox(new THREE.Vector3(width + 1.18, 0.34, 0.68), new THREE.Vector3(0, height + 0.06, z), 0.055),
|
|
282
|
+
roundedBox(new THREE.Vector3(width + 0.42, 0.22, 0.54), new THREE.Vector3(0, height - 0.27, z), 0.045),
|
|
283
|
+
)
|
|
284
|
+
for (let bracket = -6; bracket <= 6; bracket += 1) {
|
|
285
|
+
vermilion.push(
|
|
286
|
+
roundedBox(
|
|
287
|
+
new THREE.Vector3(0.18, 0.22, 0.82),
|
|
288
|
+
new THREE.Vector3((bracket * width) / 13, height + 0.28, z),
|
|
289
|
+
0.025,
|
|
290
|
+
),
|
|
291
|
+
)
|
|
292
|
+
}
|
|
293
|
+
for (const side of [-1, 1] as const) {
|
|
294
|
+
vermilion.push(
|
|
295
|
+
beam(
|
|
296
|
+
new THREE.Vector3(side * (postX - 0.08), height - 0.72, z),
|
|
297
|
+
new THREE.Vector3(side * (postX - 0.6), height - 0.18, z),
|
|
298
|
+
0.2,
|
|
299
|
+
0.34,
|
|
300
|
+
),
|
|
301
|
+
)
|
|
302
|
+
plaster.push(
|
|
303
|
+
roundedBox(
|
|
304
|
+
new THREE.Vector3(0.3, 0.8, 0.46),
|
|
305
|
+
new THREE.Vector3(side * (postX - 0.31), height - 0.58, z),
|
|
306
|
+
0.035,
|
|
307
|
+
),
|
|
308
|
+
roundedBox(new THREE.Vector3(0.36, 0.2, 0.52), new THREE.Vector3(side * (postX - 0.5), height - 0.18, z), 0.03),
|
|
309
|
+
)
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
const sharedRear = rearEnclosureLayout(height, depth, releaseHeight)
|
|
314
|
+
const rear: RearEnclosureLayout = {
|
|
315
|
+
...sharedRear,
|
|
316
|
+
panelTop: height + 0.1,
|
|
317
|
+
upperHeight: Math.max(0.24, height + 0.1 - sharedRear.openingTop),
|
|
318
|
+
}
|
|
319
|
+
markRearEnclosure(root, rear, 'castle-plaster-panels-with-vermilion-release-frame')
|
|
320
|
+
for (const side of [-1, 1] as const) {
|
|
321
|
+
const x = side * (postX - 0.11)
|
|
322
|
+
// The side infill is a continuous architectural backing from its stone
|
|
323
|
+
// sill to the roof gable. Stopping it at the nominal wall height exposed
|
|
324
|
+
// the diagonal timber behind it as triangular "missing material" wedges.
|
|
325
|
+
const plasterBottom = 0.45
|
|
326
|
+
const plasterTop = eaveY + 0.02
|
|
327
|
+
// Underlap the corner posts as well; the old depth stopped 0.3 units
|
|
328
|
+
// before each post and exposed the framing at both vertical seams.
|
|
329
|
+
const plasterDepth = depth - 0.14
|
|
330
|
+
plaster.push(
|
|
331
|
+
roundedBox(
|
|
332
|
+
new THREE.Vector3(0.16, plasterTop - plasterBottom, plasterDepth),
|
|
333
|
+
new THREE.Vector3(x, (plasterBottom + plasterTop) / 2, 0),
|
|
334
|
+
0.03,
|
|
335
|
+
),
|
|
336
|
+
)
|
|
337
|
+
stone.push(roundedBox(new THREE.Vector3(0.23, 0.32, depth - 0.64), new THREE.Vector3(x, 0.16, 0), 0.03))
|
|
338
|
+
for (const y of [0.62, 2.58, 4.54, 6.5, height - 0.32]) {
|
|
339
|
+
timber.push(roundedBox(new THREE.Vector3(0.25, 0.16, depth - 0.5), new THREE.Vector3(x, y, 0), 0.035))
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
rearPlaster.push(
|
|
343
|
+
roundedBox(
|
|
344
|
+
new THREE.Vector3(width - 0.46, rear.lowerHeight, 0.16),
|
|
345
|
+
new THREE.Vector3(0, (rear.panelBottom + rear.openingBottom) / 2, rear.panelZ),
|
|
346
|
+
0.035,
|
|
347
|
+
),
|
|
348
|
+
roundedBox(
|
|
349
|
+
new THREE.Vector3(width - 0.46, rear.upperHeight, 0.16),
|
|
350
|
+
new THREE.Vector3(0, (rear.openingTop + rear.panelTop) / 2, rear.panelZ),
|
|
351
|
+
0.035,
|
|
352
|
+
),
|
|
353
|
+
)
|
|
354
|
+
for (const y of [rear.openingBottom - 0.08, rear.openingTop + 0.08]) {
|
|
355
|
+
vermilion.push(
|
|
356
|
+
roundedBox(new THREE.Vector3(width - 0.14, 0.2, 0.24), new THREE.Vector3(0, y, rear.panelZ + 0.02), 0.04),
|
|
357
|
+
)
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
const gableShape = new THREE.Shape()
|
|
361
|
+
gableShape.moveTo(-roofHalfDepth + 0.18, eaveY - 0.02)
|
|
362
|
+
gableShape.lineTo(0, ridgeY - 0.15)
|
|
363
|
+
gableShape.lineTo(roofHalfDepth - 0.18, eaveY - 0.02)
|
|
364
|
+
gableShape.closePath()
|
|
365
|
+
for (const side of [-1, 1] as const) {
|
|
366
|
+
const gable = new VisualExtrudeGeometry(gableShape, {
|
|
367
|
+
depth: 0.16,
|
|
368
|
+
steps: 1,
|
|
369
|
+
bevelEnabled: true,
|
|
370
|
+
bevelSegments: 2,
|
|
371
|
+
bevelSize: 0.025,
|
|
372
|
+
bevelThickness: 0.018,
|
|
373
|
+
})
|
|
374
|
+
gable.applyMatrix4(
|
|
375
|
+
new THREE.Matrix4().compose(
|
|
376
|
+
new THREE.Vector3(side * (postX - 0.08), 0, 0),
|
|
377
|
+
new THREE.Quaternion().setFromAxisAngle(LOCAL_UP, (side * Math.PI) / 2),
|
|
378
|
+
UNIT,
|
|
379
|
+
),
|
|
380
|
+
)
|
|
381
|
+
plasterGables.push(metricPlanarUvs(gable))
|
|
382
|
+
const gableX = side * postX
|
|
383
|
+
timber.push(
|
|
384
|
+
roundedBox(
|
|
385
|
+
new THREE.Vector3(0.2, 0.18, roofHalfDepth * 2 - 0.28),
|
|
386
|
+
new THREE.Vector3(gableX, eaveY - 0.1, 0),
|
|
387
|
+
0.035,
|
|
388
|
+
),
|
|
389
|
+
beam(
|
|
390
|
+
new THREE.Vector3(gableX, eaveY - 0.01, -roofHalfDepth + 0.12),
|
|
391
|
+
new THREE.Vector3(gableX, ridgeY - 0.05, 0),
|
|
392
|
+
0.18,
|
|
393
|
+
0.2,
|
|
394
|
+
),
|
|
395
|
+
beam(
|
|
396
|
+
new THREE.Vector3(gableX, ridgeY - 0.05, 0),
|
|
397
|
+
new THREE.Vector3(gableX, eaveY - 0.01, roofHalfDepth - 0.12),
|
|
398
|
+
0.18,
|
|
399
|
+
0.2,
|
|
400
|
+
),
|
|
401
|
+
)
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
addBatch(root, id, { name: 'japan-vermilion-joinery', material: materials.japan.vermilion, parts: vermilion })
|
|
405
|
+
addBatch(root, id, { name: 'japan-castle-timber-framing', material: materials.japan.timber, parts: timber })
|
|
406
|
+
addBatch(root, id, { name: 'japan-castle-plaster-infill', material: materials.japan.plaster, parts: plaster })
|
|
407
|
+
addBatch(root, id, { name: 'japan-castle-plaster-gables', material: materials.japan.plaster, parts: plasterGables })
|
|
408
|
+
addBatch(root, id, { name: 'japan-rear-enclosure', material: materials.japan.plaster, parts: rearPlaster })
|
|
409
|
+
addBatch(root, id, { name: 'japan-foundation-stone', material: materials.japan.stone, parts: stone })
|
|
410
|
+
addBatch(root, id, { name: 'japan-black-silver-hardware', material: materials.japan.hardware, parts: hardware })
|
|
411
|
+
addJapaneseSakuraKawaraRoof(root, materials.japan.kawara, {
|
|
412
|
+
name: `${id}-japan`,
|
|
413
|
+
ridgeY: height + 1.7,
|
|
414
|
+
widthScale: (width + 1.18) / 5.48,
|
|
415
|
+
halfDepth: roofHalfDepth,
|
|
416
|
+
capCourses: 9,
|
|
417
|
+
themeRolePrefix: 'elevator-sakura',
|
|
418
|
+
})
|
|
419
|
+
root.userData.constructionContract = 'japanese-sakura-obstacle-kit-elevator-v3'
|
|
420
|
+
root.userData.sidePlasterCoverage = 'continuous-sill-to-gable'
|
|
421
|
+
root.userData.reusedComponents = [
|
|
422
|
+
'portal-kawara-roof',
|
|
423
|
+
'portal-vermilion-frame',
|
|
424
|
+
'castle-wall-infill',
|
|
425
|
+
'bridge-hardware',
|
|
426
|
+
]
|
|
427
|
+
root.position.copy(options.bottom)
|
|
428
|
+
root.quaternion.copy(options.rotation)
|
|
429
|
+
markTheme(root, JAPAN_ELEVATOR_THEME_ID, 'portal-derived-japanese-gatehouse-hoist')
|
|
430
|
+
group.add(root)
|
|
431
|
+
return root
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
function addSpaceSkin(group: THREE.Group, options: ElevatorSkinOptions): THREE.Group {
|
|
435
|
+
const { id, width, height, depth, releaseHeight, materials } = options
|
|
436
|
+
const root = new THREE.Group()
|
|
437
|
+
root.name = `${id}-space-armored-cargo-lift`
|
|
438
|
+
const chassis: THREE.BufferGeometry[] = []
|
|
439
|
+
const panel: THREE.BufferGeometry[] = []
|
|
440
|
+
const rib: THREE.BufferGeometry[] = []
|
|
441
|
+
const trim: THREE.BufferGeometry[] = []
|
|
442
|
+
const lights: THREE.BufferGeometry[] = []
|
|
443
|
+
const postX = width / 2 + 0.27
|
|
444
|
+
const postZ = depth / 2 + 0.24
|
|
445
|
+
const sideCoreX = width / 2 + 0.14
|
|
446
|
+
const wallModuleBottoms = [0.18, 4.12]
|
|
447
|
+
const wallModuleHeight = 3.54
|
|
448
|
+
const wallDepthStart = -depth / 2 - 0.12
|
|
449
|
+
const wallDepthEnd = depth / 2 + 0.12
|
|
450
|
+
const spaceWallMaterials = {
|
|
451
|
+
chassis: materials.space.chassis,
|
|
452
|
+
panel: materials.space.panel,
|
|
453
|
+
rib: materials.space.rib,
|
|
454
|
+
trim: materials.space.trim,
|
|
455
|
+
light: materials.space.light,
|
|
456
|
+
}
|
|
457
|
+
const sideWallRuns: SpaceStationWallPathRun[] = []
|
|
458
|
+
|
|
459
|
+
const canonicalElevatorWallRun = (frames: readonly RawWallFrame[]): SpaceStationWallPathRun => {
|
|
460
|
+
const run = createCanonicalWallRun(frames, { role: 'outer' })
|
|
461
|
+
if (!run) throw new Error('Space Station elevator wall requires a valid canonical boundary')
|
|
462
|
+
return run
|
|
463
|
+
}
|
|
464
|
+
const sideRun = (x: number, baseY: number): SpaceStationWallPathRun =>
|
|
465
|
+
canonicalElevatorWallRun(
|
|
466
|
+
[wallDepthStart, wallDepthEnd].map(z => ({
|
|
467
|
+
base: new THREE.Vector3(x, baseY, z),
|
|
468
|
+
tangent: new THREE.Vector3(0, 0, 1),
|
|
469
|
+
up: new THREE.Vector3(0, 1, 0),
|
|
470
|
+
outward: new THREE.Vector3(-1, 0, 0),
|
|
471
|
+
})),
|
|
472
|
+
)
|
|
473
|
+
const rearRun = (baseY: number): SpaceStationWallPathRun =>
|
|
474
|
+
canonicalElevatorWallRun(
|
|
475
|
+
[-width / 2 - 0.12, width / 2 + 0.12].map(x => ({
|
|
476
|
+
base: new THREE.Vector3(x, baseY, -depth / 2 - 0.14),
|
|
477
|
+
tangent: new THREE.Vector3(1, 0, 0),
|
|
478
|
+
up: new THREE.Vector3(0, 1, 0),
|
|
479
|
+
outward: new THREE.Vector3(0, 0, 1),
|
|
480
|
+
})),
|
|
481
|
+
)
|
|
482
|
+
|
|
483
|
+
for (const x of [-postX, postX]) {
|
|
484
|
+
for (const z of [-postZ, postZ]) {
|
|
485
|
+
chassis.push(
|
|
486
|
+
roundedBox(new THREE.Vector3(0.62, height + 0.82, 0.58), new THREE.Vector3(x, height / 2 + 0.06, z), 0.09),
|
|
487
|
+
)
|
|
488
|
+
rib.push(
|
|
489
|
+
roundedBox(new THREE.Vector3(0.82, 0.32, 0.78), new THREE.Vector3(x, -0.2, z), 0.065),
|
|
490
|
+
roundedBox(new THREE.Vector3(0.76, 0.3, 0.72), new THREE.Vector3(x, height + 0.43, z), 0.065),
|
|
491
|
+
)
|
|
492
|
+
for (const y of [0.38, 4.04, 8.18]) {
|
|
493
|
+
trim.push(roundedBox(new THREE.Vector3(0.68, 0.18, 0.66), new THREE.Vector3(x, y, z), 0.035))
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
for (const baseY of wallModuleBottoms) {
|
|
499
|
+
for (const side of [-1, 1] as const) {
|
|
500
|
+
chassis.push(
|
|
501
|
+
roundedBox(
|
|
502
|
+
new THREE.Vector3(0.2, wallModuleHeight, wallDepthEnd - wallDepthStart),
|
|
503
|
+
new THREE.Vector3(side * sideCoreX, baseY + wallModuleHeight / 2, 0),
|
|
504
|
+
0.035,
|
|
505
|
+
),
|
|
506
|
+
)
|
|
507
|
+
}
|
|
508
|
+
sideWallRuns.push(sideRun(-sideCoreX, baseY), sideRun(sideCoreX, baseY))
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
addSpaceStationWallVisual(root, `${id}-space-side-armored-bays`, sideWallRuns, wallModuleHeight, spaceWallMaterials)
|
|
512
|
+
|
|
513
|
+
const rear = rearEnclosureLayout(height, depth, releaseHeight)
|
|
514
|
+
markRearEnclosure(root, rear, 'large-space-station-armored-bays-with-release-opening')
|
|
515
|
+
chassis.push(
|
|
516
|
+
roundedBox(
|
|
517
|
+
new THREE.Vector3(width + 0.24, rear.lowerHeight, 0.2),
|
|
518
|
+
new THREE.Vector3(0, (rear.panelBottom + rear.openingBottom) / 2, -depth / 2 - 0.14),
|
|
519
|
+
0.035,
|
|
520
|
+
),
|
|
521
|
+
)
|
|
522
|
+
addSpaceStationWallVisual(
|
|
523
|
+
root,
|
|
524
|
+
`${id}-space-rear-lower-module`,
|
|
525
|
+
[rearRun(rear.panelBottom)],
|
|
526
|
+
rear.lowerHeight,
|
|
527
|
+
spaceWallMaterials,
|
|
528
|
+
)
|
|
529
|
+
const upperRearHeight = rear.panelTop - rear.openingTop
|
|
530
|
+
chassis.push(
|
|
531
|
+
roundedBox(
|
|
532
|
+
new THREE.Vector3(width + 0.24, upperRearHeight, 0.2),
|
|
533
|
+
new THREE.Vector3(0, rear.openingTop + upperRearHeight / 2, -depth / 2 - 0.14),
|
|
534
|
+
0.035,
|
|
535
|
+
),
|
|
536
|
+
)
|
|
537
|
+
addSpaceStationWallVisual(
|
|
538
|
+
root,
|
|
539
|
+
`${id}-space-rear-upper-module`,
|
|
540
|
+
[rearRun(rear.openingTop)],
|
|
541
|
+
upperRearHeight,
|
|
542
|
+
spaceWallMaterials,
|
|
543
|
+
)
|
|
544
|
+
|
|
545
|
+
for (const y of [rear.openingBottom - 0.08, rear.openingTop + 0.08]) {
|
|
546
|
+
rib.push(
|
|
547
|
+
roundedBox(new THREE.Vector3(width - 0.08, 0.22, 0.28), new THREE.Vector3(0, y, rear.panelZ + 0.01), 0.055),
|
|
548
|
+
)
|
|
549
|
+
lights.push(roundedBox(new THREE.Vector3(1.2, 0.08, 0.08), new THREE.Vector3(0, y, rear.panelZ + 0.19), 0.035))
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
const frontHeaderBase = height - 0.22
|
|
553
|
+
const headerHeight = 0.82
|
|
554
|
+
chassis.push(
|
|
555
|
+
roundedBox(
|
|
556
|
+
new THREE.Vector3(width + 0.92, headerHeight, 0.24),
|
|
557
|
+
new THREE.Vector3(0, frontHeaderBase + headerHeight / 2, postZ),
|
|
558
|
+
0.055,
|
|
559
|
+
),
|
|
560
|
+
)
|
|
561
|
+
panel.push(
|
|
562
|
+
roundedBox(
|
|
563
|
+
new THREE.Vector3(width - 0.34, headerHeight - 0.26, 0.09),
|
|
564
|
+
new THREE.Vector3(0, frontHeaderBase + headerHeight / 2, postZ + 0.17),
|
|
565
|
+
0.035,
|
|
566
|
+
),
|
|
567
|
+
)
|
|
568
|
+
rib.push(
|
|
569
|
+
roundedBox(
|
|
570
|
+
new THREE.Vector3(0.18, headerHeight - 0.12, 0.16),
|
|
571
|
+
new THREE.Vector3(0, frontHeaderBase + headerHeight / 2, postZ + 0.22),
|
|
572
|
+
0.04,
|
|
573
|
+
),
|
|
574
|
+
)
|
|
575
|
+
lights.push(
|
|
576
|
+
roundedBox(
|
|
577
|
+
new THREE.Vector3(1.04, 0.1, 0.08),
|
|
578
|
+
new THREE.Vector3(0, frontHeaderBase + headerHeight / 2, postZ + 0.28),
|
|
579
|
+
0.035,
|
|
580
|
+
),
|
|
581
|
+
)
|
|
582
|
+
|
|
583
|
+
for (const side of [-1, 1] as const) {
|
|
584
|
+
for (const y of [2.08, 6.02]) {
|
|
585
|
+
lights.push(
|
|
586
|
+
roundedBox(new THREE.Vector3(0.26, 0.045, 0.07), new THREE.Vector3(side * postX, y, postZ + 0.32), 0.014),
|
|
587
|
+
)
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
addBatch(root, id, { name: 'space-load-chassis', material: materials.space.chassis, parts: chassis })
|
|
592
|
+
addBatch(root, id, { name: 'space-header-panel', material: materials.space.panel, parts: panel })
|
|
593
|
+
addBatch(root, id, { name: 'space-wrapped-ribs', material: materials.space.rib, parts: rib })
|
|
594
|
+
addBatch(root, id, { name: 'space-structural-trim', material: materials.space.trim, parts: trim })
|
|
595
|
+
addBatch(root, id, { name: 'space-cyan-channels', material: materials.space.light, parts: lights })
|
|
596
|
+
root.userData.constructionContract = 'space-station-armored-wall-elevator-v3'
|
|
597
|
+
root.userData.reusedComponents = [
|
|
598
|
+
'armored-wall-bay',
|
|
599
|
+
'wrapped-seam-rib',
|
|
600
|
+
'recessed-cyan-channel',
|
|
601
|
+
'portal-chassis-pylon',
|
|
602
|
+
]
|
|
603
|
+
|
|
604
|
+
root.position.copy(options.bottom)
|
|
605
|
+
root.quaternion.copy(options.rotation)
|
|
606
|
+
markTheme(root, SPACE_ELEVATOR_THEME_ID, 'portal-derived-space-cargo-lift')
|
|
607
|
+
group.add(root)
|
|
608
|
+
return root
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
export function addPortalDerivedElevatorSkins(group: THREE.Group, options: ElevatorSkinOptions): THREE.Group[] {
|
|
612
|
+
switch (options.activeThemeId) {
|
|
613
|
+
case EGYPT_ELEVATOR_THEME_ID:
|
|
614
|
+
return [addEgyptSkin(group, options)]
|
|
615
|
+
case JAPAN_ELEVATOR_THEME_ID:
|
|
616
|
+
return [addJapanSkin(group, options)]
|
|
617
|
+
case SPACE_ELEVATOR_THEME_ID:
|
|
618
|
+
return [addSpaceSkin(group, options)]
|
|
619
|
+
default:
|
|
620
|
+
return []
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
function addShelfMesh(
|
|
625
|
+
group: THREE.Group,
|
|
626
|
+
name: string,
|
|
627
|
+
geometry: THREE.BufferGeometry,
|
|
628
|
+
material: THREE.Material,
|
|
629
|
+
position: THREE.Vector3,
|
|
630
|
+
): THREE.Mesh {
|
|
631
|
+
const mesh = new THREE.Mesh(geometry, material)
|
|
632
|
+
mesh.name = name
|
|
633
|
+
mesh.position.copy(position)
|
|
634
|
+
mesh.castShadow = true
|
|
635
|
+
mesh.receiveShadow = true
|
|
636
|
+
mesh.userData.decorative = true
|
|
637
|
+
mesh.userData.visualOnly = true
|
|
638
|
+
group.add(mesh)
|
|
639
|
+
return mesh
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
export function decoratePortalDerivedElevatorShelf(
|
|
643
|
+
shelf: THREE.Mesh,
|
|
644
|
+
id: string,
|
|
645
|
+
width: number,
|
|
646
|
+
depth: number,
|
|
647
|
+
materials: PortalDerivedElevatorMaterials,
|
|
648
|
+
activeThemeId?: string,
|
|
649
|
+
): void {
|
|
650
|
+
if (activeThemeId === EGYPT_ELEVATOR_THEME_ID) {
|
|
651
|
+
const egypt = new THREE.Group()
|
|
652
|
+
egypt.name = `${id}-egypt-approved-platform-cassette`
|
|
653
|
+
const blenderMaterials = liveEgyptBlenderMaterials(materials)
|
|
654
|
+
egypt.userData.applySemanticTheme = () => applyEgyptElevatorBlenderMaterials(egypt, blenderMaterials)
|
|
655
|
+
addEgyptElevatorBlenderPlatform(egypt, id, blenderMaterials, new THREE.Vector3((width - 0.56) / 4.94, 1, depth / 4))
|
|
656
|
+
markTheme(egypt, EGYPT_ELEVATOR_THEME_ID, 'approved-egypt-blender-platform-v5')
|
|
657
|
+
shelf.add(egypt)
|
|
658
|
+
return
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
if (activeThemeId === JAPAN_ELEVATOR_THEME_ID) {
|
|
662
|
+
const japan = new THREE.Group()
|
|
663
|
+
japan.name = `${id}-japan-bridge-cassette`
|
|
664
|
+
addShelfMesh(
|
|
665
|
+
japan,
|
|
666
|
+
`${id}-japan-timber-deck`,
|
|
667
|
+
new VisualBoxGeometry(width - 0.08, 0.08, depth - 0.1, 2, 0.035),
|
|
668
|
+
materials.japan.timber,
|
|
669
|
+
new THREE.Vector3(0, 0.13, 0),
|
|
670
|
+
)
|
|
671
|
+
addShelfMesh(
|
|
672
|
+
japan,
|
|
673
|
+
`${id}-japan-bullnose-fascia`,
|
|
674
|
+
new VisualBoxGeometry(width, 0.25, 0.16, 2, 0.065),
|
|
675
|
+
materials.japan.vermilion,
|
|
676
|
+
new THREE.Vector3(0, 0.18, depth / 2 - 0.02),
|
|
677
|
+
)
|
|
678
|
+
for (const x of [-width / 2 + 0.18, width / 2 - 0.18]) {
|
|
679
|
+
addShelfMesh(
|
|
680
|
+
japan,
|
|
681
|
+
`${id}-japan-corner-collar-${x}`,
|
|
682
|
+
new VisualBoxGeometry(0.34, 0.34, 0.24, 2, 0.045),
|
|
683
|
+
materials.japan.hardware,
|
|
684
|
+
new THREE.Vector3(x, 0.2, depth / 2 - 0.02),
|
|
685
|
+
)
|
|
686
|
+
const finial = addShelfMesh(
|
|
687
|
+
japan,
|
|
688
|
+
`${id}-japan-giboshi-${x}`,
|
|
689
|
+
new VisualSphereGeometry(0.12, 12, 8),
|
|
690
|
+
materials.japan.hardware,
|
|
691
|
+
new THREE.Vector3(x, 0.42, depth / 2 - 0.02),
|
|
692
|
+
)
|
|
693
|
+
finial.scale.y = 1.28
|
|
694
|
+
}
|
|
695
|
+
markTheme(japan, JAPAN_ELEVATOR_THEME_ID, 'japan-bridge-cassette')
|
|
696
|
+
shelf.add(japan)
|
|
697
|
+
return
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
if (activeThemeId === SPACE_ELEVATOR_THEME_ID) {
|
|
701
|
+
const space = new THREE.Group()
|
|
702
|
+
space.name = `${id}-space-cargo-cassette`
|
|
703
|
+
addShelfMesh(
|
|
704
|
+
space,
|
|
705
|
+
`${id}-space-cargo-deck`,
|
|
706
|
+
new VisualBoxGeometry(width - 0.08, 0.09, depth - 0.1, 2, 0.04),
|
|
707
|
+
materials.space.trim,
|
|
708
|
+
new THREE.Vector3(0, 0.13, 0),
|
|
709
|
+
)
|
|
710
|
+
addShelfMesh(
|
|
711
|
+
space,
|
|
712
|
+
`${id}-space-armored-fascia`,
|
|
713
|
+
new VisualBoxGeometry(width, 0.27, 0.16, 2, 0.055),
|
|
714
|
+
materials.space.chassis,
|
|
715
|
+
new THREE.Vector3(0, 0.18, depth / 2 - 0.02),
|
|
716
|
+
)
|
|
717
|
+
for (const x of [-width * 0.31, 0, width * 0.31]) {
|
|
718
|
+
addShelfMesh(
|
|
719
|
+
space,
|
|
720
|
+
`${id}-space-cyan-marker-${x}`,
|
|
721
|
+
new VisualBoxGeometry(0.34, 0.1, 0.06, 2, 0.03),
|
|
722
|
+
materials.space.light,
|
|
723
|
+
new THREE.Vector3(x, 0.19, depth / 2 + 0.08),
|
|
724
|
+
)
|
|
725
|
+
}
|
|
726
|
+
markTheme(space, SPACE_ELEVATOR_THEME_ID, 'space-cargo-cassette')
|
|
727
|
+
shelf.add(space)
|
|
728
|
+
}
|
|
729
|
+
}
|