Last active
May 30, 2023 11:09
-
-
Save nickyvanurk/18c13872ee61824ec92a511abbd6eede to your computer and use it in GitHub Desktop.
Threejs-react-starter-template (Vanilla)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { | |
BoxGeometry, | |
Color, | |
HemisphereLight, | |
Mesh, | |
MeshBasicMaterial, | |
MeshNormalMaterial, | |
PerspectiveCamera, | |
PlaneGeometry, | |
Scene, | |
WebGLRenderer, | |
} from 'three'; | |
import { MapControls } from 'three/examples/jsm/controls/OrbitControls'; | |
import Stats from 'three/examples/jsm/libs/stats.module'; | |
import { GUI } from 'lil-gui'; | |
/** | |
* Three.js map viewer | |
*/ | |
export class ThreeMapViewer { | |
container: HTMLElement; | |
camera!: PerspectiveCamera; | |
renderer!: WebGLRenderer; | |
scene!: Scene; | |
controls!: MapControls; | |
time = { last: performance.now(), now: 0, dt: 0 }; | |
stats = Stats(); | |
panel = new GUI({ autoPlace: false }); | |
settings = { performance: true }; | |
mesh!: Mesh; | |
drawCallsPanel!: Stats.Panel; | |
geometriesPanel!: Stats.Panel; | |
frameRequestId!: number; | |
/** | |
* Initial configuration | |
*/ | |
constructor(containerClass: string) { | |
this.container = document.querySelector(containerClass) as HTMLElement; | |
this.createPanel(); | |
this.createScene(); | |
this.createCamera(); | |
this.createLights(); | |
this.createControls(); | |
this.createMeshes(); | |
this.createRenderer(); | |
this.render(); | |
} | |
destroy() { | |
window.cancelAnimationFrame(this.frameRequestId); | |
} | |
/** | |
* All debug settings get initialized here | |
*/ | |
createPanel() { | |
this.stats.domElement.style.position = 'absolute'; | |
this.stats.domElement.style.display = 'flex'; | |
this.stats.domElement.style.flexDirection = 'column'; | |
this.container.appendChild(this.stats.domElement); | |
this.drawCallsPanel = Stats.Panel('Draw Calls', '#0ff', '#002'); | |
this.stats.addPanel(this.drawCallsPanel); | |
this.geometriesPanel = Stats.Panel('Geometries', '#0ff', '#002'); | |
this.stats.addPanel(this.geometriesPanel); | |
this.panel.domElement.style.position = 'absolute'; | |
this.panel.domElement.style.right = '0'; | |
this.panel.domElement.style.zIndex = '10'; | |
this.container.appendChild(this.panel.domElement); | |
const visibility = this.panel.addFolder('Visibility'); | |
visibility | |
.add(this.settings, 'performance') | |
.onChange((show: boolean) => (this.stats.dom.style.display = show ? 'flex' : 'none')); | |
} | |
/** | |
* The main scene gets initialized here | |
*/ | |
createScene() { | |
this.scene = new Scene(); | |
this.scene.background = new Color(0x050505); | |
} | |
/** | |
* All cameras get initialized here | |
*/ | |
createCamera() { | |
this.camera = new PerspectiveCamera(71, this.container.clientWidth / this.container.clientHeight, 0.1, 4000); | |
this.camera.position.set(0, 5, 10); | |
this.camera.lookAt(0, 1, 0); | |
} | |
/** | |
* All lights get initialized here | |
*/ | |
createLights() { | |
const light = new HemisphereLight(); | |
this.scene.add(light); | |
} | |
/** | |
* All meshes and 3D models, along with shaders get initialized here | |
*/ | |
createMeshes() { | |
const plane = new Mesh(new PlaneGeometry(100, 100), new MeshBasicMaterial({ color: 0xffffff })); | |
plane.rotateX(-Math.PI / 2); | |
this.scene.add(plane); | |
const box = new Mesh(new BoxGeometry(1, 1, 1), new MeshNormalMaterial()); | |
box.position.y = 1; | |
this.scene.add(box); | |
this.mesh = box; | |
} | |
/** | |
* All controls gets initialized here | |
*/ | |
createControls() { | |
this.controls = new MapControls(this.camera, this.container); | |
this.controls.enableDamping = true; | |
this.controls.panSpeed = 1.2; | |
this.controls.dampingFactor *= 2; | |
} | |
/** | |
* Renderer and its settings gets initialized here | |
*/ | |
createRenderer() { | |
this.renderer = new WebGLRenderer({ antialias: true }); | |
this.renderer.setSize(this.container.clientWidth, this.container.clientHeight); | |
this.renderer.setPixelRatio(window.devicePixelRatio); | |
this.renderer.domElement.style.position = 'absolute'; | |
this.renderer.domElement.style.top = '0'; | |
this.container.appendChild(this.renderer.domElement); | |
} | |
/** | |
* Render scene and camera | |
*/ | |
render() { | |
this.frameRequestId = window.requestAnimationFrame(this.render.bind(this)); | |
this.time.now = performance.now(); | |
this.time.dt = (this.time.now - this.time.last) / 1000; | |
this.time.last = this.time.now; | |
this.mesh.rotation.x += 1 * this.time.dt; | |
this.mesh.rotation.y += 1 * this.time.dt; | |
this.controls.update(); | |
this.renderer.render(this.scene, this.camera); | |
this.stats.update(); | |
this.geometriesPanel.update(this.renderer.info.memory.geometries, 2); | |
this.drawCallsPanel.update(this.renderer.info.render.calls, 2); | |
} | |
/** | |
* Resize renderer and camera frustum | |
*/ | |
onWindowResize() { | |
this.camera.aspect = this.container.clientWidth / this.container.clientHeight; | |
this.camera.updateProjectionMatrix(); | |
this.renderer.setSize(this.container.clientWidth, this.container.clientHeight); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { useEffect } from 'react'; | |
import { ThreeMapViewer as ThreeMapViewerImpl } from './src/three_map_viewer'; | |
/** | |
* Three.js map viewer component | |
*/ | |
export function ThreeMapViewer() { | |
useEffect(() => { | |
const viewer = new ThreeMapViewerImpl('.three-viewer'); | |
window.onresize = () => viewer.onWindowResize(); | |
return () => viewer.destroy(); | |
}, []); | |
return <div className="three-viewer" style={{ width: '100vw', height: '100vh' }}></div>; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment