# Machine authoring guide Mechanica’s machine catalogue is data-driven. New systems should be added by registering metadata, parts, animation logic, guided tours, and optional GLB assets without changing the core catalogue or viewer code. ## Machine package anatomy A complete machine entry contains: ```text src/modules/machines// ├── index.ts ├── metadata.ts ├── parts.ts ├── facts.ts ├── tour.ts ├── animation.ts └── proceduralModel.tsx or asset.glb reference ``` The exact file split can vary, but the exported registry entry should remain stable. ## Registry entry shape Recommended registry shape: ```ts type MachineCategory = 'engines' | 'gearboxes' | 'pumps' | 'mechanisms' | 'structural'; type Difficulty = 'beginner' | 'intermediate' | 'advanced'; type MachineRegistryEntry = { slug: string; title: string; category: MachineCategory; difficulty: Difficulty; complexity: number; createdAt: string; keywords: string[]; thumbnail: string; description: string; facts: EngineeringFact[]; parts: MachinePartDefinition[]; related: string[]; scene: MachineSceneDefinition; animation: MachineAnimationDefinition; tour: GuidedTourStep[]; }; ``` Required constraints: - `slug` is lowercase kebab-case and stable forever. - `related` contains only existing slugs. - `parts[].id` is unique within the machine. - Every guided tour highlight references a valid part ID. - Every animation channel references a valid part ID or object key. - `createdAt` uses ISO date format. ## Part metadata Each selectable part needs enough information for list controls, tooltips, annotations, and the detail drawer. ```ts type MachinePartDefinition = { id: string; name: string; description: string; materialHint?: string; defaultVisible: boolean; defaultOpacity: number; color?: string; tags: string[]; specs?: Record; annotation?: { label: string; localPosition: [number, number, number]; }; explodedOffset?: [number, number, number]; }; ``` Authoring rules: - Keep `defaultOpacity` between `0` and `1`. - Use human-readable `name` values; do not expose internal mesh names in UI. - Put engineering claims in `specs` or `facts`, not only in descriptions. - Use `explodedOffset` directions that separate components without hiding critical relationships. ## Engineering facts Facts should be concise and comparable across machines. ```ts type EngineeringFact = { label: string; value: string; unit?: string; note?: string; }; ``` Common facts: - Typical RPM range - Efficiency range - Invention date or era - Common applications - Primary materials - Pressure ratio, torque ratio, or reduction ratio - Lubrication/cooling requirement ## Scene source A machine can be rendered from either a procedural React Three Fiber component or a GLB asset. ```ts type MachineSceneDefinition = | { kind: 'procedural'; component: React.LazyExoticComponent>; } | { kind: 'asset'; url: string; scale?: number; rotation?: [number, number, number]; position?: [number, number, number]; partMeshMap: Record; }; ``` For asset-based machines, `partMeshMap` maps part IDs to mesh node names inside the GLB. Mesh names should be stable and descriptive in the DCC tool before export. ## Animation module interface Every machine animation module should implement the same control interface so the player does not contain machine-specific logic. ```ts type MachineAnimationContext = { elapsedSeconds: number; deltaSeconds: number; cyclePhase: number; rpm: number; timeScale: number; playback: 'playing' | 'paused' | 'stopped'; partObjects: Map; }; type MachineAnimationDefinition = { cycleDurationSeconds: number; minRpm: number; maxRpm: number; defaultRpm: number; stepCount: number; labels: string[]; update(context: MachineAnimationContext): void; reset?(partObjects: Map): void; }; ``` Rules: - Animation loops must be continuous at phase `0` and phase `1`. - Use radians for rotation values. - Use physically plausible relationships: piston/crank/cam ratios, gear ratios, valve timing, and phase offsets. - Do not update React state every frame. - Keep allocations out of per-frame update paths. ## Guided tour steps ```ts type GuidedTourStep = { id: string; title: string; body: string; durationSeconds: number; cameraPreset?: 'front' | 'back' | 'left' | 'right' | 'top' | 'isometric'; cameraPosition?: [number, number, number]; target?: [number, number, number]; highlightPartIds: string[]; animationState?: { playback?: 'playing' | 'paused'; rpm?: number; timeScale?: number; }; }; ``` A strong tour: 1. Starts with the complete system. 2. Introduces energy or motion input. 3. Walks through key conversion stages. 4. Highlights timing or control elements. 5. Ends with practical applications and limitations. ## Adding a new machine 1. Choose a stable slug. 2. Create the machine folder. 3. Add metadata, facts, and part definitions. 4. Build the procedural scene or export and optimize a GLB. 5. Implement the animation module. 6. Add guided tour steps. 7. Add a thumbnail and social image if needed. 8. Register the machine in the central registry. 9. Add related machine slugs. 10. Run: ```bash npm run typecheck npm run test npm run build ``` 11. Manually inspect: - catalogue card - search/filter results - part list - viewer interactions - animation loop - exploded view - share URL - mobile layout ## Quality checklist for new machines - The title and one-paragraph description are clear to a beginner. - Difficulty matches the amount of prerequisite knowledge. - Every selectable mesh has metadata. - Hover and selection outlines are easy to see against the material. - Exploded view reveals internal layout without scattering parts excessively. - Wireframe and cross-section modes remain intelligible. - Animation can run at minimum and maximum RPM without visual discontinuities. - The guided tour can be completed with reduced motion enabled. - Related machines create a meaningful learning path. - Asset size stays within the budgets in the asset pipeline document.