feat(3d-viewer): upgrade visuals with significant plant scaling and improved controls based on user feedback
This commit is contained in:
parent
9da01f6338
commit
1ebe7b4d34
1 changed files with 40 additions and 19 deletions
|
|
@ -65,7 +65,7 @@ function PlantInstances({ positions, onPlantClick }: { positions: any[], onPlant
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<group>
|
<group>
|
||||||
{/* Active Plants */}
|
{/* Active Plants - Render as Cylinders (Stalks/Canopy) for visibility */}
|
||||||
{plants.length > 0 && (
|
{plants.length > 0 && (
|
||||||
<Instances
|
<Instances
|
||||||
range={plants.length}
|
range={plants.length}
|
||||||
|
|
@ -77,12 +77,14 @@ function PlantInstances({ positions, onPlantClick }: { positions: any[], onPlant
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<sphereGeometry args={[0.15, 16, 16]} />
|
{/* Make plants much taller and visible: Cylinder radius 0.2, height 0.8 */}
|
||||||
|
<cylinderGeometry args={[0.2, 0.1, 0.8, 8]} />
|
||||||
<meshStandardMaterial />
|
<meshStandardMaterial />
|
||||||
{plants.map((pos, i) => (
|
{plants.map((pos, i) => (
|
||||||
<Instance
|
<Instance
|
||||||
key={pos.id || i}
|
key={pos.id || i}
|
||||||
position={[pos.x, pos.y, pos.z]}
|
// Lift y by 0.4 (half height) so it sits ON the shelf
|
||||||
|
position={[pos.x, pos.y + 0.4, pos.z]}
|
||||||
color={pos.plant?.stage === 'FLOWER' ? COLORS.FLOWER :
|
color={pos.plant?.stage === 'FLOWER' ? COLORS.FLOWER :
|
||||||
pos.plant?.stage === 'DRYING' ? COLORS.DRY :
|
pos.plant?.stage === 'DRYING' ? COLORS.DRY :
|
||||||
pos.plant?.stage === 'CURE' ? COLORS.CURE : COLORS.VEG}
|
pos.plant?.stage === 'CURE' ? COLORS.CURE : COLORS.VEG}
|
||||||
|
|
@ -91,11 +93,11 @@ function PlantInstances({ positions, onPlantClick }: { positions: any[], onPlant
|
||||||
</Instances>
|
</Instances>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Empty Slots */}
|
{/* Empty Slots - Keep subtle but visible */}
|
||||||
{emptySlots.length > 0 && (
|
{emptySlots.length > 0 && (
|
||||||
<Instances range={emptySlots.length}>
|
<Instances range={emptySlots.length}>
|
||||||
<sphereGeometry args={[0.05, 8, 8]} />
|
<cylinderGeometry args={[0.05, 0.05, 0.1, 8]} />
|
||||||
<meshStandardMaterial color={COLORS.EMPTY_SLOT} opacity={0.5} transparent />
|
<meshStandardMaterial color={COLORS.EMPTY_SLOT} opacity={0.3} transparent />
|
||||||
{emptySlots.map((pos, i) => (
|
{emptySlots.map((pos, i) => (
|
||||||
<Instance
|
<Instance
|
||||||
key={pos.id || i}
|
key={pos.id || i}
|
||||||
|
|
@ -119,22 +121,30 @@ function FacilityScene({ data, onSelectPlant }: { data: Floor3DData, onSelectPla
|
||||||
<Text
|
<Text
|
||||||
position={[room.width / 2, 0.1, room.height / 2]}
|
position={[room.width / 2, 0.1, room.height / 2]}
|
||||||
rotation={[-Math.PI / 2, 0, 0]}
|
rotation={[-Math.PI / 2, 0, 0]}
|
||||||
fontSize={0.5}
|
fontSize={1.5} // Larger text
|
||||||
color="white"
|
color="white"
|
||||||
fillOpacity={0.5}
|
fillOpacity={0.8}
|
||||||
|
anchorX="center"
|
||||||
|
anchorY="middle"
|
||||||
>
|
>
|
||||||
{room.name}
|
{room.name}
|
||||||
</Text>
|
</Text>
|
||||||
|
|
||||||
{/* Floor Plane */}
|
{/* Floor Plane - Darker for contrast */}
|
||||||
<mesh
|
<mesh
|
||||||
rotation={[-Math.PI / 2, 0, 0]}
|
rotation={[-Math.PI / 2, 0, 0]}
|
||||||
position={[room.width / 2, 0, room.height / 2]}
|
position={[room.width / 2, 0, room.height / 2]}
|
||||||
>
|
>
|
||||||
<planeGeometry args={[room.width, room.height]} />
|
<planeGeometry args={[room.width, room.height]} />
|
||||||
<meshStandardMaterial color={COLORS.ROOM_FLOOR} />
|
<meshStandardMaterial color={COLORS.ROOM_FLOOR} metalness={0.2} roughness={0.8} />
|
||||||
</mesh>
|
</mesh>
|
||||||
|
|
||||||
|
{/* Room Border/Walls */}
|
||||||
|
<lineSegments position={[room.width / 2, 0.1, room.height / 2]} rotation={[-Math.PI / 2, 0, 0]}>
|
||||||
|
<edgesGeometry args={[new THREE.PlaneGeometry(room.width, room.height)]} />
|
||||||
|
<lineBasicMaterial color="#4b5563" linewidth={2} />
|
||||||
|
</lineSegments>
|
||||||
|
|
||||||
{/* Render sections and positions */}
|
{/* Render sections and positions */}
|
||||||
{room.sections.map(section => {
|
{room.sections.map(section => {
|
||||||
// Calculate positions for this section
|
// Calculate positions for this section
|
||||||
|
|
@ -151,10 +161,10 @@ function FacilityScene({ data, onSelectPlant }: { data: Floor3DData, onSelectPla
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<group key={section.id}>
|
<group key={section.id}>
|
||||||
{/* Section Label */}
|
{/* Section Label - Lifted higher */}
|
||||||
<Text
|
<Text
|
||||||
position={[section.posX, 2.5, section.posY]}
|
position={[section.posX, 3, section.posY]}
|
||||||
fontSize={0.3}
|
fontSize={0.5}
|
||||||
color="#9ca3af"
|
color="#9ca3af"
|
||||||
>
|
>
|
||||||
{section.code}
|
{section.code}
|
||||||
|
|
@ -170,16 +180,26 @@ function FacilityScene({ data, onSelectPlant }: { data: Floor3DData, onSelectPla
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<ambientLight intensity={0.7} />
|
<ambientLight intensity={1.5} />
|
||||||
<pointLight position={[10, 10, 10]} intensity={1} />
|
<pointLight position={[20, 30, 20]} intensity={2} />
|
||||||
<directionalLight position={[-10, 20, -10]} intensity={0.5} />
|
<directionalLight position={[-10, 50, -10]} intensity={1} castShadow />
|
||||||
|
|
||||||
<group position={[-data.floor.width / 2, 0, -data.floor.height / 2]}>
|
<group position={[-data.floor.width / 2, 0, -data.floor.height / 2]}>
|
||||||
{roomMeshes}
|
{roomMeshes}
|
||||||
</group>
|
</group>
|
||||||
|
|
||||||
<OrbitControls makeDefault minPolarAngle={0} maxPolarAngle={Math.PI / 2.2} />
|
{/* Improved Controls: Allow panning, damping for smoothness */}
|
||||||
<gridHelper args={[100, 100, 0x444444, 0x222222]} />
|
<OrbitControls
|
||||||
|
makeDefault
|
||||||
|
enablePan={true}
|
||||||
|
enableZoom={true}
|
||||||
|
enableRotate={true}
|
||||||
|
minDistance={2}
|
||||||
|
maxDistance={100}
|
||||||
|
dampingFactor={0.05}
|
||||||
|
/>
|
||||||
|
<gridHelper args={[200, 100, 0x444444, 0x111111]} position={[0, -0.1, 0]} />
|
||||||
|
<axesHelper args={[5]} />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -227,6 +247,7 @@ export default function Facility3DViewerPage() {
|
||||||
<p className="text-xs text-gray-400">
|
<p className="text-xs text-gray-400">
|
||||||
{floorData ? `${floorData.floor.name} • ${floorData.stats.occupiedPositions} Plants` : 'Loading...'}
|
{floorData ? `${floorData.floor.name} • ${floorData.stats.occupiedPositions} Plants` : 'Loading...'}
|
||||||
</p>
|
</p>
|
||||||
|
<p className="text-[10px] text-gray-500">Controls: Left Click=Rotate, Right Click=Pan, Scroll=Zoom</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -287,7 +308,7 @@ export default function Facility3DViewerPage() {
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<Canvas camera={{ position: [10, 15, 10], fov: 50 }}>
|
<Canvas camera={{ position: [20, 30, 20], fov: 60 }}>
|
||||||
<ErrorBoundary>
|
<ErrorBoundary>
|
||||||
<Suspense fallback={<Html center>Loading 3D Scene...</Html>}>
|
<Suspense fallback={<Html center>Loading 3D Scene...</Html>}>
|
||||||
{floorData && (
|
{floorData && (
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue