Examples : Bender
Description
Demonstrates Bending Geometries in Threejs. Works with geometries of base type THREE.Geometry and THREE.BufferGeometry.
Bender GitHub Repository : https://github.com/Sean-Bradley/Bender
This examples demonstrates,
- Bending a THREE.BoxBufferGeometry
- Bending a THREE.TextBufferGeometry
- Replacing a meshes geometry at runtime
- Replacing the text of a THREE.TextBufferGeometry at runtime
- Using the THREE.FontLoader
Code
./src/server/server.ts
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | import express from "express" import path from "path" import http from "http" const port: number = 3000 class App { private server: http.Server private port: number constructor(port: number) { this.port = port const app = express() app.use(express.static(path.join(__dirname, '../client'))) app.use('/build/three.module.js', express.static(path.join(__dirname, '../../node_modules/three/build/three.module.js'))) app.use('/jsm/controls/OrbitControls', express.static(path.join(__dirname, '../../node_modules/three/examples/jsm/controls/OrbitControls.js'))) app.use('/jsm/libs/stats.module', express.static(path.join(__dirname, '../../node_modules/three/examples/jsm/libs/stats.module.js'))) app.use('/jsm/libs/dat.gui.module', express.static(path.join(__dirname, '../../node_modules/three/examples/jsm/libs/dat.gui.module.js'))) app.use('/fonts', express.static(path.join(__dirname, '../../node_modules/three/examples/fonts'))) this.server = new http.Server(app); } public Start() { this.server.listen(this.port, () => { console.log( `Server listening on port ${this.port}.` ) }) } } new App(port).Start() |
./src/client/bender.ts
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 | //MIT License //Copyright (c) 2020 Sean Bradley //https://github.com/Sean-Bradley/Bender/blob/main/LICENSE export default class Bender { public bend(geometry: THREE.Geometry | THREE.BufferGeometry, axis: string, angle: number) { let theta = 0 if (angle !== 0) { if ((geometry as THREE.Geometry).vertices) { const v = (geometry as THREE.Geometry).vertices for (let i = 0; i < v.length; i++) { let x = v[i].x let y = v[i].y let z = v[i].z switch (axis) { case "x": theta = z * angle break; case "y": theta = x * angle break; default://z theta = x * angle break; } let sinTheta = Math.sin(theta) let cosTheta = Math.cos(theta) switch (axis) { case "x": //bending around the X axis v[i].x = x; v[i].y = (y - 1.0 / angle) * cosTheta + 1.0 / angle; v[i].z = -(y - 1.0 / angle) * sinTheta; break; case "y": //bending around the Y axis v[i].x = -(z - 1.0 / angle) * sinTheta; v[i].y = y v[i].z = (z - 1.0 / angle) * cosTheta + 1.0 / angle; break; default://z //bending around the Z axis v[i].x = -(y - 1.0 / angle) * sinTheta; v[i].y = (y - 1.0 / angle) * cosTheta + 1.0 / angle; v[i].z = z; break; } } (geometry as THREE.Geometry).verticesNeedUpdate = true; } else { const v = (geometry as THREE.BufferGeometry).attributes.position.array as number[] for (let i = 0; i < v.length; i += 3) { let x = v[i] let y = v[i + 1] let z = v[i + 2] switch (axis) { case "x": theta = z * angle break; case "y": theta = x * angle break; default://z theta = x * angle break; } let sinTheta = Math.sin(theta) let cosTheta = Math.cos(theta) switch (axis) { case "x": //bending around the X axis v[i] = x; v[i + 1] = (y - 1.0 / angle) * cosTheta + 1.0 / angle; v[i + 2] = -(y - 1.0 / angle) * sinTheta; break; case "y": //bending around the Y axis v[i] = -(z - 1.0 / angle) * sinTheta; v[i + 1] = y v[i + 2] = (z - 1.0 / angle) * cosTheta + 1.0 / angle; break; default://z //bending around the Z axis v[i] = -(y - 1.0 / angle) * sinTheta; v[i + 1] = (y - 1.0 / angle) * cosTheta + 1.0 / angle; v[i + 2] = z; break; } } (geometry as THREE.BufferGeometry).attributes.position.needsUpdate = true; } } } } |
./src/client/client.ts
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 | import * as THREE from '/build/three.module.js' import { OrbitControls } from '/jsm/controls/OrbitControls' import Stats from '/jsm/libs/stats.module' import { GUI } from '/jsm/libs/dat.gui.module' import Bender from './bender.js' const bender = new Bender() const scene: THREE.Scene = new THREE.Scene() const camera: THREE.PerspectiveCamera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000) camera.position.set(4, 4, 6) const renderer: THREE.WebGLRenderer = new THREE.WebGLRenderer() renderer.setSize(window.innerWidth, window.innerHeight) document.body.appendChild(renderer.domElement) const controls = new OrbitControls(camera, renderer.domElement) controls.enableDamping = true const modelOptions = ["Cube", "Text"] const axisOptions = ["x", "y", "z"] const data = { model: modelOptions[0], axis: axisOptions[1], angle: Math.PI / 16, text: "seanwasere Threejs" } const material: THREE.MeshBasicMaterial = new THREE.MeshBasicMaterial({ color: 0x00ff00, wireframe: true }) const mesh: THREE.Mesh = new THREE.Mesh(new THREE.BufferGeometry(), material) scene.add(mesh) let font: THREE.Font const loader = new THREE.FontLoader(); loader.load('fonts/helvetiker_regular.typeface.json', function (f) { font = f }); window.addEventListener('resize', onWindowResize, false) function onWindowResize() { camera.aspect = window.innerWidth / window.innerHeight camera.updateProjectionMatrix() renderer.setSize(window.innerWidth, window.innerHeight) render() } const gui = new GUI() gui.add(data, "model", modelOptions).onChange(regenerateGeometry) gui.add(data, "text").onFinishChange(regenerateGeometry) gui.add(data, "axis", axisOptions).onChange(regenerateGeometry) gui.add(data, "angle", -Math.PI / 2, Math.PI / 2, 0.01).onChange(regenerateGeometry) gui.open() function regenerateGeometry() { let newGeometry if (data.model === "Cube") { newGeometry = new THREE.BoxBufferGeometry(5, 5, 5, 10, 10, 10) } else { newGeometry = new THREE.TextBufferGeometry(data.text, { font: font, size: 1, height: .2, curveSegments: 2 }); } newGeometry.center() bender.bend(newGeometry, data.axis, data.angle) mesh.geometry.dispose() mesh.geometry = newGeometry } const stats = Stats() document.body.appendChild(stats.dom) var animate = function () { requestAnimationFrame(animate) controls.update() render() stats.update() }; function render() { renderer.render(scene, camera) } regenerateGeometry() animate(); |