Skip to content

Commit

Permalink
add 3d gallery
Browse files Browse the repository at this point in the history
  • Loading branch information
paoloose committed Nov 18, 2024
1 parent 81e28b7 commit 75d2977
Show file tree
Hide file tree
Showing 11 changed files with 175 additions and 21 deletions.
Binary file added app/public/assets/3d/gallery.glb
Binary file not shown.
2 changes: 2 additions & 0 deletions app/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,5 @@ export const smallIconProps: IconProps = {
size: '14px',
stroke: 1.5,
};

export const noImageSrc = 'https://icons.veryicon.com/png/o/file-type/linear-icon-2/upload-45.png';
14 changes: 4 additions & 10 deletions app/src/pages/Editor/components/GalleryCanvas.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ export const GalleryCanvas = ({ gallery }: { gallery: string }) => {
fill="white"
/>
</Layer>
{ /* First layer of walls and doors */}
{ /* First layer of walls and doors structure */}
<Layer>
{
blocks.map((item, index) => {
Expand All @@ -168,17 +168,11 @@ export const GalleryCanvas = ({ gallery }: { gallery: string }) => {
{
blocks.map((item, index) => {
if (isWallBlock(item)) {
if (!item.props.res) {
return null;
}
return (
<PictureSlot key={index} block={item as PictureSlotProps} />
);
return null;
return <PictureSlot key={index} block={item as PictureSlotProps} />;
}
if (isModel3DBlock(item)) {
return (
<Model3DBlock key={index} block={item} />
);
return <Model3DBlock key={index} block={item} />;
}
return null;
})
Expand Down
3 changes: 2 additions & 1 deletion app/src/pages/Editor/components/blocks/Model3DBlock.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@ import { setCursor } from '@/utils';
import { UNIT } from '../constants';
import { useEditorStore } from '@/stores/editorAction';
import { Model3DBlockProps } from '@/types';
import { noImageSrc } from '@/constants';

export const Model3DBlock = ({ block }: { block: Model3DBlockProps }) => {
const { pos, props } = block;

const [image] = useImage(props.res);
const [image] = useImage(props.res ?? noImageSrc);
const [hovering, setHovering] = useState(false);
const draggingElem = useEditorStore((state) => state.draggingFile);

Expand Down
1 change: 1 addition & 0 deletions app/src/pages/Editor/components/blocks/PictureSlot.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { useEditorStore } from '@/stores/editorAction';
import { DIR_TOP, DIR_RIGHT, DIR_BOTTOM, DIR_LEFT, UNIT, WALL_THICKNESS, PICTURE_SLOT_UNIT } from '../constants';
import { useMetagalleryStore } from '@/providers/MetagalleryProvider';
import { PictureSlotProps } from '@/types';
import { noImageSrc } from '@/constants';

const HALF_THICKNESS = WALL_THICKNESS / 2;
const WALL_PADDING = 0.1;
Expand Down
79 changes: 79 additions & 0 deletions app/src/pages/Gallery3D/components/gallery/FPV.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import { useSphere } from '@react-three/cannon';
import { PointerLockControls } from '@react-three/drei';
import { useFrame, useThree } from '@react-three/fiber';
import { useState, useEffect, useRef } from 'react';
import * as THREE from 'three';

export const usePlayerControls = () => {
const keys = { KeyW: 'forward', KeyS: 'backward', KeyA: 'left', KeyD: 'right', Space: 'jump' };
const moveFieldByKey = (key: any) => keys[key as keyof typeof keys];

const [movement, setMovement] = useState({
forward: false, backward: false, left: false, right: false, jump: false,
});

useEffect(() => {
const handleKeyDown = (e) => setMovement((m) => ({ ...m, [moveFieldByKey(e.code)]: true }));
const handleKeyUp = (e) => setMovement((m) => ({ ...m, [moveFieldByKey(e.code)]: false }));

document.addEventListener('keydown', handleKeyDown);
document.addEventListener('keyup', handleKeyUp);

return () => {
document.removeEventListener('keydown', handleKeyDown);
document.removeEventListener('keyup', handleKeyUp);
};
}, []);

return movement;
};

export const FPV = (props: any) => {
const direction = new THREE.Vector3();
const frontVector = new THREE.Vector3();
const sideVector = new THREE.Vector3();
const speed = new THREE.Vector3();
const SPEED = 5;

const { camera, gl } = useThree();

const [ref, api] = useSphere((index) => ({
mass: 1,
type: 'Dynamic',
position: [0, 10, 0],
...props,
}));

const { forward, backward, left, right, jump } = usePlayerControls();
const velocity = useRef([0, 0, 0]);
useEffect(() => api.velocity.subscribe((v) => (velocity.current = v)), []);

useFrame((state) => {
ref.current!.getWorldPosition(camera.position);
frontVector.set(0, 0, Number(backward) - Number(forward));
sideVector.set(Number(left) - Number(right), 0, 0);
direction
.subVectors(frontVector, sideVector)
.normalize()
.multiplyScalar(SPEED)
.applyEuler(camera.rotation);
speed.fromArray(velocity.current);

api.velocity.set(direction.x, velocity.current[1], direction.z);
if (jump && Math.abs(Math.round(velocity.current[1] * 100) / 100) < 0.05) {
api.velocity.set(velocity.current[0], 5, velocity.current[2]);
}
});

return (
<>
<PointerLockControls args={[camera, gl.domElement]} />
<group>
<mesh castShadow position={props.position} ref={ref}>
<sphereGeometry args={props.args} />
<meshStandardMaterial color="#FFFF00" />
</mesh>
</group>
</>
);
};
12 changes: 12 additions & 0 deletions app/src/pages/Gallery3D/components/gallery/Floor.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { usePlane } from '@react-three/cannon';

export const Floor = (props) => {
const [ref] = usePlane((index) => ({ type: 'Static', mass: 0, ...props }));

return (
<mesh receiveShadow rotation={props.rotation} ref={ref}>
<planeGeometry args={[1000, 1000]} />
<meshStandardMaterial color={props.color} />
</mesh>
);
};
21 changes: 21 additions & 0 deletions app/src/pages/Gallery3D/components/gallery/Ground.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { usePlane } from '@react-three/cannon';
import { extend } from '@react-three/fiber';
import { MeshStandardMaterial, PlaneGeometry } from 'three';

extend({ MeshStandardMaterial, PlaneGeometry });

export function Ground() {
const [ref] = usePlane(() => ({
rotation: [-Math.PI / 2, 0, 0],
position: [0, -0.01, 0],
}));

return (
<mesh
ref={ref as any}
>
<planeGeometry attach="geometry" args={[100, 100]} />
<meshStandardMaterial />
</mesh>
);
}
18 changes: 18 additions & 0 deletions app/src/pages/Gallery3D/components/gallery/Scene.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { Sparkles } from '@react-three/drei';
import { useLoader } from '@react-three/fiber';
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';

export const Scene = () => {
const gltf = useLoader(GLTFLoader, '/assets/3d/gallery.glb');

return (
<group>
<Sparkles count={200} scale={[20, 20, 10]} size={3} speed={2} />
<primitive
object={gltf.scene}
position={[0, 0, 0]}
children-0-castShadow
/>
</group>
);
};
30 changes: 30 additions & 0 deletions app/src/pages/Gallery3D/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { Sky, SpotLightShadow } from '@react-three/drei';
import { Canvas } from '@react-three/fiber';
import { Physics } from '@react-three/cannon';
import { useApi } from '@/hooks/useApi';
import { UserContentFileElement } from '@/types';
import { Ground } from './components/gallery/Ground';
import { FPV } from './components/gallery/FPV';
import { Scene } from './components/gallery/Scene';

export const Gallery3D = ({ gallery }: { gallery: string }) => {
const { data } = useApi<Array<UserContentFileElement>>(`gallery/${gallery}`);

console.log('🐢🐢🐢🐢', data);

return (
<Canvas>
<ambientLight intensity={2.5} />
<spotLight
penumbra={0.5}
position={[10, 10, 5]}
castShadow />
<Physics>
<FPV controls position={[0, 1, 0]} args={[2]} color="yellow" />
<Ground />
<Scene />
</Physics>
<Sky />
</Canvas>
);
};
16 changes: 6 additions & 10 deletions app/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,17 +33,17 @@ export type PictureSlotProps = {
props: {
size: number,
dir: 0 | 1 | 2 | 3,
res: string,
res?: string,
},
};

export type Model3DBlockProps = {
type: 'model3d',
pos: [number, number],
props: {
size?: number, // We ignore size since this is a 2d view
size?: number,
dir?: 0 | 1 | 2 | 3,
res: string,
res?: string,
},
};

Expand All @@ -58,17 +58,13 @@ export type DoorBlockProps = {
};

export const isWallBlock = (block: GenericGalleryBlock): block is WallBlockProps => {
return block.type === 'wall' && block.props.size !== undefined;
return block.type === 'wall';
};

export const isModel3DBlock = (block: GenericGalleryBlock): block is Model3DBlockProps => {
return block.type === 'model3d' && block.props.res !== undefined && block.props.size === undefined;
return block.type === 'model3d';
};

export const isDoorBlock = (block: GenericGalleryBlock): block is DoorBlockProps => {
return block.type === 'door' && block.props.size !== undefined;
};

export const isPictureSlot = (block: GenericGalleryBlock): block is PictureSlotProps => {
return block.type === 'model3d' && block.props.res !== undefined && block.props.size !== undefined;
return block.type === 'door';
};

0 comments on commit 75d2977

Please sign in to comment.