Skip to content


 Zabbix
 Grafana
 Prometheus
 React Three Fiber
 Threejs and TypeScript
 SocketIO and TypeScript
 Blender Topological Earth
 Sweet Home 3D
 Design Patterns Python
 Design Patterns TypeScript
   
 Course Coupon Codes
Three.js and TypeScript
Kindle Edition
$6.99 $9.99 Paperback 
$22.99 $29.99




Design Patterns in TypeScript
Kindle Edition
$6.99 $9.99 Paperback
$11.99 $19.99




Design Patterns in Python
Kindle Edition
$6.99 $9.99 Paperback
$11.99 $19.99




Examples : Bender

Description

Demonstrates Bending BufferGeometries in Threejs.

Bender GitHub Repository : https://github.com/Sean-Bradley/Bender

This example demonstrates,

  • Bending a THREE.BoxGeometry
  • Bending a TextGeometry
  • Replacing a meshes geometry at runtime
  • Replacing the text of a TextGeometry at runtime
  • Using the FontLoader

Code

./src/client/utils/bender.ts

//MIT License
//Copyright (c) 2020-2021 Sean Bradley
//https://github.com/Sean-Bradley/Bender/blob/main/LICENSE

export default class Bender {
    public bend(geometry: THREE.BufferGeometry, axis: string, angle: number) {
        let theta = 0
        if (angle !== 0) {
            const v = (geometry.attributes.position as THREE.BufferAttribute)
                .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':
                        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':
                        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
                        v[i] = -(y - 1.0 / angle) * sinTheta
                        v[i + 1] = (y - 1.0 / angle) * cosTheta + 1.0 / angle
                        v[i + 2] = z
                        break
                }
            }
            geometry.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
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
import * as THREE from 'three'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
import Stats from 'three/examples/jsm/libs/stats.module'
import { GUI } from 'dat.gui'
import { Font, FontLoader } from 'three/examples/jsm/loaders/FontLoader'
import { TextGeometry } from 'three/examples/jsm/geometries/TextGeometry'
import Bender from './utils/bender'

const bender = new Bender()

const scene = new THREE.Scene()

const camera = new THREE.PerspectiveCamera(
    75,
    window.innerWidth / window.innerHeight,
    0.1,
    1000
)
camera.position.set(4, 4, 6)

const renderer = 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', 'Plane']
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: Font
const loader = new 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.BoxGeometry(5, 5, 5, 10, 10, 10)
    } else if (data.model === 'Plane') {
        newGeometry = new THREE.PlaneGeometry(10, 5, 20, 10)
    } else {
        newGeometry = new TextGeometry(data.text, {
            font: font,
            size: 1,
            height: 0.2,
            curveSegments: 2,
        })
    }

    newGeometry.center()
    bender.bend(newGeometry, data.axis, data.angle)
    mesh.geometry.dispose()
    mesh.geometry = newGeometry
}

const stats = new Stats()
document.body.appendChild(stats.dom)

function animate() {
    requestAnimationFrame(animate)

    controls.update()

    render()

    stats.update()
}

function render() {
    renderer.render(scene, camera)
}

regenerateGeometry()

animate()