Before the doublework change
This commit is contained in:
@@ -0,0 +1,78 @@
|
||||
import type { FC } from 'react'
|
||||
import * as THREE from 'three'
|
||||
import { SpinningModel } from './model3d'
|
||||
|
||||
// The Barricelli Luggage: Nils Aall Barricelli's patented suitcase-commode. Two wooden
|
||||
// beams that taper upward carry three suitcases slung between them as drawers, so the
|
||||
// whole thing reads as a rudimentary chest of drawers built from luggage.
|
||||
|
||||
const WOOD = new THREE.MeshStandardMaterial({ color: 0x6b4a2f, roughness: 0.85, metalness: 0.04, flatShading: true })
|
||||
const BRASS = new THREE.MeshStandardMaterial({ color: 0xc9a24a, roughness: 0.45, metalness: 0.6, flatShading: true })
|
||||
const STRAP = new THREE.MeshStandardMaterial({ color: 0x2f2118, roughness: 0.8, flatShading: true })
|
||||
const LEATHERS = [0x8a5a34, 0x6f4632, 0x9a6b3f].map(color => new THREE.MeshStandardMaterial({ color, roughness: 0.8, metalness: 0.05, flatShading: true }))
|
||||
|
||||
// One suitcase drawer: leather body, a lid seam, two wrap-around straps, brass latches,
|
||||
// corner caps, and a front pull (it is a drawer now, so the handle faces the viewer).
|
||||
function buildSuitcase(width: number, height: number, depth: number, leather: THREE.Material) {
|
||||
const suitcase = new THREE.Group()
|
||||
const body = new THREE.Mesh(new THREE.BoxGeometry(width, height, depth), leather); suitcase.add(body)
|
||||
const seam = new THREE.Mesh(new THREE.BoxGeometry(width + 0.02, 0.04, depth + 0.02), STRAP); suitcase.add(seam)
|
||||
for (const sign of [-1, 1]) {
|
||||
const strap = new THREE.Mesh(new THREE.BoxGeometry(0.07, height + 0.02, depth + 0.04), STRAP)
|
||||
strap.position.x = sign * width * 0.3; suitcase.add(strap)
|
||||
const latch = new THREE.Mesh(new THREE.BoxGeometry(0.1, 0.07, 0.03), BRASS)
|
||||
latch.position.set(sign * width * 0.3, 0.02, depth / 2 + 0.01); suitcase.add(latch)
|
||||
}
|
||||
for (const sx of [-1, 1]) for (const sy of [-1, 1]) { // brass corner caps on the front face
|
||||
const cap = new THREE.Mesh(new THREE.BoxGeometry(0.07, 0.07, 0.06), BRASS)
|
||||
cap.position.set(sx * (width / 2 - 0.035), sy * (height / 2 - 0.035), depth / 2)
|
||||
suitcase.add(cap)
|
||||
}
|
||||
const pull = new THREE.Mesh(new THREE.BoxGeometry(0.26, 0.05, 0.05), BRASS)
|
||||
pull.position.set(0, -height * 0.16, depth / 2 + 0.05); suitcase.add(pull)
|
||||
for (const sign of [-1, 1]) { const stem = new THREE.Mesh(new THREE.BoxGeometry(0.03, 0.03, 0.05), BRASS); stem.position.set(sign * 0.1, -height * 0.16, depth / 2 + 0.02); suitcase.add(stem) }
|
||||
return suitcase
|
||||
}
|
||||
|
||||
function buildBarricelliLuggage() {
|
||||
const frame = new THREE.Group()
|
||||
const postHeight = 2.1
|
||||
const suitcase = { w: 1.05, h: 0.44, d: 0.62 }
|
||||
const levels = [0.62, 0, -0.62] // three stacked drawers
|
||||
|
||||
for (const sign of [-1, 1]) { // two beams, each tapering upward, leaning slightly inward
|
||||
const post = new THREE.Mesh(new THREE.CylinderGeometry(0.085, 0.12, postHeight, 4), WOOD)
|
||||
post.rotation.y = Math.PI / 4 // square the prism to face front
|
||||
post.rotation.z = -sign * 0.05 // splay the feet, converge the tops
|
||||
post.position.x = sign * 0.66
|
||||
frame.add(post)
|
||||
const foot = new THREE.Mesh(new THREE.BoxGeometry(0.24, 0.09, 0.32), WOOD)
|
||||
foot.position.set(sign * 0.7, -postHeight / 2 - 0.02, 0); frame.add(foot)
|
||||
}
|
||||
// Top cap and a low stretcher tie the beams into a stand.
|
||||
const cap = new THREE.Mesh(new THREE.BoxGeometry(1.44, 0.11, 0.5), WOOD); cap.position.y = postHeight / 2 + 0.02; frame.add(cap)
|
||||
const stretcher = new THREE.Mesh(new THREE.BoxGeometry(1.4, 0.08, 0.18), WOOD); stretcher.position.y = -postHeight / 2 + 0.14; frame.add(stretcher)
|
||||
|
||||
levels.forEach((y, i) => {
|
||||
const drawer = buildSuitcase(suitcase.w, suitcase.h, suitcase.d, LEATHERS[i % LEATHERS.length])
|
||||
drawer.position.y = y; frame.add(drawer)
|
||||
for (const sign of [-1, 1]) { // small wooden brackets mounting each case to the beams
|
||||
const bracket = new THREE.Mesh(new THREE.BoxGeometry(0.16, 0.08, 0.3), WOOD)
|
||||
bracket.position.set(sign * (suitcase.w / 2 + 0.05), y, 0); frame.add(bracket)
|
||||
}
|
||||
})
|
||||
|
||||
frame.traverse(obj => { if (obj instanceof THREE.Mesh) obj.castShadow = true })
|
||||
return frame
|
||||
}
|
||||
|
||||
export const BarricelliLuggageMerit: FC<{ label: string; onComplete: () => void }> = ({ label, onComplete }) => (
|
||||
<div className="cutscene-card merit-card">
|
||||
<div className="title-card-inner">
|
||||
<small className="merit-eyebrow">◆ MERIT AWARDED ◆</small>
|
||||
<SpinningModel build={buildBarricelliLuggage} className="merit-stage" cameraPos={[0.9, 0.35, 4.9]} />
|
||||
<h1>{label}</h1>
|
||||
<button className="cutscene-begin" onClick={event => { event.stopPropagation(); onComplete() }}>Accept ▸</button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
Reference in New Issue
Block a user