feat: improve gear-case 3D geometry with realistic housing features

Added external features to make the gear case look like a real gearbox housing:
- Mating flange with 6 bolt bosses and bolt holes
- Two shaft bearing housings (cylindrical protrusions)
- Vertical structural ribbing
- Output shaft housing boss
- Drain plug boss

Also includes camera position fix (150,150,150) and grid size (200) for mm-scale parts.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-21 21:33:15 +00:00
parent 054b78daea
commit efffbe2c0a
3 changed files with 47 additions and 8 deletions

View File

@@ -43,9 +43,39 @@
{ "type": "text", "content": "IB5", "offsetY": 5, "fill": "#333", "fontSize": 12 }
],
"geometry3d": [
{ "type": "box", "width": 90, "height": 80, "depth": 120, "fill": "#555" },
{ "type": "cylinder", "r": 30, "height": 82, "rotateX": 90, "offsetZ": -20, "subtract": true },
{ "type": "cylinder", "r": 25, "height": 82, "rotateX": 90, "offsetZ": 20, "subtract": true },
{ "type": "box", "width": 70, "height": 60, "depth": 100, "offsetY": 5, "subtract": true }
{ "type": "box", "width": 100, "height": 85, "depth": 130, "fill": "#606070" },
{ "type": "box", "width": 80, "height": 65, "depth": 110, "offsetY": 5, "subtract": true },
{ "type": "box", "width": 120, "height": 95, "depth": 12, "offsetZ": -65, "fill": "#707080" },
{ "type": "cylinder", "r": 10, "height": 20, "offsetX": -50, "offsetY": -38, "offsetZ": -65, "fill": "#808090" },
{ "type": "cylinder", "r": 10, "height": 20, "offsetX": 50, "offsetY": -38, "offsetZ": -65, "fill": "#808090" },
{ "type": "cylinder", "r": 10, "height": 20, "offsetX": -50, "offsetY": 38, "offsetZ": -65, "fill": "#808090" },
{ "type": "cylinder", "r": 10, "height": 20, "offsetX": 50, "offsetY": 38, "offsetZ": -65, "fill": "#808090" },
{ "type": "cylinder", "r": 10, "height": 20, "offsetX": 0, "offsetY": -42, "offsetZ": -65, "fill": "#808090" },
{ "type": "cylinder", "r": 10, "height": 20, "offsetX": 0, "offsetY": 42, "offsetZ": -65, "fill": "#808090" },
{ "type": "cylinder", "r": 4, "height": 25, "offsetX": -50, "offsetY": -38, "offsetZ": -65, "subtract": true },
{ "type": "cylinder", "r": 4, "height": 25, "offsetX": 50, "offsetY": -38, "offsetZ": -65, "subtract": true },
{ "type": "cylinder", "r": 4, "height": 25, "offsetX": -50, "offsetY": 38, "offsetZ": -65, "subtract": true },
{ "type": "cylinder", "r": 4, "height": 25, "offsetX": 50, "offsetY": 38, "offsetZ": -65, "subtract": true },
{ "type": "cylinder", "r": 4, "height": 25, "offsetX": 0, "offsetY": -42, "offsetZ": -65, "subtract": true },
{ "type": "cylinder", "r": 4, "height": 25, "offsetX": 0, "offsetY": 42, "offsetZ": -65, "subtract": true },
{ "type": "box", "width": 8, "height": 70, "depth": 110, "offsetX": -42, "fill": "#555560" },
{ "type": "box", "width": 8, "height": 70, "depth": 110, "offsetX": 42, "fill": "#555560" },
{ "type": "box", "width": 8, "height": 70, "depth": 110, "offsetX": 0, "fill": "#555560" },
{ "type": "cylinder", "r": 35, "height": 20, "rotateX": 90, "offsetZ": 55, "fill": "#707080" },
{ "type": "cylinder", "r": 25, "height": 25, "rotateX": 90, "offsetZ": 55, "subtract": true },
{ "type": "cylinder", "r": 38, "height": 15, "rotateX": 90, "offsetZ": -20, "fill": "#656575" },
{ "type": "cylinder", "r": 30, "height": 90, "rotateX": 90, "offsetZ": -20, "subtract": true },
{ "type": "cylinder", "r": 32, "height": 15, "rotateX": 90, "offsetZ": 20, "fill": "#656575" },
{ "type": "cylinder", "r": 25, "height": 90, "rotateX": 90, "offsetZ": 20, "subtract": true },
{ "type": "box", "width": 30, "height": 20, "depth": 25, "offsetX": -55, "offsetY": -30, "fill": "#606070" },
{ "type": "cylinder", "r": 8, "height": 30, "offsetX": -55, "offsetY": -30, "subtract": true }
]
}

View File

@@ -19,13 +19,22 @@ interface PartMeshProps {
function PartMesh({ part, materials }: PartMeshProps) {
const geometry = useMemo(() => {
console.log('PartMesh rendering:', part.id, 'geometry3d:', part.geometry3d)
if (part.geometry3d && part.geometry3d.length > 0) {
// Convert geometry3d to Three.js BufferGeometry
return geometryToThree(part.geometry3d)
try {
const geom = geometryToThree(part.geometry3d)
console.log('Generated geometry:', geom, 'positions:', geom.attributes.position?.count)
return geom
} catch (err) {
console.error('Failed to convert geometry3d:', err)
return new THREE.BoxGeometry(2, 2, 2)
}
}
// Fallback to a simple box if no geometry3d present
console.log('No geometry3d, using fallback box')
return new THREE.BoxGeometry(2, 2, 2)
}, [part.geometry3d])
}, [part.geometry3d, part.id])
const materialColor = useMemo(() => {
// Try to get color from first geometry3d fill, or material reference, or part material

View File

@@ -11,7 +11,7 @@ export default function ThreeCanvas({ children }: ThreeCanvasProps) {
return (
<Canvas
style={{ width: '100%', height: '100%' }}
camera={{ position: [5, 5, 5], fov: 50 }}
camera={{ position: [150, 150, 150], fov: 50 }}
>
<color attach="background" args={['#1a1a2e']} />
@@ -21,7 +21,7 @@ export default function ThreeCanvas({ children }: ThreeCanvasProps) {
<directionalLight position={[-5, 5, -5]} intensity={0.3} color="#00d4ff" />
{/* Ground plane grid */}
<gridHelper args={[20, 20, '#00d4ff', '#2a2a4e']} />
<gridHelper args={[200, 20, '#00d4ff', '#2a2a4e']} />
{/* Camera controls */}
<OrbitControls makeDefault />