Skip to content

Commit

Permalink
feat: three fiber
Browse files Browse the repository at this point in the history
  • Loading branch information
wangzhu committed Nov 3, 2023
1 parent 41edeed commit 8eaa70e
Show file tree
Hide file tree
Showing 10 changed files with 491 additions and 37 deletions.
2 changes: 1 addition & 1 deletion .husky/pre-commit
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@
. "$(dirname "$0")/_/husky.sh"

pnpm test
pnpm exec lint-staged
# pnpm exec lint-staged
9 changes: 8 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,13 @@
"lint": "eslint --cache ./src"
},
"dependencies": {
"@react-three/drei": "^9.88.11",
"@react-three/fiber": "^8.14.5",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-error-boundary": "^3.1.4",
"react-router-dom": "^6.16.0",
"three": "^0.157.0",
"vite-plugin-svgr": "2.4.0"
},
"devDependencies": {
Expand All @@ -43,6 +47,7 @@
"@types/node": "^18.11.18",
"@types/react": "^18.0.26",
"@types/react-dom": "^18.0.10",
"@types/three": "^0.157.0",
"@typescript-eslint/eslint-plugin": "^5.48.0",
"@typescript-eslint/parser": "^5.48.0",
"@vitejs/plugin-react": "^3.0.1",
Expand All @@ -60,14 +65,16 @@
"husky": "^8.0.3",
"jest": "29.3.1",
"jest-environment-jsdom": "^29.3.1",
"leva": "^0.9.35",
"lint-staged": "^13.1.0",
"postcss": "^8.4.21",
"prettier": "2.8.2",
"r3f-perf": "^7.1.2",
"react-test-renderer": "^18.2.0",
"tailwindcss": "^3.2.4",
"ts-jest": "29.0.3",
"typescript": "^4.9.4",
"vite": "^4.0.4",
"vite": "^4.4.11",
"vite-tsconfig-paths": "^4.0.3"
}
}
53 changes: 46 additions & 7 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,52 @@
import { Header } from 'components/Header';
import { Button } from 'components/Button';
import { ReactComponent as Logo } from 'assets/favicon.svg';
import Box from 'components/Box';
import Lights from 'components/Lights';
import Setup from 'components/Setup';
import { useControls } from 'leva';
import { Perf } from 'r3f-perf';
import { useMemo, useRef } from 'react';
import { OrbitControls, Stats } from '@react-three/drei';
import Polyhedron from 'components/Polyhedron';
import * as THREE from 'three';

function App() {
const containerRef = useRef<HTMLDivElement>(null);
const { showAxes } = useControls({
showAxes: true,
});

return (
<div className="App">
<Header title="hola" />
<Logo height={100} width={100} />
<Button onClick={() => alert('hola')}>Heyo</Button>
<div ref={containerRef} className="App h-full w-full">
<Setup>
<Box position={[2, 0, 2]} />
<Box position={[2, 0, 0]} />
<Box position={[0, 0, 2]} />
<Polyhedron
name="meshBasicMaterial"
position={[-3, 1, -1]}
material={new THREE.MeshBasicMaterial()}
/>
<Polyhedron
name="meshNormalMaterial"
position={[-1, 1, -1]}
material={new THREE.MeshNormalMaterial()}
/>
<Polyhedron
name="meshPhongMaterial"
position={[1, 1, -1]}
material={new THREE.MeshPhongMaterial()}
/>
<Polyhedron
name="meshStandardMaterial"
position={[3, 1, -1]}
material={new THREE.MeshStandardMaterial()}
/>
<gridHelper args={[50, 50, 0x444444, 'teal']} />
{showAxes && <axesHelper position={[0, 0, 0]} args={[20]}></axesHelper>}
<Lights />
<OrbitControls enableDamping={false} />
<Stats showPanel={2} />
<Perf position="bottom-right" />
</Setup>
</div>
);
}
Expand Down
133 changes: 133 additions & 0 deletions src/App.tsx.old
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
import * as THREE from 'three';
import { useEffect, useRef, useState } from 'react';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';
import Stats from 'three/examples/jsm/libs/stats.module';
import { GUI } from 'three/examples/jsm/libs/lil-gui.module.min';

function makeInstance(
geometry: THREE.BoxGeometry,
color: THREE.ColorRepresentation,
position: THREE.Vector3
) {
const material = new THREE.MeshPhongMaterial({ color });
const cube = new THREE.Mesh(geometry, material);
cube.position.set(position.x, position.y, position.z);
return cube;
}

