Skip to content

Vandanjon/Workshop-ThreeJS-part2

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

16 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Workshop-ThreeJS - Part 2

#js #react #3D #threejs #fiber #drei #postprocessing

Objective - Create a Simple Solar System

After the hard work done in Part 1, we rest a lot.

But now, time to move our little solo planet into a shiny animated system.

Steps

  1. πŸ“ Setup project
  2. πŸ–‹οΈ Convert code from vanilla THREE.JS to REACT-THREE FIBER.
  3. 🌍 Build Sun-Earth relation
  4. πŸ’« Add missing planets
  5. 🌘 Add shadows
  6. 🌌 Add rotations and revolutions
  7. ⭐ BONUS !

πŸ“ STEP 1. Devenir Cheyenne

To create our new universe, let's be organized. First, clone this repo, you will have the following "src" folder:

src
β”œβ”€β”€ assets
β”‚   β”œβ”€β”€ textures
β”‚   β”‚   β”œβ”€β”€ file1.jpg
β”‚   β”‚   β”œβ”€β”€ file2.jpg
β”‚   β”‚   └── ...
β”œβ”€β”€ css
β”‚   └── style.css
β”œβ”€β”€ datas
β”‚   └── celestials.js
β”œβ”€β”€ App.js
β”œβ”€β”€ index.css
β”œβ”€β”€ index.js
└── logo.svg

Which you would have get either by launch npx create-react-app simple-solar-system on the last project, remove useless files (as we will now work with React) and add datas and textures.

After this, you can add the following dependency:

npm i three

PROGRESSION: :white_check_mark::eight_pointed_black_star::eight_pointed_black_star::eight_pointed_black_star::eight_pointed_black_star::eight_pointed_black_star::eight_pointed_black_star:

πŸ† STEP 1 DONE πŸ†

πŸ–‹οΈ STEP 2. Convert code

We will use React-Three Fiber, which is merely a React renderer for Three.js. The good thing is that it allows us to develop our solar system even faster.

Install the dependency npm i @react-three/fiber

Rewrite after entirely this files by the following:

index.js

import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App";

import "./index.css";

const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
	<React.StrictMode>
		<App />
	</React.StrictMode>
);

In "App.js", replace ALL the code by the following:

App.js

import { TextureLoader } from "three";
import { Canvas, useLoader } from "@react-three/fiber";

import { planetsDatas } from "./datas/celestials";

const texture = planetsDatas[2].texture;

const App = () => {
	return (
		<Canvas camera={{ position: [0, 30, 50] }}>
			<ambientLight intensity={0.1} />

			<pointLight />

			<mesh>
				<sphereGeometry />
				<meshLambertMaterial map={useLoader(TextureLoader, texture)} />
			</mesh>
		</Canvas>
	);
};

export default App;

Well done.

We realized in a few lines the same job as before, but in half effort and time.

PROGRESSION: :white_check_mark::white_check_mark::eight_pointed_black_star::eight_pointed_black_star::eight_pointed_black_star::eight_pointed_black_star::eight_pointed_black_star:

πŸ† STEP 2 DONE πŸ†

🌍 STEP 3. Build Sun-Earth relation

Create two components : "Sun.js" and "Earth.js".

In "Sun.js", put this code:

import { useRef } from "react";
import { TextureLoader } from "three";
import { useLoader } from "@react-three/fiber";

import { sunDatas } from "../datas/celestials";

const Sun = () => {
	const sunRef = useRef(null);
	const relativeSize = sunDatas[0].relativeSize;
	const texture = useLoader(TextureLoader, sunDatas[0].texture);
	const intensity = sunDatas[0].intensity;

	return (
		<>
			<pointLight position={[0, 0, 0]} intensity={intensity} castShadow />
			<mesh ref={sunRef}>
				<sphereGeometry args={[relativeSize, 32, 32]} />
				<meshBasicMaterial map={texture} />
			</mesh>
		</>
	);
};

export default Sun;

and for "Earth.js":

import { TextureLoader } from "three";
import { useLoader } from "@react-three/fiber";

import { planetsDatas } from "../datas/celestials";

const Earth = () => {
	const textureMap = useLoader(TextureLoader, planetsDatas[2].texture);

	return (
		<>
			<mesh position={planetsDatas[2].position}>
				<sphereGeometry args={[planetsDatas[2].relativeSize, 32, 32]} />
				<meshLambertMaterial map={textureMap} />
			</mesh>
		</>
	);
};

