Jelajahi Sumber

Interleaved BufferGeometry

Added both intanced and non instanced interleaved bufferedGeometry
Ben Adams 10 tahun lalu
induk
melakukan
69c81f0da5

+ 24 - 21
examples/webgl_buffergeometry_instancing.html

@@ -1,7 +1,7 @@
 <!DOCTYPE html>
 <html lang="en">
 <head>
-    <title>three.js webgl - instancing test</title>
+    <title>three.js webgl - instancing test (single triangle)</title>
     <meta charset="utf-8">
     <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
     <style>
@@ -43,29 +43,28 @@
 
     <div id="container"></div>
     <div id="info">
-        <a href="http://threejs.org" target="_blank">three.js</a> - instancing demo
+        <a href="http://threejs.org" target="_blank">three.js</a> - instancing demo (single triangle)
         <div id="notSupported" style="display:none">Sorry your graphics card + browser does not support hardware instancing</div>
     </div>
 
-    <script src="../build/three.js"></script>
-
+    <script src="js/libs/dat.gui.min.js"></script>
+    <script src="../build/three.min.js"></script>
     <script src="js/Detector.js"></script>
     <script src="js/libs/stats.min.js"></script>
 
     <script id="vertexShader" type="x-shader/x-vertex">
-
         precision highp float;
 
         uniform float sineTime;
 
-        uniform mat4 modelViewMatrix; // optional
-        uniform mat4 projectionMatrix; // optional
+        uniform mat4 modelViewMatrix; 
+        uniform mat4 projectionMatrix; 
 
         attribute vec3 position;
         attribute vec3 offset;
         attribute vec4 color;
-        attribute vec4 rotationStart;
-        attribute vec4 rotationEnd;
+        attribute vec4 orientationStart;
+        attribute vec4 orientationEnd;
 
         varying vec3 vPosition;
         varying vec4 vColor;