function App() {
const containerRef = useRef<HTMLDivElement>(null);
const [renderer, setRenderer] = useState(
new THREE.WebGLRenderer({
antialias: true,
})
);

const initCamera = () => {
const camera = new THREE.PerspectiveCamera(
45,
window.innerWidth / window.innerHeight,
0.1,
1000
);
camera.position.set(-50, 100, 50);
camera.lookAt(0, 0, 0);
return camera;
};

const initGui = () => {
const gui = new GUI();
gui.domElement.style.right = '0px';
gui.domElement.style.width = '300px';
return gui;
};

const addLight = (): [THREE.DirectionalLight, THREE.DirectionalLightHelper] => {
const color = 0xffffff;
const intensity = 1;
const light = new THREE.DirectionalLight(color, intensity);
light.position.set(50, 50, 50);
light.lookAt(0, 0, 0);
const lightHelper = new THREE.DirectionalLightHelper(light);
return [light, lightHelper];
};

const addLine = () => {
const material = new THREE.LineBasicMaterial({ color: 0x0000ff });
const points = [];
points.push(new THREE.Vector3(-10, 0, 0));
points.push(new THREE.Vector3(0, 10, 0));
points.push(new THREE.Vector3(10, 0, 0));
const geometry = new THREE.BufferGeometry().setFromPoints(points);
const line = new THREE.Line(geometry, material);
return line;
};

const init = () => {
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setClearColor(0x444444, 1);
const scene = new THREE.Scene();
const camera = initCamera();
const lights = addLight();
const geometry = new THREE.BoxGeometry(10, 10, 10);
const cubes: ReturnType<typeof makeInstance>[] = [];
for (let i = -2; i < 3; i++) {
for (let j = -2; j < 3; j++) {
const pos = new THREE.Vector3(i * 20, 0, j * 20);
const cube = makeInstance(geometry, (0x44aa88 >> i) << j, pos);
cubes.push(cube);
}
}
const axesHelper = new THREE.AxesHelper(20);
scene.add(...cubes);
scene.add(...lights);
scene.add(axesHelper);
const stats = new Stats();
document.body.appendChild(stats.dom);
function animate(time: number) {
requestAnimationFrame(animate);
time *= 0.001; // 将时间单位变为秒

cubes.forEach((cube, ndx) => {
const speed = 1 + ndx * 0.1;
const rot = time * speed;
cube.rotation.x = rot;
cube.rotation.y = rot;
});
stats.update();
renderer.render(scene, camera);
}
if (containerRef.current) {
containerRef.current.innerHTML = '';
containerRef.current?.appendChild(renderer.domElement);
animate(1);
}
const controls = new OrbitControls(camera, renderer.domElement);
// 如果OrbitControls改变了相机参数,重新调用渲染器渲染三维场景
controls.addEventListener('change', function () {
renderer.render(scene, camera); //执行渲染操作
}); //监听鼠标、键盘事件

window.onresize = () => {
if (containerRef.current) {
renderer.setSize(containerRef.current?.offsetWidth, containerRef.current?.offsetHeight);
camera.aspect = containerRef.current?.offsetWidth / containerRef.current?.offsetHeight;
camera.updateProjectionMatrix();
}
};

const gui = initGui();
gui.add(lights[0], 'intensity', 0, 5);
gui.add(lights[0].position, 'x', -100, 100);
gui.add(lights[0].position, 'y', -100, 100);
gui.add(lights[0].position, 'z', -100, 100);
};

useEffect(() => {
init();
});

return <div ref={containerRef} className="App h-full w-full"></div>;
}

export default App;
24 changes: 24 additions & 0 deletions src/components/Box/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { memo, useRef } from 'react';
import { useFrame } from '@react-three/fiber';

import type { Mesh } from 'three';
import type { MeshProps } from '@react-three/fiber';

const Box = (props: MeshProps) => {
const boxRef = useRef<Mesh>(null);

useFrame((_, delta) => {
if (!boxRef.current) return;
boxRef.current.rotation.x += 1 * delta;
boxRef.current.rotation.y += 0.5 * delta;
});

return (
<mesh {...props} ref={boxRef}>
<boxGeometry />
<meshBasicMaterial color={0x00ff00} wireframe />
</mesh>
);
};

export default memo(Box);
20 changes: 20 additions & 0 deletions src/components/Lights/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import * as THREE from 'three';
import { useRef } from 'react';
import { useHelper } from '@react-three/drei';
import type { DirectionalLight, Object3D } from 'three';
export default function Lights() {
const directionalLightRef = useRef<DirectionalLight>(null);
useHelper(directionalLightRef, THREE.DirectionalLightHelper, 2);

return (
<>
<ambientLight intensity={1} />
<directionalLight
position={[0, 50, 0]}
color={0xffffff}
intensity={1}
ref={directionalLightRef}
/>
</>
);
}
40 changes: 40 additions & 0 deletions src/components/Polyhedron/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { memo, useRef } from 'react';
import { useFrame } from '@react-three/fiber';

import { Color, type Mesh } from 'three';
import type { MeshProps } from '@react-three/fiber';
import { useControls } from 'leva';

function Polyhedron(props: MeshProps) {
const polyhedronRef = useRef<Mesh>(null);

useFrame((_, delta) => {
if (!polyhedronRef.current) return;
polyhedronRef.current.rotation.x += 0.2 * delta;
polyhedronRef.current.rotation.y += 0.05 * delta;
});

useControls(props.name!, {
flatShading: {
value: true,
onChange: (v) => {
polyhedronRef.current!.material.flatShading = v;
polyhedronRef.current!.material.needsUpdate = true;
},
},
color: {
value: 'lime',
onChange: (v) => {
polyhedronRef.current!.material.color = new Color(v);
},
},
});

return (
<mesh {...props} ref={polyhedronRef}>
<icosahedronGeometry args={[1, 1]} />
</mesh>
);
}

export default memo(Polyhedron);
22 changes: 22 additions & 0 deletions src/components/Setup/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { Canvas } from '@react-three/fiber';
import { PropsWithChildren } from 'react';

function Setup({ children }: PropsWithChildren) {
return (
<Canvas
camera={{
fov: 45,
aspect: window.innerWidth / window.innerHeight,
near: 0.1,
far: 1000,
position: [-50, 100, 50],
}}
gl={{ antialias: true }}
>
<color attach="background" args={[0x444444]} />
{children}
</Canvas>
);
}

export default Setup;
6 changes: 1 addition & 5 deletions src/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,5 @@ import App from './App';
const container = document.getElementById('root');
if (container) {
const root = createRoot(container);
root.render(
<StrictMode>
<App />
</StrictMode>
);
root.render(<App />);
}
Loading

0 comments on commit 8eaa70e

Please sign in to comment.