export default Earth;

Now, you just need to clean "App.js" from useless code and import our two components.

import { Suspense } from "react";
import { Canvas } from "@react-three/fiber";

import Sun from "./components/Sun";
import Earth from "./components/Earth";

const App = () => {
	return (
		<Canvas camera={{ position: [0, 30, 50], fov: 50, near: 0.1, far: 1000 }}>
			<Suspense fallback={null}>
				<ambientLight intensity={0.1} />

				<Sun />

				<Earth />
			</Suspense>
		</Canvas>
	);
};

export default App;

Impressive. You have rewrite this code like a pro.

PROGRESSION: :white_check_mark::white_check_mark::white_check_mark::eight_pointed_black_star::eight_pointed_black_star::eight_pointed_black_star::eight_pointed_black_star:

πŸ† STEP 3 DONE πŸ†

πŸ’« STEP 4. Add missing planets

Now, you should add missing planets.

We won't write thousands of components here. The best is to refacto "Earth.js" onto a "Planet.js" component which will allow us to generate planets with datas from "celestials.js".

Checklist :

  • Make a "Planet" component which use props from parent
SOLUTION

Planet.js

import { useRef } from "react";
import { TextureLoader } from "three";
import { useLoader } from "@react-three/fiber";

const Planet = ({ texture, position, relativeSize }) => {
	const planetRef = useRef(null);

	const textureMap = useLoader(TextureLoader, texture);

	return (
		<>
			<mesh ref={planetRef} position={position}>
				<sphereGeometry args={[relativeSize, 32, 32]} />
				<meshLambertMaterial map={textureMap} />
			</mesh>
		</>
	);
};

export default Planet;
  • Map on this component in "App.js" to display all planets
SOLUTION

App.js

import Planet from "./components/Planet";
import { planetsDatas } from "./datas/celestials";

...

        {planetsDatas.map((p) => (
          <Planet
            key={p.id}
            texture={p.texture}
            position={p.position}
            relativeSize={p.relativeSize}
          />
        ))}
  • Import either {OrbitControls} from drei (another sugar library)
SOLUTION

npm i @react-three/drei

App.js

import { OrbitControls } from "@react-three/drei";
...

const App = () => {
  return (
    <Canvas camera={{ position: [0, 30, 50], fov: 50, near: 0.1, far: 1000 }}>
      <OrbitControls />
	  ...
  • Buy me a tea

πŸ† STEP 4 DONE πŸ†

PROGRESSION: :white_check_mark::white_check_mark::white_check_mark::white_check_mark::eight_pointed_black_star::eight_pointed_black_star::eight_pointed_black_star:

🌘 STEP 5. Add shadows

πŸ† STEP 5 DONE πŸ†

PROGRESSION: :white_check_mark::white_check_mark::white_check_mark::white_check_mark::white_check_mark::eight_pointed_black_star::eight_pointed_black_star:

🌌 STEP 6. Add rotations and revolutions

πŸ† STEP 6 DONE πŸ†

PROGRESSION: :white_check_mark::white_check_mark::white_check_mark::white_check_mark::white_check_mark::white_check_mark::eight_pointed_black_star:

We have a universe, but it lacks life. We're Gods, let's snap to make magic happens.

First, we need to create an axis for planet's revolution around the sun. To realize this, create an Ecliptic component.

import { BufferGeometry, Vector3 } from "three";

const Ecliptic = ({ xRadius, zRadius }) => {
	const points = [];
	for (let index = 0; index < 64; index++) {
		const angle = (index / 64) * 2 * Math.PI;
		const x = xRadius * Math.cos(angle);
		const z = zRadius * Math.sin(angle);
		points.push(new Vector3(x, 0, z));
	}

	points.push(points[0]);

	const lineGeometry = new BufferGeometry().setFromPoints(points);
	return (
		<line geometry={lineGeometry}>
			<lineBasicMaterial attach="material" color="#393e46" linewidth={10} />
		</line>
	);
};

export default Ecliptic;

Then, import the Ecliptic component in the Planet Component to add it to the meshes.

The tricky part will be to make the positions of the planets move as the page refreshes. To do this, we will use the useFrame() Hook, which is the equivalent of animate() in ThreeJS.

⭐ STEP 7. BONUS

  • Add a panel that displays planet's infos on click
  • Add Glooming for the "WOW effect"

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published