@@ -73,9 +72,9 @@
         void main()	{
 
         vPosition = offset * max(abs(sineTime * 2.0 + 1.0), 0.5) + position;
-        vec4 rotation = normalize(mix(rotationStart, rotationEnd, sineTime));
-        vec3 vcV = cross(rotation.xyz, vPosition);
-        vPosition = vcV * (2.0 * rotation.w) + (cross(rotation.xyz, vcV) * 2.0 + vPosition);
+        vec4 orientation = normalize(mix(orientationStart, orientationEnd, sineTime));
+        vec3 vcV = cross(orientation.xyz, vPosition);
+        vPosition = vcV * (2.0 * orientation.w) + (cross(orientation.xyz, vcV) * 2.0 + vPosition);
 
         vColor = color;
 
@@ -132,6 +131,10 @@
 
             var geometry = new THREE.InstancedBufferGeometry();
 
+            geometry.maxInstancedCount = instances; // set so its initalized for dat.GUI, will be set in first draw otherwise
+            var gui = new dat.GUI();
+            gui.add( geometry, "maxInstancedCount", 0, 65000 );
+
             var vertices = new THREE.BufferAttribute( new Float32Array( triangles * 3 * 3 ), 3 );
 
             vertices.setXYZ( 0, 0.025, -0.025, 0 );
@@ -162,31 +165,31 @@
 
             var vector = new THREE.Vector4();
 
-            var rotationsStart = new THREE.InstancedBufferAttribute( new Float32Array( instances * 4 ), 4, 1, false );
+            var orientationsStart = new THREE.InstancedBufferAttribute( new Float32Array( instances * 4 ), 4, 1, false );
 
-            for ( var i = 0, ul = rotationsStart.length / 4; i < ul; i++ ) {
+            for ( var i = 0, ul = orientationsStart.length / 4; i < ul; i++ ) {
 
                 vector.set( Math.random() * 2 - 1, Math.random() * 2 - 1, Math.random() * 2 - 1, Math.random() * 2 - 1 );
                 vector.normalize();
 
-                rotationsStart.setXYZW( i, vector.x, vector.y, vector.z, vector.w );
+                orientationsStart.setXYZW( i, vector.x, vector.y, vector.z, vector.w );
 
             }
 
-            geometry.addAttribute( 'rotationStart', rotationsStart );
+            geometry.addAttribute( 'orientationStart', orientationsStart );
 
-            var rotationsEnd = new THREE.InstancedBufferAttribute( new Float32Array( instances * 4 ), 4, 1, false );
+            var orientationsEnd = new THREE.InstancedBufferAttribute( new Float32Array( instances * 4 ), 4, 1, false );
 
-            for ( var i = 0, ul = rotationsEnd.length / 4; i < ul; i++ ) {
+            for ( var i = 0, ul = orientationsEnd.length / 4; i < ul; i++ ) {
 
                 vector.set( Math.random() * 2 - 1, Math.random() * 2 - 1, Math.random() * 2 - 1, Math.random() * 2 - 1 );
                 vector.normalize();
 
-                rotationsEnd.setXYZW( i, vector.x, vector.y, vector.z, vector.w );
+                orientationsEnd.setXYZW( i, vector.x, vector.y, vector.z, vector.w );
 
             }
 
-            geometry.addAttribute( 'rotationEnd', rotationsEnd );
+            geometry.addAttribute( 'orientationEnd', orientationsEnd );
 
             // material
 
@@ -200,7 +203,7 @@
                 fragmentShader: document.getElementById( 'fragmentShader' ).textContent,
                 side: THREE.DoubleSide,
                 transparent: true,
-                attributes: { 'position': 0, 'offset': 1, 'color': 2, 'rotationStart': 3, 'rotationEnd': 4 }
+                attributes: { 'position': 0, 'offset': 1, 'color': 2, 'orientationStart': 3, 'orientationEnd': 4 }
 
             } );
 

+ 18 - 18
examples/webgl_buffergeometry_instancing_dynamic.html

@@ -1,7 +1,7 @@
 <!DOCTYPE html>
 <html lang="en">
 <head>
-    <title>three.js webgl - indexed instancing, dynamic updates</title>
+    <title>three.js webgl - indexed instancing (single box), dynamic updates</title>
     <meta charset="utf-8">
     <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
     <style>
@@ -43,11 +43,11 @@
 
     <div id="container"></div>
     <div id="info">
-        <a href="http://threejs.org" target="_blank">three.js</a> - indexed instancing, dynamic updates
+        <a href="http://threejs.org" target="_blank">three.js</a> - indexed instancing (single box), dynamic updates
         <div id="notSupported" style="display:none">Sorry your graphics card + browser does not support hardware instancing</div>
     </div>
 
-    <script src="../build/three.js"></script>
+    <script src="../build/three.min.js"></script>
 
     <script src="js/Detector.js"></script>
     <script src="js/libs/stats.min.js"></script>
@@ -55,21 +55,21 @@
     <script id="vertexShader" type="x-shader/x-vertex">
         precision highp float;
         
-        uniform mat4 modelViewMatrix; // optional
-        uniform mat4 projectionMatrix; // optional
+        uniform mat4 modelViewMatrix; 
+        uniform mat4 projectionMatrix; 
 
         attribute vec3 position;
         attribute vec3 offset;
         attribute vec2 uv;
-        attribute vec4 rotation;
+        attribute vec4 orientation;
 
         varying vec2 vUv;
 
         void main()	{
 
         vec3 vPosition = position;
-        vec3 vcV = cross(rotation.xyz, vPosition);
-        vPosition = vcV * (2.0 * rotation.w) + (cross(rotation.xyz, vcV) * 2.0 + vPosition);
+        vec3 vcV = cross(orientation.xyz, vPosition);
+        vPosition = vcV * (2.0 * orientation.w) + (cross(orientation.xyz, vcV) * 2.0 + vPosition);
 
         vUv = uv;
 
@@ -101,7 +101,7 @@
         var container, stats;
 
         var camera, scene, renderer;
-        var rotations;
+        var orientations;
 
 
         function init() {
@@ -226,18 +226,18 @@
             geometry.addAttribute( 'offset', offsets ); // per mesh translation
 
 
-            rotations = new THREE.InstancedBufferAttribute( new Float32Array( instances * 4 ), 4, 1, true );
+            orientations = new THREE.InstancedBufferAttribute( new Float32Array( instances * 4 ), 4, 1, true );
 
-            for ( var i = 0, ul = rotations.length / 4; i < ul; i++ ) {
+            for ( var i = 0, ul = orientations.length / 4; i < ul; i++ ) {
 
                 vector.set( Math.random() * 2 - 1, Math.random() * 2 - 1, Math.random() * 2 - 1, Math.random() * 2 - 1 );
                 vector.normalize();
 
-                rotations.setXYZW( i, vector.x, vector.y, vector.z, vector.w );
+                orientations.setXYZW( i, vector.x, vector.y, vector.z, vector.w );
 
             }
 
-            geometry.addAttribute( 'rotation', rotations ); // per mesh rotation
+            geometry.addAttribute( 'orientation', orientations ); // per mesh orientation
 
             // material
             var texture = THREE.ImageUtils.loadTexture( 'textures/crate.gif' );
@@ -252,7 +252,7 @@
                 fragmentShader: document.getElementById( 'fragmentShader' ).textContent,
                 side: THREE.DoubleSide,
                 transparent: false,
-                attributes: { 'position': 0, 'offset': 1, 'rotation': 2, 'uv': 3 }
+                attributes: { 'position': 0, 'offset': 1, 'orientation': 2, 'uv': 3 }
 
             } );
 
@@ -318,15 +318,15 @@
             var delta = ( time - lastTime ) / 5000;
             tmpQ.set( moveQ.x * delta, moveQ.y * delta, moveQ.z * delta, 1 ).normalize();
 
-            for ( var i = 0, ul = rotations.length / 4; i < ul; i++ ) {
+            for ( var i = 0, ul = orientations.length / 4; i < ul; i++ ) {
                 var index = i * 4;
-                currentQ.set( rotations.array[index], rotations.array[index + 1], rotations.array[index + 2], rotations.array[index + 3] );
+                currentQ.set( orientations.array[index], orientations.array[index + 1], orientations.array[index + 2], orientations.array[index + 3] );
                 currentQ.multiply( tmpQ );
                 
-                rotations.setXYZW( i, currentQ.x, currentQ.y, currentQ.z, currentQ.w );
+                orientations.setXYZW( i, currentQ.x, currentQ.y, currentQ.z, currentQ.w );
 
             }
-            rotations.needsUpdate = true;
+            orientations.needsUpdate = true;
             lastTime = time;
         }
 

+ 313 - 0
examples/webgl_buffergeometry_instancing_interleaved_dynamic.html

@@ -0,0 +1,313 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <title>three.js webgl - indexed instancing (single box), interleaved buffers, dynamic updates</title>
+    <meta charset="utf-8">
+    <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
+    <style>
+        body {
+            color: #ffffff;
+            font-family: Monospace;
+            font-size: 13px;
+            text-align: center;
+            font-weight: bold;
+            background-color: #000000;
+            margin: 0px;
+            overflow: hidden;
+        }
+
+        #info {
+            position: absolute;
+            top: 0px;
+            width: 100%;
+            padding: 5px;
+        }
+
+        a {
+            color: #ffffff;
+        }
+
+        #oldie a {
+            color: #da0;
+        }
+
+        #notSupported {
+            width: 50%;
+            margin: auto;
+            border: 2px red solid;
+            margin-top: 20px;
+            padding: 10px;
+        }
+    </style>
+</head>
+<body>
+
+    <div id="container"></div>
+    <div id="info">
+        <a href="http://threejs.org" target="_blank">three.js</a> - indexed instancing (single box), interleaved buffers, dynamic updates
+        <div id="notSupported" style="display:none">Sorry your graphics card + browser does not support hardware instancing</div>
+    </div>
+
+    <script src="../build/three.min.js"></script>
+
+    <script src="js/Detector.js"></script>
+    <script src="js/libs/stats.min.js"></script>
+
+    <script id="vertexShader" type="x-shader/x-vertex">
+        precision highp float;
+
+        uniform mat4 modelViewMatrix; 
+        uniform mat4 projectionMatrix; 
+
+        attribute vec3 position;
+        attribute vec3 offset;
+        attribute vec2 uv;
+        attribute vec4 orientation;
+
+        varying vec2 vUv;
+
+        void main()	{
+
+        vec3 vPosition = position;
+        vec3 vcV = cross(orientation.xyz, vPosition);
+        vPosition = vcV * (2.0 * orientation.w) + (cross(orientation.xyz, vcV) * 2.0 + vPosition);
+
+        vUv = uv;
+
+        gl_Position = projectionMatrix * modelViewMatrix * vec4( offset + vPosition, 1.0 );
+
+        }
+
+    </script>
+
+    <script id="fragmentShader" type="x-shader/x-fragment">
+        precision highp float;
+
+        uniform sampler2D map;
+
+        varying vec2 vUv;
+
+        void main()	{
+
+        gl_FragColor = texture2D(map, vUv);
+
+        }
+
+    </script>
+
+    <script>
+
+    if ( !Detector.webgl ) Detector.addGetWebGLMessage();
+
+    var container, stats;
+
+    var camera, scene, renderer;
+    var orientations, instanceBuffer;
+
+
+    function init() {
+
+        container = document.getElementById( 'container' );
+
+        camera = new THREE.PerspectiveCamera( 50, window.innerWidth / window.innerHeight, 1, 1000 );
+        //camera.position.z = 20;
+
+        renderer = new THREE.WebGLRenderer();
+        scene = new THREE.Scene();
+
+        // geometry
+
+        var instances = 5000;
+
+        var geometry = new THREE.InstancedBufferGeometry();
+
+        // per mesh data x,y,z,w,u,v,s,t for 4-element alignment
+        // only use x,y,z and u,v; but x, y, z, nx, ny, nz, u, v would be a good layout
+        var vertexBuffer = new THREE.InterleavedBuffer( new Float32Array( [
+            // Front
+            -1, 1, 1, 0, 0, 0, 0, 0,
+            1, 1, 1, 0, 1, 0, 0, 0,
+            -1, -1, 1, 0, 0, 1, 0, 0,
+            1, -1, 1, 0, 1, 1, 0, 0,
+            // Back
+            1, 1, -1, 0, 1, 0, 0, 0,
+            -1, 1, -1, 0, 0, 0, 0, 0,
+            1, -1, -1, 0, 1, 1, 0, 0,
+            -1, -1, -1, 0, 0, 1, 0, 0,
+            // Left
+            -1, 1, -1, 0, 1, 1, 0, 0,
+            -1, 1, 1, 0, 1, 0, 0, 0,
+            -1, -1, -1, 0, 0, 1, 0, 0,
+            -1, -1, 1, 0, 0, 0, 0, 0,
+            // Right
+            1, 1, 1, 0, 1, 0, 0, 0,
+            1, 1, -1, 0, 1, 1, 0, 0,
+            1, -1, 1, 0, 0, 0, 0, 0,
+            1, -1, -1, 0, 0, 1, 0, 0,
+            // Top
+            -1, 1, 1, 0, 0, 0, 0, 0,
+            1, 1, 1, 0, 1, 0, 0, 0,
+            -1, 1, -1, 0, 0, 1, 0, 0,
+            1, 1, -1, 0, 1, 1, 0, 0,
+            // Bottom
+            1, -1, 1, 0, 1, 0, 0, 0,
+            -1, -1, 1, 0, 0, 0, 0, 0,
+            1, -1, -1, 0, 1, 1, 0, 0,
+            -1, -1, -1, 0, 0, 1, 0, 0,
+        ] ), 8, false );
+
+        // Use vertexBuffer, starting at offset 0, 3 items in position attribute
+        var positions = new THREE.InterleavedBufferAttribute( vertexBuffer, 3, 0 );
+        geometry.addAttribute( 'position', positions );
+        // Use vertexBuffer, starting at offset 4, 2 items in uv attribute
+        var uvs = new THREE.InterleavedBufferAttribute( vertexBuffer, 2, 4 );
+        geometry.addAttribute( 'uv', uvs );
+
+        var indices = new Uint16Array( [
+            0, 1, 2,
+            2, 1, 3,
+            4, 5, 6,
+            6, 5, 7,
+            8, 9, 10,
+            10, 9, 11,
+            12, 13, 14,
+            14, 13, 15,
+            16, 17, 18,
+            18, 17, 19,
+            20, 21, 22,
+            22, 21, 23
+        ] );
+
+        geometry.addAttribute( 'index', new THREE.BufferAttribute( indices, 1 ) );
+
+        // per instance data
+        instanceBuffer = new THREE.InstancedInterleavedBuffer( new Float32Array( instances * 8 ), 8, true, 1 );
+        var offsets = new THREE.InterleavedBufferAttribute( instanceBuffer, 3, 0 );
+
+        var vector = new THREE.Vector4();
+        for ( var i = 0, ul = offsets.length / 3; i < ul; i++ ) {
+            var x = Math.random() * 100 - 50;
+            var y = Math.random() * 100 - 50;
+            var z = Math.random() * 100 - 50;
+            vector.set( x, y, z, 0 ).normalize();
+            // move out at least 5 units from center in current direction
+            offsets.setXYZ( i, x + vector.x * 5, y + vector.y * 5, z + vector.z * 5 );
+
+        }
+
+        geometry.addAttribute( 'offset', offsets ); // per mesh translation
+
+        orientations = new THREE.InterleavedBufferAttribute( instanceBuffer, 4, 4 );
+
+        for ( var i = 0, ul = orientations.length / 4; i < ul; i++ ) {
+
+            vector.set( Math.random() * 2 - 1, Math.random() * 2 - 1, Math.random() * 2 - 1, Math.random() * 2 - 1 );
+            vector.normalize();
+
+            orientations.setXYZW( i, vector.x, vector.y, vector.z, vector.w );
+
+        }
+
+        geometry.addAttribute( 'orientation', orientations ); // per mesh orientation
+
+        // material
+        var texture = THREE.ImageUtils.loadTexture( 'textures/crate.gif' );
+        texture.anisotropy = renderer.getMaxAnisotropy();
+
+        var material = new THREE.RawShaderMaterial( {
+
+            uniforms: {
+                map: { type: "t", value: texture }
+            },
+            vertexShader: document.getElementById( 'vertexShader' ).textContent,
+            fragmentShader: document.getElementById( 'fragmentShader' ).textContent,
+            side: THREE.DoubleSide,
+            transparent: false,
+            attributes: { 'position': 0, 'offset': 1, 'orientation': 2, 'uv': 3 }
+
+        } );
+
+        var mesh = new THREE.Mesh( geometry, material );
+        mesh.frustumCulled = false;
+        scene.add( mesh );
+
+
+        if ( !renderer.supportsInstancedArrays ) {
+            document.getElementById( "notSupported" ).style.display = "";
+            return;
+        }
+
+        renderer.setClearColor( 0x101010 );
+        renderer.setPixelRatio( window.devicePixelRatio );
+        renderer.setSize( window.innerWidth, window.innerHeight );
+        container.appendChild( renderer.domElement );
+
+        stats = new Stats();
+        stats.domElement.style.position = 'absolute';
+        stats.domElement.style.top = '0px';
+        container.appendChild( stats.domElement );
+
+        window.addEventListener( 'resize', onWindowResize, false );
+
+    }
+
+    function onWindowResize( event ) {
+
+        camera.aspect = window.innerWidth / window.innerHeight;
+        camera.updateProjectionMatrix();
+
+        renderer.setSize( window.innerWidth, window.innerHeight );
+
+    }
+
+    //
+
+    function animate() {
+
+        requestAnimationFrame( animate );
+
+        render();
+        stats.update();
+
+    }
+
+    var lastTime = 0;
+
+    var moveQ = ( new THREE.Quaternion( .5, .5, .5, 0.0 ) ).normalize();
+    var tmpQ = new THREE.Quaternion();
+    var currentQ = new THREE.Quaternion();
+    function render() {
+
+        var time = performance.now();
+
+
+        var object = scene.children[0];
+
+        object.rotation.y = time * 0.00005;
+
+        renderer.render( scene, camera );
+
+        var delta = ( time - lastTime ) / 5000;
+        tmpQ.set( moveQ.x * delta, moveQ.y * delta, moveQ.z * delta, 1 ).normalize();
+
+        for ( var i = 0, ul = orientations.length / 4; i < ul; i++ ) {
+            var index = i * instanceBuffer.stride + orientations.offset;
+            currentQ.set( instanceBuffer.array[index], instanceBuffer.array[index + 1], instanceBuffer.array[index + 2], instanceBuffer.array[index + 3] );
+            currentQ.multiply( tmpQ );
+
+            orientations.setXYZW( i, currentQ.x, currentQ.y, currentQ.z, currentQ.w );
+
+        }
+        instanceBuffer.needsUpdate = true;
+        lastTime = time;
+    }
+
+    init();
+    animate();
+    </script>
+
+</body>
+
+
+
+</html>

+ 1 - 1
src/core/BufferGeometry.js

@@ -29,7 +29,7 @@ THREE.BufferGeometry.prototype = {
 
 	addAttribute: function ( name, attribute ) {
 
-		if ( attribute instanceof THREE.BufferAttribute === false ) {
+	    if ( attribute instanceof THREE.BufferAttribute === false && attribute instanceof THREE.InterleavedBufferAttribute === false ) {
 
 			THREE.warn( 'THREE.BufferGeometry: .addAttribute() now expects ( name, attribute ).' );
 

+ 1 - 1
src/core/InstancedBufferGeometry.js

@@ -7,7 +7,7 @@ THREE.InstancedBufferGeometry = function () {
     THREE.BufferGeometry.call( this );
 
     this.type = 'InstancedBufferGeometry';
-    this.maxInstancedCount = -1;
+    this.maxInstancedCount = undefined;
 
 };
 

+ 20 - 0
src/core/InstancedInterleavedBuffer.js

@@ -0,0 +1,20 @@
+/**
+ * @author benaadams / https://twitter.com/ben_a_adams
+ */
+
+THREE.InstancedInterleavedBuffer = function ( array, stride, dynamic, meshPerAttribute ) {
+
+    THREE.InterleavedBuffer.call( this, array, stride, dynamic );
+
+    this.meshPerAttribute = meshPerAttribute || 1;
+
+};
+
+THREE.InstancedInterleavedBuffer.prototype = Object.create( THREE.InterleavedBuffer.prototype );
+THREE.InstancedInterleavedBuffer.prototype.constructor = THREE.InstancedInterleavedBuffer;
+
+THREE.InstancedInterleavedBuffer.prototype.clone = function () {
+
+    return new THREE.InstancedInterleavedBuffer( new this.array.constructor( this.array ), this.stride, this.dynamic, this.meshPerAttribute );
+    
+};

+ 58 - 0
src/core/InterleavedBuffer.js

@@ -0,0 +1,58 @@
+/**
+ * @author benaadams / https://twitter.com/ben_a_adams
+ */
+
+THREE.InterleavedBuffer = function ( array, stride, dynamic ) {
+
+    this.array = array;
+    this.stride = stride;
+
+    this.needsUpdate = false;
+
+	this.dynamic = dynamic || false;
+	this.updateRange = { offset: 0, count: -1 };
+    
+};
+
+THREE.InterleavedBuffer.prototype = {
+
+    constructor: THREE.InterleavedBuffer,
+
+    get length () {
+
+        return this.array.length;
+
+    },
+
+    copyAt: function ( index1, attribute, index2 ) {
+
+        index1 *= this.stride;
+        index2 *= attribute.stride;
+
+        for ( var i = 0, l = this.stride; i < l; i++ ) {
+
+            this.array[ index1 + i ] = attribute.array[ index2 + i ];
+
+        }
+
+        return this;
+
+    },
+
+    set: function ( value, offset ) {
+
+        if ( offset === undefined ) offset = 0;
+
+        this.array.set( value, offset );
+
+        return this;
+
+    },
+
+    clone: function () {
+
+        return new THREE.InterleavedBuffer( new this.array.constructor( this.array ), this.stride, this.dynamic );
+
+    }
+
+};

+ 84 - 0
src/core/InterleavedBufferAttribute.js

@@ -0,0 +1,84 @@
+/**
+ * @author benaadams / https://twitter.com/ben_a_adams
+ */
+
+THREE.InterleavedBufferAttribute = function ( interleavedBuffer, itemSize, offset ) {
+
+    this.data = interleavedBuffer;
+    this.itemSize = itemSize;
+    this.offset = offset;
+
+};
+
+
+THREE.InterleavedBufferAttribute.prototype = {
+
+    constructor: THREE.InterleavedBufferAttribute,
+
+    get length() {
+
+        return this.itemSize * this.data.array.length / this.data.stride;
+
+    },
+    
+    setX: function ( index, x ) {
+
+        this.data.array[ index * this.data.stride + this.offset ] = x;
+
+        return this;
+
+    },
+
+    setY: function ( index, y ) {
+
+        this.data.array[ index * this.data.stride + this.offset + 1 ] = y;
+
+        return this;
+
+    },
+
+    setZ: function ( index, z ) {
+
+        this.data.array[ index * this.data.stride + this.offset + 2 ] = z;
+
+        return this;
+
+    },
+
+    setXY: function ( index, x, y ) {
+
+        index = index * this.data.stride + this.offset;
+
+        this.data.array[ index ] = x;
+        this.data.array[ index + 1 ] = y;
+
+        return this;
+
+    },
+
+    setXYZ: function ( index, x, y, z ) {
+
+        index = index * this.data.stride + this.offset;
+
+        this.data.array[ index ] = x;
+        this.data.array[ index + 1 ] = y;
+        this.data.array[ index + 2 ] = z;
+
+        return this;
+
+    },
+
+    setXYZW: function ( index, x, y, z, w ) {
+
+        index = index * this.data.stride + this.offset;
+
+        this.data.array[ index ] = x;
+        this.data.array[ index + 1 ] = y;
+        this.data.array[ index + 2 ] = z;
+        this.data.array[ index + 3 ] = w;
+
+        return this;
+
+    }
+
+};

+ 88 - 30
src/renderers/WebGLRenderer.js

@@ -2451,28 +2451,58 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 				if ( geometryAttribute !== undefined ) {
 
-					var size = geometryAttribute.itemSize;
+				    var size = geometryAttribute.itemSize;
+					state.enableAttribute( programAttribute );
 
-					_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryAttribute.buffer );
+					if ( geometryAttribute instanceof THREE.InterleavedBufferAttribute ) {
+                        
+					    var data = geometryAttribute.data;
+					    var stride = data.stride;
+					    var offset = geometryAttribute.offset;
 
-					state.enableAttribute( programAttribute );
+					    _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryAttribute.data.buffer );
+					    _gl.vertexAttribPointer( programAttribute, size, _gl.FLOAT, false, stride * data.array.BYTES_PER_ELEMENT, ( startIndex * stride + offset ) * data.array.BYTES_PER_ELEMENT ); 
 
-					_gl.vertexAttribPointer( programAttribute, size, _gl.FLOAT, false, 0, startIndex * size * 4 ); // 4 bytes per Float32
+					    if ( data instanceof THREE.InstancedInterleavedBuffer ) {
 
-					if ( geometryAttribute instanceof THREE.InstancedBufferAttribute ) {
+					        if ( extension === null ) {
 
-					    if ( extension === null ) {
+					            THREE.error( 'THREE.WebGLRenderer.setupVertexAttributes: using THREE.InstancedBufferAttribute but hardware does not support extension ANGLE_instanced_arrays.' );
+					            return;
 
-					        THREE.error( 'THREE.WebGLRenderer.setupVertexAttributes: using THREE.InstancedBufferAttribute but hardware does not support extension ANGLE_instanced_arrays.' );
-					        return;
+					        }
+
+					        extension.vertexAttribDivisorANGLE( programAttribute, data.meshPerAttribute );
+
+					        if ( geometry.maxInstancedCount === undefined ) {
+
+					            geometry.maxInstancedCount = data.meshPerAttribute * ( data.array.length / data.stride );
+
+					        }
 
 					    }
 
-					    extension.vertexAttribDivisorANGLE( programAttribute, geometryAttribute.meshPerAttribute );
+					} else {
+
+					    _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryAttribute.buffer );
+					    _gl.vertexAttribPointer( programAttribute, size, _gl.FLOAT, false, 0, startIndex * size * 4 ); // 4 bytes per Float32
+
+					    if ( geometryAttribute instanceof THREE.InstancedBufferAttribute ) {
+
+					        if ( extension === null ) {
+
+					            THREE.error( 'THREE.WebGLRenderer.setupVertexAttributes: using THREE.InstancedBufferAttribute but hardware does not support extension ANGLE_instanced_arrays.' );
+					            return;
+
+					        }
+
+					        extension.vertexAttribDivisorANGLE( programAttribute, geometryAttribute.meshPerAttribute );
 
-					    if ( geometry.maxInstancedCount === -1 ) {
+					        if ( geometry.maxInstancedCount === undefined ) {
 
-					        geometry.maxInstancedCount = geometryAttribute.meshPerAttribute * ( geometryAttribute.array.length / geometryAttribute.itemSize );
+					            geometry.maxInstancedCount = geometryAttribute.meshPerAttribute * ( geometryAttribute.array.length / geometryAttribute.itemSize );
+
+					        }
 
 					    }
 
@@ -2562,7 +2592,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 					}
 
-					if ( geometry instanceof THREE.InstancedBufferGeometry ) {
+					if ( geometry instanceof THREE.InstancedBufferGeometry && geometry.maxInstancedCount > 0 ) {
 
 					    var extension = extensions.get( 'ANGLE_instanced_arrays' );
 
@@ -2605,7 +2635,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 						// render indexed triangles
                         
-						if ( geometry instanceof THREE.InstancedBufferGeometry ) {
+						if ( geometry instanceof THREE.InstancedBufferGeometry && offsets[i].instances > 0 ) {
 
 						    var extension = extensions.get( 'ANGLE_instanced_arrays' );
 
@@ -2645,7 +2675,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 				// render non-indexed triangles
                 
-				if ( geometry instanceof THREE.InstancedBufferGeometry ) {
+				if ( geometry instanceof THREE.InstancedBufferGeometry && geometry.maxInstancedCount > 0 ) {
 
 				    var extension = extensions.get( 'ANGLE_instanced_arrays' );
 
@@ -2656,12 +2686,27 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 				    }
 
-				    extension.drawArraysInstancedANGLE( mode, 0, position.array.length / position.itemSize, geometry.maxInstancedCount ); // Draw the instanced meshes
+				    if ( position instanceof THREE.InterleavedBufferAttribute ) {
+
+				        extension.drawArraysInstancedANGLE( mode, 0, position.data.array.length / position.data.stride, geometry.maxInstancedCount ); // Draw the instanced meshes
+
+				    } else {
+
+				        extension.drawArraysInstancedANGLE( mode, 0, position.array.length / position.itemSize, geometry.maxInstancedCount ); // Draw the instanced meshes
+
+				    }
 
 				} else {
 
-				    _gl.drawArrays( mode, 0, position.array.length / position.itemSize );
+				    
+				    if ( position instanceof THREE.InterleavedBufferAttribute ) {
+
+				        _gl.drawArrays( mode, 0, position.data.array.length / position.data.stride );
+
+				    } else {
 
+				        _gl.drawArrays( mode, 0, position.array.length / position.itemSize );
+				    }
 				}
 
 				_this.info.render.calls ++;
@@ -4001,36 +4046,49 @@ THREE.WebGLRenderer = function ( parameters ) {
 				var attribute = attributes[ key ];
 				var bufferType = ( key === 'index' ) ? _gl.ELEMENT_ARRAY_BUFFER : _gl.ARRAY_BUFFER;
 
-				if ( attribute.buffer === undefined ) {
+                var data = ( attribute instanceof THREE.InterleavedBufferAttribute ) ? attribute.data : attribute;
 
-					attribute.buffer = _gl.createBuffer();
-					_gl.bindBuffer( bufferType, attribute.buffer );
-					_gl.bufferData( bufferType, attribute.array, ( attribute instanceof THREE.DynamicBufferAttribute && !( attribute instanceof THREE.InstancedBufferAttribute && attribute.dynamic === false ) ) ? _gl.DYNAMIC_DRAW : _gl.STATIC_DRAW );
+                if ( data.buffer === undefined ) {
+
+                    data.buffer = _gl.createBuffer();
+                    _gl.bindBuffer( bufferType, data.buffer );
+
+					var usage = _gl.STATIC_DRAW;
+					if ( data instanceof THREE.DynamicBufferAttribute
+                         || ( data instanceof THREE.InstancedBufferAttribute && data.dynamic === true )
+                         || ( data instanceof THREE.InterleavedBuffer && data.dynamic === true )
+                    ) {
+
+					    usage = _gl.DYNAMIC_DRAW;
+
+                    }
+
+					_gl.bufferData( bufferType, data.array, usage );
 
-					attribute.needsUpdate = false;
+					data.needsUpdate = false;
 
-				} else if ( attribute.needsUpdate === true ) {
+                } else if ( data.needsUpdate === true ) {
 
-					_gl.bindBuffer( bufferType, attribute.buffer );
+                    _gl.bindBuffer( bufferType, data.buffer );
 
-					if ( attribute.updateRange === undefined || attribute.updateRange.count === -1 ) { // Not using update ranges
+                    if ( data.updateRange === undefined || data.updateRange.count === -1 ) { // Not using update ranges
 
-						_gl.bufferSubData( bufferType, 0, attribute.array );
+					    _gl.bufferSubData( bufferType, 0, data.array );
 
-					} else if ( attribute.updateRange.count === 0 ) {
+                    } else if ( data.updateRange.count === 0 ) {
 
 						console.error( 'THREE.WebGLRenderer.updateObject: using updateRange for THREE.DynamicBufferAttribute and marked as needsUpdate but count is 0, ensure you are using set methods or updating manually.' );
 
 					} else {
 
-						_gl.bufferSubData( bufferType, attribute.updateRange.offset * attribute.array.BYTES_PER_ELEMENT,
-										   attribute.array.subarray( attribute.updateRange.offset, attribute.updateRange.offset + attribute.updateRange.count ) );
+					    _gl.bufferSubData( bufferType, data.updateRange.offset * data.array.BYTES_PER_ELEMENT,
+										   data.array.subarray( data.updateRange.offset, data.updateRange.offset + data.updateRange.count ) );
 
-						attribute.updateRange.count = 0; // reset range
+					    data.updateRange.count = 0; // reset range
 
 					}
 
-					attribute.needsUpdate = false;
+                    data.needsUpdate = false;
 
 				}
 

+ 3 - 0
utils/build/includes/common.json

@@ -27,6 +27,9 @@
 	"src/core/BufferAttribute.js",
 	"src/core/DynamicBufferAttribute.js",
 	"src/core/InstancedBufferAttribute.js",
+	"src/core/InterleavedBuffer.js",
+	"src/core/InstancedInterleavedBuffer.js",
+	"src/core/InterleavedBufferAttribute.js",
 	"src/core/Geometry.js",
 	"src/core/BufferGeometry.js",
 	"src/core/InstancedBufferGeometry.js",