├── .gitignore
├── .npmignore
├── index.html
├── demo
├── three-primitive.js
└── index.js
├── LICENSE.md
├── package.json
├── README.md
└── index.js
/.gitignore:
--------------------------------------------------------------------------------
1 | bower_components
2 | node_modules
3 | *.log
4 | .DS_Store
5 | bundle.js
6 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | bower_components
2 | node_modules
3 | *.log
4 | .DS_Store
5 | bundle.js
6 | test
7 | test.js
8 | demo/
9 | .npmignore
10 | LICENSE.md
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | three-buffer-vertex-data
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/demo/three-primitive.js:
--------------------------------------------------------------------------------
1 | var buffer = require('../')
2 |
3 | module.exports = setPrimitive
4 | function setPrimitive (geometry, data) {
5 | if (!data.cells || !data.positions) {
6 | throw new TypeError('expected at least { cells, positions }')
7 | }
8 |
9 | buffer.index(geometry, data.cells)
10 | buffer.attr(geometry, 'position', data.positions, 3)
11 |
12 | if (data.uvs) buffer.attr(geometry, 'uv', data.uvs, 2)
13 | else geometry.removeAttribute('uv')
14 |
15 | if (data.normals) buffer.attr(geometry, 'normal', data.normals, 3)
16 | else geometry.removeAttribute('normal')
17 | }
18 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 | Copyright (c) 2015 Jam3
3 |
4 | Permission is hereby granted, free of charge, to any person obtaining a copy
5 | of this software and associated documentation files (the "Software"), to deal
6 | in the Software without restriction, including without limitation the rights
7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 | copies of the Software, and to permit persons to whom the Software is
9 | furnished to do so, subject to the following conditions:
10 |
11 | The above copyright notice and this permission notice shall be included in all
12 | copies or substantial portions of the Software.
13 |
14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
17 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
18 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
19 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
20 | OR OTHER DEALINGS IN THE SOFTWARE.
21 |
22 |
--------------------------------------------------------------------------------
/demo/index.js:
--------------------------------------------------------------------------------
1 | global.THREE = require('three')
2 |
3 | // some mesh primitives from npm
4 | var createSphere = require('primitive-sphere')
5 | var createTorus = require('primitive-torus')
6 | var createQuad = require('primitive-quad')
7 |
8 | // utility function to set a BufferGeometry to a primitive
9 | var setToPrimitive = require('./three-primitive')
10 | var baboon = require('baboon-image-uri')
11 | var loadImage = require('img')
12 |
13 | var createOrbitViewer = require('three-orbit-viewer')(THREE)
14 | var app = createOrbitViewer({
15 | clearColor: 0x000000,
16 | clearAlpha: 1.0,
17 | fov: 65,
18 | position: new THREE.Vector3(1, 1, -2)
19 | })
20 |
21 | var primitives = [
22 | createQuad(), createSphere(), createTorus()
23 | ]
24 |
25 | var tick = 0
26 | var geometry = new THREE.BufferGeometry()
27 | var material = new THREE.MeshBasicMaterial({
28 | map: new THREE.Texture(),
29 | side: THREE.DoubleSide
30 | })
31 | var mesh = new THREE.Mesh(geometry, material)
32 | app.scene.add(mesh)
33 |
34 | // test mesh primitive UVs
35 | loadImage(baboon, function (err, image) {
36 | if (err) throw err
37 | var tex = material.map
38 | tex.image = image
39 | tex.wrapS = tex.wrapT = THREE.RepeatWrapping
40 | tex.flipY = false
41 | tex.needsUpdate = true
42 | tex.repeat.set(5, 5)
43 | })
44 |
45 | next()
46 | setInterval(next, 1000)
47 |
48 | function next () {
49 | var newMesh = primitives[tick++ % primitives.length]
50 | setToPrimitive(geometry, newMesh)
51 | }
52 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "three-buffer-vertex-data",
3 | "version": "1.1.0",
4 | "description": "an easy way to set vertex data on a BufferGeometry",
5 | "main": "index.js",
6 | "license": "MIT",
7 | "author": {
8 | "name": "Matt DesLauriers",
9 | "email": "dave.des@gmail.com",
10 | "url": "https://github.com/mattdesl"
11 | },
12 | "dependencies": {
13 | "flatten-vertex-data": "^1.0.0"
14 | },
15 | "devDependencies": {
16 | "baboon-image-uri": "^1.0.1",
17 | "browserify": "^12.0.1",
18 | "budo": "^7.0.0",
19 | "img": "^1.0.0",
20 | "primitive-quad": "^1.0.1",
21 | "primitive-sphere": "^2.0.0",
22 | "primitive-torus": "^1.0.4",
23 | "three": "^0.73.0",
24 | "three-orbit-viewer": "^69.3.1",
25 | "uglify-js": "^2.6.1"
26 | },
27 | "scripts": {
28 | "test": "node test.js",
29 | "start": "budo demo/index.js:bundle.js --live",
30 | "build": "browserify demo/index.js | uglifyjs -cm > bundle.js"
31 | },
32 | "standard": {
33 | "globals": [
34 | "THREE"
35 | ]
36 | },
37 | "keywords": [
38 | "buffer",
39 | "geometry",
40 | "BufferGeometry",
41 | "three",
42 | "webgl",
43 | "js",
44 | "threejs",
45 | "three.js",
46 | "data",
47 | "vertex",
48 | "vert",
49 | "vertices",
50 | "dynamic",
51 | "bufferdata"
52 | ],
53 | "repository": {
54 | "type": "git",
55 | "url": "git://github.com/Jam3/three-buffer-vertex-data.git"
56 | },
57 | "homepage": "https://github.com/Jam3/three-buffer-vertex-data",
58 | "bugs": {
59 | "url": "https://github.com/Jam3/three-buffer-vertex-data/issues"
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # three-buffer-vertex-data
2 |
3 | [](http://github.com/badges/stability-badges)
4 |
5 | [(demo)](http://jam3.github.io/three-buffer-vertex-data/) - [(source)](./demo/index.js)
6 |
7 | Convenience functions for sending vertex data to an attribtue of a [THREE.BufferGeometry](http://threejs.org/docs/#Reference/Core/BufferGeometry).
8 |
9 | This will flatten vertex data if necessary, attempt to re-use arrays where possible to minimize GC, and handle some compatibility issues across different versions of ThreeJS. It can handle dynamically growing and shrinking buffers.
10 |
11 | The input is expected in simple arrays, and can be one of:
12 |
13 | - a nested array like `[ [ x, y ], [ x, y ] ]`
14 | - a flat array like `[ x, y, z, x, y, z ]`
15 | - a typed array like `new Float32Array([ x, y ])`
16 |
17 | This is particularly useful in new versions of ThreeJS, where BufferGeometry is required for custom shader attributes.
18 |
19 | ## Install
20 |
21 | ```sh
22 | npm install three-buffer-vertex-data --save
23 | ```
24 |
25 | ## Example
26 |
27 | A simple example using [snowden](https://github.com/stackgl/snowden).
28 |
29 | ```js
30 | var buffer = require('three-buffer-vertex-data')
31 |
32 | // grab a simplicial complex
33 | var snowden = require('snowden')
34 |
35 | // set up our geometry
36 | var geometry = new THREE.BufferGeometry()
37 | buffer.index(geometry, snowden.cells)
38 | buffer.attr(geometry, 'position', snowden.positions)
39 |
40 | // add to scene
41 | var material = new THREE.MeshBasicMaterial()
42 | var mesh = new THREE.Mesh(geometry, material)
43 | scene.add(mesh)
44 | ```
45 |
46 | The `'position'`, `'uv'` and `'normal'` attributes are built-in to ThreeJS and will work with common materials. Result:
47 |
48 |
49 |
50 | See [demo](./demo/index.js) for a more complex example, which cycles various [mesh primitives](https://github.com/glo-js/mesh-primitives) into the same vertex buffers.
51 |
52 | ## Usage
53 |
54 | [](https://www.npmjs.com/package/three-buffer-vertex-data)
55 |
56 | The `data` passed is sent through [flatten-vertex-data](https://github.com/glo-js/flatten-vertex-data), and can be in the form of:
57 |
58 | - a nested array like `[ [ x, y ], [ x, y ] ]`
59 | - a flat array like `[ x, y, z, x, y, z ]`
60 | - a typed array like `new Float32Array([ x, y ])`
61 |
62 | If the attribute exists, its underlying `array` will try to be re-used. Otherwise, a new typed array will be created from the optional [dtype](https://www.npmjs.com/package/dtype).
63 |
64 | #### `buffer.index(geometry, data, [itemSize], [dtype])`
65 |
66 | Sets the `index` attribute for the given buffer `geometry`, with your list of indices in `data`. The `itemSize` defaults to 1, and `dtype` string defaults to `'uint16'`
67 |
68 | #### `buffer.attr(geometry, key, data, [itemSize], [dtype])`
69 |
70 | Sets a generic attribute on the given buffer `geometry` by the given `key` and vertex `data`. The `itemSize` defaults to 3, and `dtype` string defaults to `'float32'`.
71 |
72 | ## Gotchas
73 |
74 | In r73, ThreeJS breaks when using `wireframe` and attempting to dynamically grow/shrink and add/remove attributes.
75 |
76 | Also, ThreeJS typically expects indices and `positions` to exist. Things may break without these attributes set.
77 |
78 | You will need THREE r82 or newer if you wish to dynamically grow and shrink vertex buffers.
79 |
80 | ## License
81 |
82 | MIT, see [LICENSE.md](http://github.com/Jam3/three-buffer-vertex-data/blob/master/LICENSE.md) for details.
83 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | var flatten = require('flatten-vertex-data')
2 | var warned = false;
3 |
4 | module.exports.attr = setAttribute
5 | module.exports.index = setIndex
6 |
7 | function setIndex (geometry, data, itemSize, dtype) {
8 | if (typeof itemSize !== 'number') itemSize = 1
9 | if (typeof dtype !== 'string') dtype = 'uint16'
10 |
11 | var isR69 = !geometry.index && typeof geometry.setIndex !== 'function'
12 | var attrib = isR69 ? geometry.getAttribute('index') : geometry.index
13 | var newAttrib = updateAttribute(attrib, data, itemSize, dtype)
14 | if (newAttrib) {
15 | if (isR69) geometry.addAttribute('index', newAttrib)
16 | else geometry.index = newAttrib
17 | }
18 | }
19 |
20 | function setAttribute (geometry, key, data, itemSize, dtype) {
21 | if (typeof itemSize !== 'number') itemSize = 3
22 | if (typeof dtype !== 'string') dtype = 'float32'
23 | if (Array.isArray(data) &&
24 | Array.isArray(data[0]) &&
25 | data[0].length !== itemSize) {
26 | throw new Error('Nested vertex array has unexpected size; expected ' +
27 | itemSize + ' but found ' + data[0].length)
28 | }
29 |
30 | var attrib = geometry.getAttribute(key)
31 | var newAttrib = updateAttribute(attrib, data, itemSize, dtype)
32 | if (newAttrib) {
33 | geometry.addAttribute(key, newAttrib)
34 | }
35 | }
36 |
37 | function updateAttribute (attrib, data, itemSize, dtype) {
38 | data = data || []
39 | if (!attrib || rebuildAttribute(attrib, data, itemSize)) {
40 | // create a new array with desired type
41 | data = flatten(data, dtype)
42 |
43 | var needsNewBuffer = attrib && typeof attrib.setArray !== 'function'
44 | if (!attrib || needsNewBuffer) {
45 | // We are on an old version of ThreeJS which can't
46 | // support growing / shrinking buffers, so we need
47 | // to build a new buffer
48 | if (needsNewBuffer && !warned) {
49 | warned = true
50 | console.warn([
51 | 'A WebGL buffer is being updated with a new size or itemSize, ',
52 | 'however this version of ThreeJS only supports fixed-size buffers.',
53 | '\nThe old buffer may still be kept in memory.\n',
54 | 'To avoid memory leaks, it is recommended that you dispose ',
55 | 'your geometries and create new ones, or update to ThreeJS r82 or newer.\n',
56 | 'See here for discussion:\n',
57 | 'https://github.com/mrdoob/three.js/pull/9631'
58 | ].join(''))
59 | }
60 |
61 | // Build a new attribute
62 | attrib = new THREE.BufferAttribute(data, itemSize);
63 | }
64 |
65 | attrib.itemSize = itemSize
66 | attrib.needsUpdate = true
67 |
68 | // New versions of ThreeJS suggest using setArray
69 | // to change the data. It will use bufferData internally,
70 | // so you can change the array size without any issues
71 | if (typeof attrib.setArray === 'function') {
72 | attrib.setArray(data)
73 | }
74 |
75 | return attrib
76 | } else {
77 | // copy data into the existing array
78 | flatten(data, attrib.array)
79 | attrib.needsUpdate = true
80 | return null
81 | }
82 | }
83 |
84 | // Test whether the attribute needs to be re-created,
85 | // returns false if we can re-use it as-is.
86 | function rebuildAttribute (attrib, data, itemSize) {
87 | if (attrib.itemSize !== itemSize) return true
88 | if (!attrib.array) return true
89 | var attribLength = attrib.array.length
90 | if (Array.isArray(data) && Array.isArray(data[0])) {
91 | // [ [ x, y, z ] ]
92 | return attribLength !== data.length * itemSize
93 | } else {
94 | // [ x, y, z ]
95 | return attribLength !== data.length
96 | }
97 | return false
98 | }
99 |
--------------------------------------------------------------------------------