Skip to content

Updating THREE.Geometry to THREE.BufferGeometry

Description

Three r125 contained a major breaking change from previous versions.

The class THREE.Geometry was deprecated, renamed to just Geometry and moved to /jsm/deprecated/Geometry.js.

If you used the THREE.Geometry in any of your custom code, and you want to use THREE r125 or later, then you will need to update to use THREE.BufferGeometry.

Or add an import for /jsm/deprecated/Geometry.js

Since Three r125, all inbuilt geometries now derive from the THREE.BufferGeometry only. A BufferGeometry is a more efficient way of representing meshes since it stores the data as typed arrays. The classic THREE.Geometry stores its data as arrays of THREE.Vector3 and THREE.Color objects. The classic THREE.Geometry is more intuitive to read and edit from a human perspective, but it is slower to process in the WebGL shader code within the core of Threejs. Beware that in future versions of Threejs, THREE.BufferGeometry may be renamed to THREE.Geometry.

The below examples show several use cases of THREE.Geometry and how you can update them to use THREE.BufferGeometry.

Create a Line

The below example creates a line with 2 points.

THREE.Geometry

const geometry = new THREE.Geometry()
geometry.vertices.push(new THREE.Vector3(-5, 0, 0))
geometry.vertices.push(new THREE.Vector3(5, 0, 0))
const line = new THREE.Line(
    geometry,
    new THREE.LineBasicMaterial({ color: 0x888888 })
)
scene.add(line)

THREE.BufferGeometry

const points = []
points.push(new THREE.Vector3(-5, 0, 0))
points.push(new THREE.Vector3(5, 0, 0))
let geometry = new THREE.BufferGeometry().setFromPoints(points)
let line = new THREE.Line(
    geometry,
    new THREE.LineBasicMaterial({ color: 0x888888 })
)
scene.add(line)

Explode Points

Modifying the vertices of a geometry by repositioning them outwards from the center by a factor of 2.

THREE.Geometry

const vertices = (geometry as THREE.Geometry).vertices
for (let i = 0; i < vertices.length; i++) {
    vertices[i].multiplyScalar(2)
}
;(geometry as THREE.Geometry).verticesNeedUpdate = true

THREE.BufferGeometry

const positions = (geometry.attributes.position as THREE.BufferAttribute)
    .array as Array<number>
for (let i = 0; i < positions.length; i += 3) {
    const v = new THREE.Vector3(
        positions[i],
        positions[i + 1],
        positions[i + 2]
    ).multiplyScalar(2)
    positions[i] = v.x
    positions[i + 1] = v.y
    positions[i + 2] = v.z
}
;(geometry.attributes.position as THREE.BufferAttribute).needsUpdate = true

Tetrahedron

Manually creating a tetrahedron, also known as a triangular pyramid.

THREE.Geometry

const material = new THREE.MeshNormalMaterial()
let geometry = new THREE.Geometry()
geometry.vertices.push(
    new THREE.Vector3(1, 1, 1), //a
    new THREE.Vector3(-1, -1, 1), //b
    new THREE.Vector3(-1, 1, -1), //c
    new THREE.Vector3(1, -1, -1) //d
)
geometry.faces.push(
    new THREE.Face3(2, 1, 0),
    new THREE.Face3(0, 3, 2),
    new THREE.Face3(1, 3, 0),
    new THREE.Face3(2, 3, 1)
)
geometry.computeFlatVertexNormals()
const mesh = new THREE.Mesh(geometry, material)
scene.add(mesh)

THREE.BufferGeometry

const material = new THREE.MeshNormalMaterial()
let geometry = new THREE.BufferGeometry()
const points = [
    new THREE.Vector3(-1, 1, -1), //c
    new THREE.Vector3(-1, -1, 1), //b
    new THREE.Vector3(1, 1, 1), //a

    new THREE.Vector3(1, 1, 1), //a
    new THREE.Vector3(1, -1, -1), //d
    new THREE.Vector3(-1, 1, -1), //c

    new THREE.Vector3(-1, -1, 1), //b
    new THREE.Vector3(1, -1, -1), //d
    new THREE.Vector3(1, 1, 1), //a

    new THREE.Vector3(-1, 1, -1), //c
    new THREE.Vector3(1, -1, -1), //d
    new THREE.Vector3(-1, -1, 1), //b
]

