From dda5bdf3929ceecea50ae913887d13d58dd5c33a Mon Sep 17 00:00:00 2001 From: Florian Sylvain Date: Sun, 9 Jun 2024 21:20:52 +0200 Subject: [PATCH] feat: faces and blocks --- index.html | 2 +- package.json | 10 +++++ pnpm-lock.yaml | 58 ++++++++++++++++++++++++++++ src/block.ts | 42 ++++++++++++++++++++ src/counter.ts | 9 ----- src/face.ts | 77 +++++++++++++++++++++++++++++++++++++ src/main.ts | 83 ++++++++++++++++++++++++++++----------- src/style.css | 96 +--------------------------------------------- src/typescript.svg | 1 - 9 files changed, 251 insertions(+), 127 deletions(-) create mode 100644 src/block.ts delete mode 100644 src/counter.ts create mode 100644 src/face.ts delete mode 100644 src/typescript.svg diff --git a/index.html b/index.html index 44a9335..e71d871 100644 --- a/index.html +++ b/index.html @@ -4,7 +4,7 @@ - Vite + TS + ThatsMyWorld
diff --git a/package.json b/package.json index af79b8d..a62b751 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,17 @@ "preview": "vite preview" }, "devDependencies": { + "@types/three": "^0.165.0", + "prettier": "^3.3.1", "typescript": "^5.2.2", "vite": "^5.2.0" + }, + "dependencies": { + "three": "^0.165.0" + }, + "prettier": { + "useTabs": true, + "semi": false, + "trailingComma": "none" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 463b23d..89eb546 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -7,7 +7,17 @@ settings: importers: .: + dependencies: + '@types/three': + specifier: ^0.165.0 + version: 0.165.0 + three: + specifier: ^0.165.0 + version: 0.165.0 devDependencies: + prettier: + specifier: ^3.3.1 + version: 3.3.1 typescript: specifier: ^5.2.2 version: 5.4.5 @@ -235,19 +245,37 @@ packages: cpu: [x64] os: [win32] + '@tweenjs/tween.js@23.1.2': + resolution: {integrity: sha512-kMCNaZCJugWI86xiEHaY338CU5JpD0B97p1j1IKNn/Zto8PgACjQx0UxbHjmOcLl/dDOBnItwD07KmCs75pxtQ==} + '@types/estree@1.0.5': resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==} + '@types/stats.js@0.17.3': + resolution: {integrity: sha512-pXNfAD3KHOdif9EQXZ9deK82HVNaXP5ZIF5RP2QG6OQFNTaY2YIetfrE9t528vEreGQvEPRDDc8muaoYeK0SxQ==} + + '@types/three@0.165.0': + resolution: {integrity: sha512-AJK8JZAFNBF0kBXiAIl5pggYlzAGGA8geVYQXAcPCEDRbyA+oEjkpUBcJJrtNz6IiALwzGexFJGZG2yV3WsYBw==} + + '@types/webxr@0.5.16': + resolution: {integrity: sha512-0E0Cl84FECtzrB4qG19TNTqpunw0F1YF0QZZnFMF6pDw1kNKJtrlTKlVB34stGIsHbZsYQ7H0tNjPfZftkHHoA==} + esbuild@0.20.2: resolution: {integrity: sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==} engines: {node: '>=12'} hasBin: true + fflate@0.8.2: + resolution: {integrity: sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==} + fsevents@2.3.3: resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} os: [darwin] + meshoptimizer@0.18.1: + resolution: {integrity: sha512-ZhoIoL7TNV4s5B6+rx5mC//fw8/POGyNxS/DZyCJeiZ12ScLfVwRE/GfsxwiTkMYYD5DmK2/JXnEVXqL4rF+Sw==} + nanoid@3.3.7: resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} @@ -260,6 +288,11 @@ packages: resolution: {integrity: sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==} engines: {node: ^10 || ^12 || >=14} + prettier@3.3.1: + resolution: {integrity: sha512-7CAwy5dRsxs8PHXT3twixW9/OEll8MLE0VRPCJyl7CkS6VHGPSlsVaWTiASPTyGyYRyApxlaWTzwUxVNrhcwDg==} + engines: {node: '>=14'} + hasBin: true + rollup@4.18.0: resolution: {integrity: sha512-QmJz14PX3rzbJCN1SG4Xe/bAAX2a6NpCP8ab2vfu2GiUr8AQcr2nCV/oEO3yneFarB67zk8ShlIyWb2LGTb3Sg==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} @@ -269,6 +302,9 @@ packages: resolution: {integrity: sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==} engines: {node: '>=0.10.0'} + three@0.165.0: + resolution: {integrity: sha512-cc96IlVYGydeceu0e5xq70H8/yoVT/tXBxV/W8A/U6uOq7DXc4/s1Mkmnu6SqoYGhSRWWYFOhVwvq6V0VtbplA==} + typescript@5.4.5: resolution: {integrity: sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==} engines: {node: '>=14.17'} @@ -421,8 +457,22 @@ snapshots: '@rollup/rollup-win32-x64-msvc@4.18.0': optional: true + '@tweenjs/tween.js@23.1.2': {} + '@types/estree@1.0.5': {} + '@types/stats.js@0.17.3': {} + + '@types/three@0.165.0': + dependencies: + '@tweenjs/tween.js': 23.1.2 + '@types/stats.js': 0.17.3 + '@types/webxr': 0.5.16 + fflate: 0.8.2 + meshoptimizer: 0.18.1 + + '@types/webxr@0.5.16': {} + esbuild@0.20.2: optionalDependencies: '@esbuild/aix-ppc64': 0.20.2 @@ -449,9 +499,13 @@ snapshots: '@esbuild/win32-ia32': 0.20.2 '@esbuild/win32-x64': 0.20.2 + fflate@0.8.2: {} + fsevents@2.3.3: optional: true + meshoptimizer@0.18.1: {} + nanoid@3.3.7: {} picocolors@1.0.1: {} @@ -462,6 +516,8 @@ snapshots: picocolors: 1.0.1 source-map-js: 1.2.0 + prettier@3.3.1: {} + rollup@4.18.0: dependencies: '@types/estree': 1.0.5 @@ -486,6 +542,8 @@ snapshots: source-map-js@1.2.0: {} + three@0.165.0: {} + typescript@5.4.5: {} vite@5.2.13: diff --git a/src/block.ts b/src/block.ts new file mode 100644 index 0000000..7ff66d8 --- /dev/null +++ b/src/block.ts @@ -0,0 +1,42 @@ +import { Object3D, Vector3 } from "three" +import Face from "./face" + +export default class Block { + faces: Face[] + facesIndexes: number[] = [] + position: Vector3 + + constructor(faces: Face[], coordinates: Vector3) { + this.faces = faces + this.position = coordinates + + this.faces.forEach((face) => { + const dummy = new Object3D() + dummy.position.add(coordinates) + dummy.updateMatrix() + face.instancedMesh?.setMatrixAt(face.instaceIndex, dummy.matrix) + this.facesIndexes.push(face.instaceIndex) + face.instaceIndex += 1 + }) + } + + translate(vec3: Vector3) { + this.position.add(vec3) + this.updateFacesPosition() + } + + setPosition(vec3: Vector3) { + this.position = vec3 + this.updateFacesPosition() + } + + private updateFacesPosition() { + this.faces.forEach((face, i) => { + const dummy = new Object3D() + dummy.position.add(this.position) + dummy.updateMatrix() + face.instancedMesh!.setMatrixAt(this.facesIndexes[i], dummy.matrix) + face.instancedMesh!.instanceMatrix.needsUpdate = true + }) + } +} diff --git a/src/counter.ts b/src/counter.ts deleted file mode 100644 index 09e5afd..0000000 --- a/src/counter.ts +++ /dev/null @@ -1,9 +0,0 @@ -export function setupCounter(element: HTMLButtonElement) { - let counter = 0 - const setCounter = (count: number) => { - counter = count - element.innerHTML = `count is ${counter}` - } - element.addEventListener('click', () => setCounter(counter + 1)) - setCounter(0) -} diff --git a/src/face.ts b/src/face.ts new file mode 100644 index 0000000..7591af5 --- /dev/null +++ b/src/face.ts @@ -0,0 +1,77 @@ +import { + Color, + InstancedMesh, + MeshLambertMaterial, + PlaneGeometry, + Vector3 +} from "three" + +export type Orientation = "North" | "South" | "East" | "West" | "Up" | "Down" + +export const DEG_RAD = Math.PI / 180 +export const FACE_ORIENTATION = [ + { + orientation: "North", + position: new Vector3(0, 0, 0.5), + rotation: new Vector3(0, 0, 0) + }, + { + orientation: "Down", + position: new Vector3(0, -0.5, 0), + rotation: new Vector3(90 * DEG_RAD, 0, 0) + }, + { + orientation: "Up", + position: new Vector3(0, 0.5, 0), + rotation: new Vector3(-90 * DEG_RAD, 0, 0) + }, + { + orientation: "East", + position: new Vector3(0.5, 0, 0), + rotation: new Vector3(0, 90 * DEG_RAD, 0) + }, + { + orientation: "West", + position: new Vector3(-0.5, 0, 0), + rotation: new Vector3(0, -90 * DEG_RAD, 0) + }, + { + orientation: "South", + position: new Vector3(0, 0, -0.5), + rotation: new Vector3(0, 180 * DEG_RAD, 0) + } +] + +export default class Face { + material: MeshLambertMaterial + orientation: Orientation + geometry: PlaneGeometry + instancedMesh: InstancedMesh | undefined + instaceIndex: number = 0 + + constructor(color: Color, orientation: Orientation, instancesCount: number) { + this.material = new MeshLambertMaterial({ color }) + + this.orientation = orientation + + const orientationFace = FACE_ORIENTATION.find( + (x) => x.orientation === orientation + ) + + this.geometry = new PlaneGeometry() + this.geometry.rotateX(orientationFace!.rotation.x) + this.geometry.rotateY(orientationFace!.rotation.y) + this.geometry.rotateZ(orientationFace!.rotation.z) + this.geometry.translate( + orientationFace!.position.x, + orientationFace!.position.y, + orientationFace!.position.z + ) + + this.instancedMesh = new InstancedMesh( + this.geometry, + this.material, + instancesCount + ) + } +} diff --git a/src/main.ts b/src/main.ts index 791547b..5f14aaf 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,24 +1,63 @@ -import './style.css' -import typescriptLogo from './typescript.svg' -import viteLogo from '/vite.svg' -import { setupCounter } from './counter.ts' +import "./style.css" +import * as THREE from "three" +import { OrbitControls } from "three/addons/controls/OrbitControls.js" +import Face from "./face" +import Block from "./block" -document.querySelector('#app')!.innerHTML = ` -
- - - - - - -

Vite + TypeScript

-
- -
-

- Click on the Vite and TypeScript logos to learn more -

-
-` +const scene = new THREE.Scene() +const camera = new THREE.PerspectiveCamera( + 75, + window.innerWidth / window.innerHeight, + 0.1, + 1000 +) -setupCounter(document.querySelector('#counter')!) +const renderer = new THREE.WebGLRenderer() +renderer.setSize(window.innerWidth, window.innerHeight) +renderer.setAnimationLoop(animate) +const controls = new OrbitControls(camera, renderer.domElement) +controls.update() +document.body.appendChild(renderer.domElement) + +const ambientLight = new THREE.AmbientLight(new THREE.Color("white"), 5) +scene.add(ambientLight) + +const chunk = [] as THREE.Vector3[] +for (let i = -16; i < 16; i++) { + for (let j = -16; j < 16; j++) { + chunk.push( + new THREE.Vector3( + i, + Math.floor(Math.sin(i / 12) * 4 + Math.cos(j / 12) * 4), + j + ) + ) + } +} + +const faces = [ + new Face(new THREE.Color("white"), "Up", chunk.length), + new Face(new THREE.Color("red"), "Down", chunk.length), + new Face(new THREE.Color("blue"), "North", chunk.length), + new Face(new THREE.Color("green"), "East", chunk.length), + new Face(new THREE.Color("yellow"), "West", chunk.length), + new Face(new THREE.Color("purple"), "South", chunk.length) +] + +const cubes = [] as Block[] +chunk.forEach((block) => { + cubes.push(new Block(faces, block)) +}) + +faces.forEach((face) => { + if (face.instancedMesh) scene.add(face.instancedMesh) +}) + +camera.position.x = 20 +camera.position.y = 20 +camera.position.z = 20 + +function animate(elapsedTimeMs: number) { + renderer.render(scene, camera) + controls.update() +} diff --git a/src/style.css b/src/style.css index f9c7350..34b8e5e 100644 --- a/src/style.css +++ b/src/style.css @@ -1,96 +1,4 @@ -:root { - font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; - line-height: 1.5; - font-weight: 400; - - color-scheme: light dark; - color: rgba(255, 255, 255, 0.87); - background-color: #242424; - - font-synthesis: none; - text-rendering: optimizeLegibility; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; -} - -a { - font-weight: 500; - color: #646cff; - text-decoration: inherit; -} -a:hover { - color: #535bf2; -} - body { + box-sizing: border-box; margin: 0; - display: flex; - place-items: center; - min-width: 320px; - min-height: 100vh; -} - -h1 { - font-size: 3.2em; - line-height: 1.1; -} - -#app { - max-width: 1280px; - margin: 0 auto; - padding: 2rem; - text-align: center; -} - -.logo { - height: 6em; - padding: 1.5em; - will-change: filter; - transition: filter 300ms; -} -.logo:hover { - filter: drop-shadow(0 0 2em #646cffaa); -} -.logo.vanilla:hover { - filter: drop-shadow(0 0 2em #3178c6aa); -} - -.card { - padding: 2em; -} - -.read-the-docs { - color: #888; -} - -button { - border-radius: 8px; - border: 1px solid transparent; - padding: 0.6em 1.2em; - font-size: 1em; - font-weight: 500; - font-family: inherit; - background-color: #1a1a1a; - cursor: pointer; - transition: border-color 0.25s; -} -button:hover { - border-color: #646cff; -} -button:focus, -button:focus-visible { - outline: 4px auto -webkit-focus-ring-color; -} - -@media (prefers-color-scheme: light) { - :root { - color: #213547; - background-color: #ffffff; - } - a:hover { - color: #747bff; - } - button { - background-color: #f9f9f9; - } -} +} \ No newline at end of file diff --git a/src/typescript.svg b/src/typescript.svg deleted file mode 100644 index d91c910..0000000 --- a/src/typescript.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file