From dca2f612d3dfe83be3b02482ee8babcfdf3c8a0f Mon Sep 17 00:00:00 2001 From: Thomas Rudin Date: Sat, 15 Feb 2020 11:23:29 +0100 Subject: [PATCH] use instanced rendering --- static/js/lib/BufferGeometryUtils.js | 724 --------------------------- static/js/test.js | 18 +- static/test.html | 1 - 3 files changed, 10 insertions(+), 733 deletions(-) delete mode 100644 static/js/lib/BufferGeometryUtils.js diff --git a/static/js/lib/BufferGeometryUtils.js b/static/js/lib/BufferGeometryUtils.js deleted file mode 100644 index c24d7fa..0000000 --- a/static/js/lib/BufferGeometryUtils.js +++ /dev/null @@ -1,724 +0,0 @@ -/** - * @author mrdoob / http://mrdoob.com/ - */ - -THREE.BufferGeometryUtils = { - - computeTangents: function ( geometry ) { - - var index = geometry.index; - var attributes = geometry.attributes; - - // based on http://www.terathon.com/code/tangent.html - // (per vertex tangents) - - if ( index === null || - attributes.position === undefined || - attributes.normal === undefined || - attributes.uv === undefined ) { - - console.warn( 'THREE.BufferGeometry: Missing required attributes (index, position, normal or uv) in BufferGeometry.computeTangents()' ); - return; - - } - - var indices = index.array; - var positions = attributes.position.array; - var normals = attributes.normal.array; - var uvs = attributes.uv.array; - - var nVertices = positions.length / 3; - - if ( attributes.tangent === undefined ) { - - geometry.setAttribute( 'tangent', new THREE.BufferAttribute( new Float32Array( 4 * nVertices ), 4 ) ); - - } - - var tangents = attributes.tangent.array; - - var tan1 = [], tan2 = []; - - for ( var i = 0; i < nVertices; i ++ ) { - - tan1[ i ] = new THREE.Vector3(); - tan2[ i ] = new THREE.Vector3(); - - } - - var vA = new THREE.Vector3(), - vB = new THREE.Vector3(), - vC = new THREE.Vector3(), - - uvA = new THREE.Vector2(), - uvB = new THREE.Vector2(), - uvC = new THREE.Vector2(), - - sdir = new THREE.Vector3(), - tdir = new THREE.Vector3(); - - function handleTriangle( a, b, c ) { - - vA.fromArray( positions, a * 3 ); - vB.fromArray( positions, b * 3 ); - vC.fromArray( positions, c * 3 ); - - uvA.fromArray( uvs, a * 2 ); - uvB.fromArray( uvs, b * 2 ); - uvC.fromArray( uvs, c * 2 ); - - vB.sub( vA ); - vC.sub( vA ); - - uvB.sub( uvA ); - uvC.sub( uvA ); - - var r = 1.0 / ( uvB.x * uvC.y - uvC.x * uvB.y ); - - // silently ignore degenerate uv triangles having coincident or colinear vertices - - if ( ! isFinite( r ) ) return; - - sdir.copy( vB ).multiplyScalar( uvC.y ).addScaledVector( vC, - uvB.y ).multiplyScalar( r ); - tdir.copy( vC ).multiplyScalar( uvB.x ).addScaledVector( vB, - uvC.x ).multiplyScalar( r ); - - tan1[ a ].add( sdir ); - tan1[ b ].add( sdir ); - tan1[ c ].add( sdir ); - - tan2[ a ].add( tdir ); - tan2[ b ].add( tdir ); - tan2[ c ].add( tdir ); - - } - - var groups = geometry.groups; - - if ( groups.length === 0 ) { - - groups = [ { - start: 0, - count: indices.length - } ]; - - } - - for ( var i = 0, il = groups.length; i < il; ++ i ) { - - var group = groups[ i ]; - - var start = group.start; - var count = group.count; - - for ( var j = start, jl = start + count; j < jl; j += 3 ) { - - handleTriangle( - indices[ j + 0 ], - indices[ j + 1 ], - indices[ j + 2 ] - ); - - } - - } - - var tmp = new THREE.Vector3(), tmp2 = new THREE.Vector3(); - var n = new THREE.Vector3(), n2 = new THREE.Vector3(); - var w, t, test; - - function handleVertex( v ) { - - n.fromArray( normals, v * 3 ); - n2.copy( n ); - - t = tan1[ v ]; - - // Gram-Schmidt orthogonalize - - tmp.copy( t ); - tmp.sub( n.multiplyScalar( n.dot( t ) ) ).normalize(); - - // Calculate handedness - - tmp2.crossVectors( n2, t ); - test = tmp2.dot( tan2[ v ] ); - w = ( test < 0.0 ) ? - 1.0 : 1.0; - - tangents[ v * 4 ] = tmp.x; - tangents[ v * 4 + 1 ] = tmp.y; - tangents[ v * 4 + 2 ] = tmp.z; - tangents[ v * 4 + 3 ] = w; - - } - - for ( var i = 0, il = groups.length; i < il; ++ i ) { - - var group = groups[ i ]; - - var start = group.start; - var count = group.count; - - for ( var j = start, jl = start + count; j < jl; j += 3 ) { - - handleVertex( indices[ j + 0 ] ); - handleVertex( indices[ j + 1 ] ); - handleVertex( indices[ j + 2 ] ); - - } - - } - - }, - - /** - * @param {Array} geometries - * @param {Boolean} useGroups - * @return {THREE.BufferGeometry} - */ - mergeBufferGeometries: function ( geometries, useGroups ) { - - var isIndexed = geometries[ 0 ].index !== null; - - var attributesUsed = new Set( Object.keys( geometries[ 0 ].attributes ) ); - var morphAttributesUsed = new Set( Object.keys( geometries[ 0 ].morphAttributes ) ); - - var attributes = {}; - var morphAttributes = {}; - - var morphTargetsRelative = geometries[ 0 ].morphTargetsRelative; - - var mergedGeometry = new THREE.BufferGeometry(); - - var offset = 0; - - for ( var i = 0; i < geometries.length; ++ i ) { - - var geometry = geometries[ i ]; - - // ensure that all geometries are indexed, or none - - if ( isIndexed !== ( geometry.index !== null ) ) return null; - - // gather attributes, exit early if they're different - - for ( var name in geometry.attributes ) { - - if ( ! attributesUsed.has( name ) ) return null; - - if ( attributes[ name ] === undefined ) attributes[ name ] = []; - - attributes[ name ].push( geometry.attributes[ name ] ); - - } - - // gather morph attributes, exit early if they're different - - if ( morphTargetsRelative !== geometry.morphTargetsRelative ) return null; - - for ( var name in geometry.morphAttributes ) { - - if ( ! morphAttributesUsed.has( name ) ) return null; - - if ( morphAttributes[ name ] === undefined ) morphAttributes[ name ] = []; - - morphAttributes[ name ].push( geometry.morphAttributes[ name ] ); - - } - - // gather .userData - - mergedGeometry.userData.mergedUserData = mergedGeometry.userData.mergedUserData || []; - mergedGeometry.userData.mergedUserData.push( geometry.userData ); - - if ( useGroups ) { - - var count; - - if ( isIndexed ) { - - count = geometry.index.count; - - } else if ( geometry.attributes.position !== undefined ) { - - count = geometry.attributes.position.count; - - } else { - - return null; - - } - - mergedGeometry.addGroup( offset, count, i ); - - offset += count; - - } - - } - - // merge indices - - if ( isIndexed ) { - - var indexOffset = 0; - var mergedIndex = []; - - for ( var i = 0; i < geometries.length; ++ i ) { - - var index = geometries[ i ].index; - - for ( var j = 0; j < index.count; ++ j ) { - - mergedIndex.push( index.getX( j ) + indexOffset ); - - } - - indexOffset += geometries[ i ].attributes.position.count; - - } - - mergedGeometry.setIndex( mergedIndex ); - - } - - // merge attributes - - for ( var name in attributes ) { - - var mergedAttribute = this.mergeBufferAttributes( attributes[ name ] ); - - if ( ! mergedAttribute ) return null; - - mergedGeometry.setAttribute( name, mergedAttribute ); - - } - - // merge morph attributes - - for ( var name in morphAttributes ) { - - var numMorphTargets = morphAttributes[ name ][ 0 ].length; - - if ( numMorphTargets === 0 ) break; - - mergedGeometry.morphAttributes = mergedGeometry.morphAttributes || {}; - mergedGeometry.morphAttributes[ name ] = []; - - for ( var i = 0; i < numMorphTargets; ++ i ) { - - var morphAttributesToMerge = []; - - for ( var j = 0; j < morphAttributes[ name ].length; ++ j ) { - - morphAttributesToMerge.push( morphAttributes[ name ][ j ][ i ] ); - - } - - var mergedMorphAttribute = this.mergeBufferAttributes( morphAttributesToMerge ); - - if ( ! mergedMorphAttribute ) return null; - - mergedGeometry.morphAttributes[ name ].push( mergedMorphAttribute ); - - } - - } - - return mergedGeometry; - - }, - - /** - * @param {Array} attributes - * @return {THREE.BufferAttribute} - */ - mergeBufferAttributes: function ( attributes ) { - - var TypedArray; - var itemSize; - var normalized; - var arrayLength = 0; - - for ( var i = 0; i < attributes.length; ++ i ) { - - var attribute = attributes[ i ]; - - if ( attribute.isInterleavedBufferAttribute ) return null; - - if ( TypedArray === undefined ) TypedArray = attribute.array.constructor; - if ( TypedArray !== attribute.array.constructor ) return null; - - if ( itemSize === undefined ) itemSize = attribute.itemSize; - if ( itemSize !== attribute.itemSize ) return null; - - if ( normalized === undefined ) normalized = attribute.normalized; - if ( normalized !== attribute.normalized ) return null; - - arrayLength += attribute.array.length; - - } - - var array = new TypedArray( arrayLength ); - var offset = 0; - - for ( var i = 0; i < attributes.length; ++ i ) { - - array.set( attributes[ i ].array, offset ); - - offset += attributes[ i ].array.length; - - } - - return new THREE.BufferAttribute( array, itemSize, normalized ); - - }, - - /** - * @param {Array} attributes - * @return {Array} - */ - interleaveAttributes: function ( attributes ) { - - // Interleaves the provided attributes into an InterleavedBuffer and returns - // a set of InterleavedBufferAttributes for each attribute - var TypedArray; - var arrayLength = 0; - var stride = 0; - - // calculate the the length and type of the interleavedBuffer - for ( var i = 0, l = attributes.length; i < l; ++ i ) { - - var attribute = attributes[ i ]; - - if ( TypedArray === undefined ) TypedArray = attribute.array.constructor; - if ( TypedArray !== attribute.array.constructor ) { - - console.warn( 'AttributeBuffers of different types cannot be interleaved' ); - return null; - - } - - arrayLength += attribute.array.length; - stride += attribute.itemSize; - - } - - // Create the set of buffer attributes - var interleavedBuffer = new THREE.InterleavedBuffer( new TypedArray( arrayLength ), stride ); - var offset = 0; - var res = []; - var getters = [ 'getX', 'getY', 'getZ', 'getW' ]; - var setters = [ 'setX', 'setY', 'setZ', 'setW' ]; - - for ( var j = 0, l = attributes.length; j < l; j ++ ) { - - var attribute = attributes[ j ]; - var itemSize = attribute.itemSize; - var count = attribute.count; - var iba = new THREE.InterleavedBufferAttribute( interleavedBuffer, itemSize, offset, attribute.normalized ); - res.push( iba ); - - offset += itemSize; - - // Move the data for each attribute into the new interleavedBuffer - // at the appropriate offset - for ( var c = 0; c < count; c ++ ) { - - for ( var k = 0; k < itemSize; k ++ ) { - - iba[ setters[ k ] ]( c, attribute[ getters[ k ] ]( c ) ); - - } - - } - - } - - return res; - - }, - - /** - * @param {Array} geometry - * @return {number} - */ - estimateBytesUsed: function ( geometry ) { - - // Return the estimated memory used by this geometry in bytes - // Calculate using itemSize, count, and BYTES_PER_ELEMENT to account - // for InterleavedBufferAttributes. - var mem = 0; - for ( var name in geometry.attributes ) { - - var attr = geometry.getAttribute( name ); - mem += attr.count * attr.itemSize * attr.array.BYTES_PER_ELEMENT; - - } - - var indices = geometry.getIndex(); - mem += indices ? indices.count * indices.itemSize * indices.array.BYTES_PER_ELEMENT : 0; - return mem; - - }, - - /** - * @param {THREE.BufferGeometry} geometry - * @param {number} tolerance - * @return {THREE.BufferGeometry>} - */ - mergeVertices: function ( geometry, tolerance = 1e-4 ) { - - tolerance = Math.max( tolerance, Number.EPSILON ); - - // Generate an index buffer if the geometry doesn't have one, or optimize it - // if it's already available. - var hashToIndex = {}; - var indices = geometry.getIndex(); - var positions = geometry.getAttribute( 'position' ); - var vertexCount = indices ? indices.count : positions.count; - - // next value for triangle indices - var nextIndex = 0; - - // attributes and new attribute arrays - var attributeNames = Object.keys( geometry.attributes ); - var attrArrays = {}; - var morphAttrsArrays = {}; - var newIndices = []; - var getters = [ 'getX', 'getY', 'getZ', 'getW' ]; - - // initialize the arrays - for ( var i = 0, l = attributeNames.length; i < l; i ++ ) { - - var name = attributeNames[ i ]; - - attrArrays[ name ] = []; - - var morphAttr = geometry.morphAttributes[ name ]; - if ( morphAttr ) { - - morphAttrsArrays[ name ] = new Array( morphAttr.length ).fill().map( () => [] ); - - } - - } - - // convert the error tolerance to an amount of decimal places to truncate to - var decimalShift = Math.log10( 1 / tolerance ); - var shiftMultiplier = Math.pow( 10, decimalShift ); - for ( var i = 0; i < vertexCount; i ++ ) { - - var index = indices ? indices.getX( i ) : i; - - // Generate a hash for the vertex attributes at the current index 'i' - var hash = ''; - for ( var j = 0, l = attributeNames.length; j < l; j ++ ) { - - var name = attributeNames[ j ]; - var attribute = geometry.getAttribute( name ); - var itemSize = attribute.itemSize; - - for ( var k = 0; k < itemSize; k ++ ) { - - // double tilde truncates the decimal value - hash += `${ ~ ~ ( attribute[ getters[ k ] ]( index ) * shiftMultiplier ) },`; - - } - - } - - // Add another reference to the vertex if it's already - // used by another index - if ( hash in hashToIndex ) { - - newIndices.push( hashToIndex[ hash ] ); - - } else { - - // copy data to the new index in the attribute arrays - for ( var j = 0, l = attributeNames.length; j < l; j ++ ) { - - var name = attributeNames[ j ]; - var attribute = geometry.getAttribute( name ); - var morphAttr = geometry.morphAttributes[ name ]; - var itemSize = attribute.itemSize; - var newarray = attrArrays[ name ]; - var newMorphArrays = morphAttrsArrays[ name ]; - - for ( var k = 0; k < itemSize; k ++ ) { - - var getterFunc = getters[ k ]; - newarray.push( attribute[ getterFunc ]( index ) ); - - if ( morphAttr ) { - - for ( var m = 0, ml = morphAttr.length; m < ml; m ++ ) { - - newMorphArrays[ m ].push( morphAttr[ m ][ getterFunc ]( index ) ); - - } - - } - - } - - } - - hashToIndex[ hash ] = nextIndex; - newIndices.push( nextIndex ); - nextIndex ++; - - } - - } - - // Generate typed arrays from new attribute arrays and update - // the attributeBuffers - const result = geometry.clone(); - for ( var i = 0, l = attributeNames.length; i < l; i ++ ) { - - var name = attributeNames[ i ]; - var oldAttribute = geometry.getAttribute( name ); - - var buffer = new oldAttribute.array.constructor( attrArrays[ name ] ); - var attribute = new THREE.BufferAttribute( buffer, oldAttribute.itemSize, oldAttribute.normalized ); - - result.setAttribute( name, attribute ); - - // Update the attribute arrays - if ( name in morphAttrsArrays ) { - - for ( var j = 0; j < morphAttrsArrays[ name ].length; j ++ ) { - - var oldMorphAttribute = geometry.morphAttributes[ name ][ j ]; - - var buffer = new oldMorphAttribute.array.constructor( morphAttrsArrays[ name ][ j ] ); - var morphAttribute = new THREE.BufferAttribute( buffer, oldMorphAttribute.itemSize, oldMorphAttribute.normalized ); - result.morphAttributes[ name ][ j ] = morphAttribute; - - } - - } - - } - - // indices - - result.setIndex( newIndices ); - - return result; - - }, - - /** - * @param {THREE.BufferGeometry} geometry - * @param {number} drawMode - * @return {THREE.BufferGeometry>} - */ - toTrianglesDrawMode: function ( geometry, drawMode ) { - - if ( drawMode === THREE.TrianglesDrawMode ) { - - console.warn( 'THREE.BufferGeometryUtils.toTrianglesDrawMode(): Geometry already defined as triangles.' ); - return geometry; - - } - - if ( drawMode === THREE.TriangleFanDrawMode || drawMode === THREE.TriangleStripDrawMode ) { - - var index = geometry.getIndex(); - - // generate index if not present - - if ( index === null ) { - - var indices = []; - - var position = geometry.getAttribute( 'position' ); - - if ( position !== undefined ) { - - for ( var i = 0; i < position.count; i ++ ) { - - indices.push( i ); - - } - - geometry.setIndex( indices ); - index = geometry.getIndex(); - - } else { - - console.error( 'THREE.BufferGeometryUtils.toTrianglesDrawMode(): Undefined position attribute. Processing not possible.' ); - return geometry; - - } - - } - - // - - var numberOfTriangles = index.count - 2; - var newIndices = []; - - if ( drawMode === THREE.TriangleFanDrawMode ) { - - // gl.TRIANGLE_FAN - - for ( var i = 1; i <= numberOfTriangles; i ++ ) { - - newIndices.push( index.getX( 0 ) ); - newIndices.push( index.getX( i ) ); - newIndices.push( index.getX( i + 1 ) ); - - } - - } else { - - // gl.TRIANGLE_STRIP - - for ( var i = 0; i < numberOfTriangles; i ++ ) { - - if ( i % 2 === 0 ) { - - newIndices.push( index.getX( i ) ); - newIndices.push( index.getX( i + 1 ) ); - newIndices.push( index.getX( i + 2 ) ); - - - } else { - - newIndices.push( index.getX( i + 2 ) ); - newIndices.push( index.getX( i + 1 ) ); - newIndices.push( index.getX( i ) ); - - } - - } - - } - - if ( ( newIndices.length / 3 ) !== numberOfTriangles ) { - - console.error( 'THREE.BufferGeometryUtils.toTrianglesDrawMode(): Unable to generate correct amount of triangles.' ); - - } - - // build final geometry - - var newGeometry = geometry.clone(); - newGeometry.setIndex( newIndices ); - newGeometry.clearGroups(); - - return newGeometry; - - } else { - - console.error( 'THREE.BufferGeometryUtils.toTrianglesDrawMode(): Unknown draw mode:', drawMode ); - return geometry; - - } - - } - -}; diff --git a/static/js/test.js b/static/js/test.js index dd186b8..ffc264b 100644 --- a/static/js/test.js +++ b/static/js/test.js @@ -1,6 +1,4 @@ var camera, scene, renderer; -var geometry = new THREE.BufferGeometry() - .fromGeometry(new THREE.BoxGeometry(1,1,1)); init(); animate(); @@ -84,14 +82,12 @@ function drawMapblock(posx,posy,posz){ var contentId = mapblock.contentid[i]; var nodeName = mapblock.blockmapping[contentId]; - var geo = geometry.clone(); var matrix = new THREE.Matrix4() .makeTranslation( x + (posx*16), y + (posy*16), z + (posz*16) ); - geo.applyMatrix4(matrix); var list = nodenameGeometriesMap[nodeName]; if (!list){ @@ -99,7 +95,7 @@ function drawMapblock(posx,posy,posz){ nodenameGeometriesMap[nodeName] = list; } - list.push(geo); + list.push(matrix); } } } @@ -108,8 +104,13 @@ function drawMapblock(posx,posy,posz){ var material = getMaterial(nodeName); if (material){ - var list = THREE.BufferGeometryUtils.mergeBufferGeometries(nodenameGeometriesMap[nodeName]); - var mesh = new THREE.Mesh(list, material); + var list = nodenameGeometriesMap[nodeName]; + var geometry = new THREE.BoxGeometry(1,1,1); + var mesh = new THREE.InstancedMesh( geometry, material, list.length ); + for (var i=0; i -