geometry.setFromPoints(points)
geometry.computeVertexNormals()

const mesh = new THREE.Mesh(geometry, material)
scene.add(mesh)

Examples

Modify a position attribute

This example demonstrates modifying one of the values in the geometries attributes.position.array. The modified value affects the X coordinate of the second point that was added to the tetrahedron.

./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
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'

const scene = new THREE.Scene()

const camera = new THREE.PerspectiveCamera(
    75,
    window.innerWidth / window.innerHeight,
    0.1,
    100
)
camera.position.set(1.6, 1.7, 2)

const renderer = new THREE.WebGLRenderer()
renderer.setSize(window.innerWidth, window.innerHeight)
document.body.appendChild(renderer.domElement)

const controls = new OrbitControls(camera, renderer.domElement)

const material = new THREE.MeshNormalMaterial({ side: THREE.DoubleSide })
let geometry = new THREE.BufferGeometry()
const points = [
    new THREE.Vector3(-1, 1, -1), //c
    new THREE.Vector3(-1, -1, 1), //b
    new THREE.Vector3(1, 1, 1), //a

    new THREE.Vector3(1, 1, 1), //a
    new THREE.Vector3(1, -1, -1), //d
    new THREE.Vector3(-1, 1, -1), //c

    new THREE.Vector3(-1, -1, 1), //b
    new THREE.Vector3(1, -1, -1), //d
    new THREE.Vector3(1, 1, 1), //a

    new THREE.Vector3(-1, 1, -1), //c
    new THREE.Vector3(1, -1, -1), //d
    new THREE.Vector3(-1, -1, 1), //b
]

geometry.setFromPoints(points)
geometry.computeVertexNormals()

const mesh = new THREE.Mesh(geometry, material)
scene.add(mesh)

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

let data = {
    x: 1,
}
const gui = new GUI()
gui.add(data, 'x', -5, -1, 0.01).onChange(() => {
    ;((geometry.attributes.position as THREE.BufferAttribute)
        .array[3] as number) = data.x
    geometry.attributes.position.needsUpdate = true
})
gui.open()

function animate() {
    requestAnimationFrame(animate)
    controls.update()
    render()
    stats.update()
}

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

animate()

Twisted Cube

By iterating through all position attributes, you can apply a rotation and create a twisted cube.

./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
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'

const scene = new THREE.Scene()

const camera = new THREE.PerspectiveCamera(
    75,
    window.innerWidth / window.innerHeight,
    0.1,
    100
)
camera.position.set(0, 0.75, 1.5)

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

function twist(geometry: THREE.BufferGeometry, factor: number) {
    const q = new THREE.Quaternion()
    const up = new THREE.Vector3(0, 1, 0)
    const p = (geometry.attributes.position as THREE.BufferAttribute)
        .array as number[]

    for (let i = 0; i < p.length; i += 3) {
        q.setFromAxisAngle(up, p[i + 1] * factor)

        let vec = new THREE.Vector3(p[i], p[i + 1], p[i + 2])
        vec.applyQuaternion(q)

        p[i] = vec.x
        p[i + 2] = vec.z
    }

    geometry.computeVertexNormals()
    geometry.attributes.position.needsUpdate = true
}

let geometry = new THREE.BoxGeometry(1, 1, 1, 10, 10, 10)
twist(geometry, Math.PI / 2)
const twistedCube = new THREE.Mesh(
    geometry,
    new THREE.MeshNormalMaterial({
        wireframe: true,
    })
)
scene.add(twistedCube)

window.addEventListener(
    'resize',
    () => {
        camera.aspect = window.innerWidth / window.innerHeight
        camera.updateProjectionMatrix()
        renderer.setSize(window.innerWidth, window.innerHeight)
    },
    false
)

let data = {
    t: Math.PI / 2,
}

const gui = new GUI()
gui.add(data, 't', -Math.PI, Math.PI, 0.01).onChange((t) => {
    twistedCube.geometry.dispose()
    geometry = new THREE.BoxGeometry(1, 1, 1, 10, 10, 10)
    twist(geometry, t)
    twistedCube.geometry = geometry
})
gui.open()

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

function animate() {
    requestAnimationFrame(animate)
    controls.update()
    render()
    stats.update()
}

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

animate()

Comments