diff --git a/src/BulletCollision/BroadphaseCollision/Dbvt.js b/src/BulletCollision/BroadphaseCollision/Dbvt.js index 69bc202..166d099 100644 --- a/src/BulletCollision/BroadphaseCollision/Dbvt.js +++ b/src/BulletCollision/BroadphaseCollision/Dbvt.js @@ -10,6 +10,58 @@ (function( window, Bump ) { + var createGetter = function( Type, pool ) { + return function() { + return pool.pop() || Type.create(); + }; + }; + + var createDeller = function( pool ) { + return function() { + for ( var i = 0; i < arguments.length; ++i ) { + pool.push( arguments[i] ); + } + }; + }; + + // memory pool for recycling Vector3 objects + var vector3Pool = []; + + var getVector3 = createGetter( Bump.Vector3, vector3Pool ); + var delVector3 = createDeller( vector3Pool ); + + // memory pool for recycling arrays + var arrayPool = []; + + var getArray = function() { + return arrayPool.pop() || []; + }; + var delArray = function( arr ) { + arr.length = 0; + arrayPool.push( arr ); + }; + + // memory pool for recycling arrays for the rayTest function + var rayTestStackPool = []; + var getRayTestStackArray = function( length ) { + return rayTestStackPool.pop() || new Array( length ); + }; + var delRayTestStackArray = function( arr ) { + rayTestStackPool.push( arr ); + }; + + var scalarRefPool = []; + var getScalarRef = function( init ) { + var ref = scalarRefPool.pop(); + if ( ref ) { + ref.value = init; + return ref; + } + + return { value: init }; + }; + var delScalarRef = createDeller( scalarRefPool ); + // Used in collideTV var tmpCTVVol1; @@ -104,34 +156,34 @@ SignedExpand: function( e ) { if ( e.x > 0 ) { this.mx.x += e.x; - } - else{ + } else { this.mi.x += e.x; } + if ( e.y > 0 ) { this.mx.y += e.y; - } - else{ + } else { this.mi.y += e.y; } + if ( e.z > 0 ) { this.mx.z += e.z; - } - else{ + } else { this.mi.z += e.z; } + return this; }, // Given `DbvtAabbMm` `a`, return true if `a`s bounding box is contained // within `this` bounding box. Contain: function( a ) { - return( ( this.mi.x <= a.mi.x ) && + return (( this.mi.x <= a.mi.x ) && ( this.mi.y <= a.mi.y ) && ( this.mi.z <= a.mi.z ) && ( this.mx.x >= a.mx.x ) && ( this.mx.y >= a.mx.y ) && - ( this.mx.z >= a.mx.z ) ); + ( this.mx.z >= a.mx.z )); }, // Classify `this` DbvtAabbMm based on which side of a specified plane it @@ -151,8 +203,8 @@ // Note: There seems to be a bug here, in which intersections are not // reliably detected... Classify: function( n, o, s ) { - var pi = Bump.Vector3.create(), - px = Bump.Vector3.create(); + var pi = Bump.Vector3.create(); + var px = Bump.Vector3.create(); switch ( s ) { case ( 0 + 0 + 0 ): @@ -220,24 +272,23 @@ if ( d.x < 0 ) { span.smi += this.mx.x * d.x; span.smx += this.mi.x * d.x; - } - else{ + } else { span.smi += this.mi.x * d.x; span.smx += this.mx.x * d.x; } + if ( d.y < 0 ) { span.smi += this.mx.y * d.y; span.smx += this.mi.y * d.y; - } - else{ + } else { span.smi += this.mi.y * d.y; span.smx += this.mx.y * d.y; } + if ( d.z < 0 ) { span.smi += this.mx.z * d.z; span.smx += this.mi.z * d.z; - } - else{ + } else { span.smi += this.mi.z * d.z; span.smx += this.mx.z * d.z; } @@ -293,23 +344,23 @@ // Intersect test for 2 `DbvtAabbMm`s Bump.Intersect.DbvtAabbMm2 = function( a, b ) { - return ( ( a.mi.x <= b.mx.x ) && - ( a.mx.x >= b.mi.x ) && - ( a.mi.y <= b.mx.y ) && - ( a.mx.y >= b.mi.y ) && - ( a.mi.z <= b.mx.z ) && - ( a.mx.z >= b.mi.z ) ); + return (( a.mi.x <= b.mx.x ) && + ( a.mx.x >= b.mi.x ) && + ( a.mi.y <= b.mx.y ) && + ( a.mx.y >= b.mi.y ) && + ( a.mi.z <= b.mx.z ) && + ( a.mx.z >= b.mi.z )); }; Bump.Intersect.DbvtVolume2 = Bump.Intersect.DbvtAabbMm2; // typedef consistency // Intersect test for `DbvtAabbMm` `a` and `Vector3` `b` Bump.Intersect.DbvtAabbMm.Vector3 = function( a, b ) { - return ( ( b.x >= a.mi.x ) && - ( b.y >= a.mi.y ) && - ( b.z >= a.mi.z ) && - ( b.x <= a.mx.x ) && - ( b.y <= a.mx.y ) && - ( b.z <= a.mx.z ) ); + return (( b.x >= a.mi.x ) && + ( b.y >= a.mi.y ) && + ( b.z >= a.mi.z ) && + ( b.x <= a.mx.x ) && + ( b.y <= a.mx.y ) && + ( b.z <= a.mx.z )); }; Bump.Proximity = {}; @@ -340,40 +391,37 @@ // unrolled to avoid array-like vector access (for speed) if ( a.mi.x < b.mi.x ) { r.mi.x = a.mi.x; - } - else { + } else { r.mi.x = b.mi.x; } + if ( a.mx.x > b.mx.x ) { r.mx.x = a.mx.x; - } - else { + } else { r.mx.x = b.mx.x; } if ( a.mi.y < b.mi.y ) { r.mi.y = a.mi.y; - } - else { + } else { r.mi.y = b.mi.y; } + if ( a.mx.y > b.mx.y ) { r.mx.y = a.mx.y; - } - else { + } else { r.mx.y = b.mx.y; } if ( a.mi.z < b.mi.z ) { r.mi.z = a.mi.z; - } - else { + } else { r.mi.z = b.mi.z; } + if ( a.mx.z > b.mx.z ) { r.mx.z = a.mx.z; - } - else { + } else { r.mx.z = b.mx.z; } }; @@ -444,35 +492,34 @@ // static DBVT_INLINE int indexof(const btDbvtNode* node) var indexof = function( node ) { return node.parent.childs[ 1 ] === node ? 1 : 0; - }, + }; - merge = function( a, b ) { + var merge = function( a, b ) { var res = Bump.DbvtVolume.create(); Bump.Merge.DbvtVolume3( a, b, res ); return res; - }, + }; - size = function( a ) { + var size = function( a ) { var edges = a.Lengths(); return edges.x * edges.y * edges.z + edges.x + edges.y + edges.z; - }, + }; - getmaxdepth = function( node, depth, maxdepthRef ) { + var getmaxdepth = function( node, depth, maxdepthRef ) { if ( node.isinternal() ) { getmaxdepth( node.childs[ 0 ], depth + 1, maxdepthRef ); getmaxdepth( node.childs[ 1 ], depth + 1, maxdepthRef ); - } - else { + } else { maxdepthRef.value = Math.max( maxdepthRef.value, depth ); } - }, + }; - deletenode = function( pdbvt, node ) { + var deletenode = function( pdbvt, node ) { // btAlignedFree(pdbvt->free); pdbvt.free = node; - }, + }; - recursedeletenode = function( pdbvt, node ) { + var recursedeletenode = function( pdbvt, node ) { if ( node.isleaf() ) { recursedeletenode( pdbvt, node.childs[ 0 ] ); recursedeletenode( pdbvt, node.childs[ 1 ] ); @@ -483,10 +530,10 @@ } deletenode( pdbvt, node ); - }, + }; // overloaded createnode functions have been renamed - createnodeTreeParentData = function( pdbvt, parent, data ) { + var createnodeTreeParentData = function( pdbvt, parent, data ) { var node; if ( pdbvt.free ) { node = pdbvt.free; @@ -499,21 +546,21 @@ node.data = data; node.childs[ 1 ] = 0; // redundant? return node; - }, + }; - createnodeTreeParentVolumeData = function( pdbvt, parent, volume, data ) { + var createnodeTreeParentVolumeData = function( pdbvt, parent, volume, data ) { var node = createnodeTreeParentData( pdbvt, parent, data ); volume.clone( node.volume ); return node; - }, + }; - createnodeTreeParentVolume2Data = function( pdbvt, parent, volume0, volume1, data ) { + var createnodeTreeParentVolume2Data = function( pdbvt, parent, volume0, volume1, data ) { var node = createnodeTreeParentData( pdbvt, parent, data ); Bump.Merge.DbvtVolume3( volume0, volume1, node.volume ); return node; - }, + }; - insertleaf = function( pdbvt, root, leaf ) { + var insertleaf = function( pdbvt, root, leaf ) { if ( !pdbvt.root ) { pdbvt.root = leaf; leaf.parent = 0; @@ -523,9 +570,7 @@ if ( !root.isleaf() ) { do { root = root.childs[ - Bump.Select.DbvtVolume3( leaf.volume, - root.childs[0].volume, - root.childs[1].volume ) + Bump.Select.DbvtVolume3( leaf.volume, root.childs[0].volume, root.childs[1].volume ) ]; } while ( !root.isleaf() ); } @@ -540,7 +585,7 @@ leaf.parent = node; do { if ( !prev.volume.Contain( node.volume ) ) { - Bump.Merge.DbvtVolume3( prev.childs[ 0 ].volume, prev.childs[ 1 ].volume, prev.volume ); + Bump.Merge.DbvtVolume3( prev.childs[0].volume, prev.childs[1].volume, prev.volume ); } else { break; } @@ -557,12 +602,12 @@ pdbvt.root = node; } } - }, + }; - removeleaf = function( pdbvt, leaf ) { + var removeleaf = function( pdbvt, leaf ) { if ( leaf === pdbvt.root ) { - pdbvt.root = 0; - return 0; + pdbvt.root = 0; + return 0; } else { @@ -575,7 +620,7 @@ deletenode( pdbvt, parent ); while ( prev ) { var pb = prev.volume; - Bump.Merge.DbvtVolume3( prev.childs[ 0 ].volume, prev.childs[ 1 ].volume, prev.volume ); + Bump.Merge.DbvtVolume3( prev.childs[0].volume, prev.childs[1].volume, prev.volume ); if ( Bump.NotEqual.DbvtVolume2( pb, prev.volume ) ) { prev = prev.parent; } else { @@ -592,44 +637,44 @@ return pdbvt.root; } } - }, + }; - fetchleaves = function( pdbvt, root, leaves, depth ) { + var fetchleaves = function( pdbvt, root, leaves, depth ) { depth = ( depth === undefined ) ? -1 : depth; if ( root.isinternal() && depth ) { - fetchleaves( pdbvt, root.childs[ 0 ], leaves, depth - 1 ); - fetchleaves( pdbvt, root.childs[ 1 ], leaves, depth - 1 ); + fetchleaves( pdbvt, root.childs[0], leaves, depth - 1 ); + fetchleaves( pdbvt, root.childs[1], leaves, depth - 1 ); deletenode( pdbvt, root ); } else { leaves.push( root ); } - }, + }; - split = function( leaves, left, right, org, axis ) { + var split = function( leaves, left, right, org, axis ) { left.splice( 0 ); // left.resize( 0 ); right.splice( 0 ); // right.resize( 0 ); var tmpVector3 = Bump.Vector3.create(); for ( var i = 0, ni = leaves.length; i < ni; ++i ) { - if ( axis.dot( leaves[ i ].volume.Center().subract( org, tmpVector3 ) ) < 0 ) { - left.push( leaves[ i ] ); + if ( axis.dot( leaves[i].volume.Center().subract( org, tmpVector3 ) ) < 0 ) { + left.push( leaves[i] ); } else { - right.push( leaves[ i ] ); + right.push( leaves[i] ); } } - }, + }; - bounds = function( leaves ) { + var bounds = function( leaves ) { var volume = leaves[0].volume.clone(); for ( var i = 1, ni = leaves.length; i < ni; ++i ) { Bump.Merge.DbvtVolume3( volume, leaves[i].volume, volume ); } return volume; - }, + }; - bottomup = function( pdbvt, leaves ) { + var bottomup = function( pdbvt, leaves ) { while ( leaves.length > 1 ) { - var minsize = Bump.SIMD_INFINITY, - minidx = [ -1, -1 ]; + var minsize = Bump.SIMD_INFINITY; + var minidx = [ -1, -1 ]; for ( var i = 0; i < leaves.length; ++i ) { for ( var j = i + 1; j < leaves.length; ++j ) { var sz = size( merge( leaves[i].volume, leaves[j].volume ) ); @@ -640,8 +685,9 @@ } } } - var n = [ leaves[ minidx[0] ], leaves[ minidx[1] ] ], - p = createnodeTreeParentVolume2Data( pdbvt, 0, n[0].volume, n[1].volume, 0 ); + + var n = [ leaves[ minidx[0] ], leaves[ minidx[1] ] ]; + var p = createnodeTreeParentVolume2Data( pdbvt, 0, n[0].volume, n[1].volume, 0 ); p.childs[0] = n[0]; p.childs[1] = n[1]; n[0].parent = p; @@ -650,9 +696,9 @@ leaves[ minidx[1] ] = leaves[ leaves.length - 1 ]; // instead of swap leaves.pop(); } - }, + }; - topdown = function( pdbvt, leaves, bu_threshold ) { + var topdown = function( pdbvt, leaves, bu_threshold ) { var axis = [ Bump.Vector3.create( 1, 0, 0 ), Bump.Vector3.create( 0, 1, 0 ), @@ -661,13 +707,13 @@ if ( leaves.length > 1 ) { if ( leaves.length > bu_threshold ) { - var vol = bounds( leaves ), - org = vol.Center().clone(), - sets = [], - bestaxis = -1, - bestmidp = leaves.length, - splitcount = [ [ 0, 0 ], [ 0, 0 ], [ 0, 0 ] ], - i; + var vol = bounds( leaves ); + var org = vol.Center().clone(); + var sets = []; + var bestaxis = -1; + var bestmidp = leaves.length; + var splitcount = [ [ 0, 0 ], [ 0, 0 ], [ 0, 0 ] ]; + var i; for ( i = 0; i < leaves.length; ++i ) { var x = leaves[i].volume.Center().subtract( org ); @@ -697,12 +743,13 @@ sets[ k & 1 ].push( leaves[k] ); } } + var node = createnodeTreeParentVolumeData( pdbvt, 0, vol, 0 ); node.childs[0] = topdown( pdbvt, sets[0], bu_threshold ); node.childs[1] = topdown( pdbvt, sets[1], bu_threshold ); node.childs[0].parent = node; node.childs[1].parent = node; - return( node ); + return node; } else { bottomup( pdbvt, leaves ); return leaves[0]; @@ -710,24 +757,23 @@ } return leaves[0]; - }, + }; // `n` : `DbvtNode`, // `rRef` : a "by-reference" `DbvtNode` - sort = function( n, rRef ) { + var sort = function( n, rRef ) { var p = n.parent; // Bump.Assert( n.isinternal() ); if ( p > n ) { - var i = indexof( n ), - j = 1 - i, - s = p.childs[ j ], - q = p.parent; + var i = indexof( n ); + var j = 1 - i; + var s = p.childs[ j ]; + var q = p.parent; // Bump.Assert( n === p.childs[i] ); if ( q ) { q.childs[ indexof( p ) ] = n; - } - else { + } else { rRef.value = n; } s.parent = n; @@ -816,10 +862,11 @@ if ( passes < 0 ) { passes = this.leaves; } + if ( this.root && ( passes > 0 ) ) { do { - var node = this.root, - bit = 0; + var node = this.root; + var bit = 0; while ( node.isinternal() ) { node = sort( node, this.root ).childs[ ( this.opath >> bit ) & 1 ]; // bit = ( bit + 1 ) & ( sizeof ( unsigned ) * 8 - 1 ); @@ -915,17 +962,18 @@ Bump.Dbvt.enumNodes( this.root, nodes ); iwriter.Prepare( this.root, nodes.nodes.length ); for ( var i = 0; i < nodes.nodes.length; ++i ) { - var n = nodes.nodes[ i ], - p = -1; + var n = nodes.nodes[ i ]; + var p = -1; + if ( n.parent ) { p = nodes.nodes.indexOf( n.parent ); } + if ( n.isinternal() ) { var c0 = nodes.nodes.indexOf( n.childs[0] ); var c1 = nodes.nodes.indexOf( n.childs[1] ); iwriter.WriteNode( n, i, p, c0, c1 ); - } - else { + } else { iwriter.WriteLeaf( n, i, p ); } } @@ -949,14 +997,15 @@ if ( e.parent !== 0 ) { // e.parent.childs[ i & 1 ] = n; nice trick, but doesn't work if you want an exact clone e.parent.childs[ indexof( e.node ) ] = n; - } - else { + } else { dest.root = n; } + if ( e.node.isinternal() ) { stack.push( Bump.Dbvt.sStkCLN.create(e.node.childs[0], n ) ); stack.push( Bump.Dbvt.sStkCLN.create(e.node.childs[1], n ) ); } + else if ( iclone ) { // added if to make sure iclone is defined iclone.CloneLeaf( n ); } @@ -969,9 +1018,9 @@ // TODO : Add description of use. collideTT: function( root0, root1, policy ) { if ( root0 && root1 ) { - var depth = 1, - threshold = Bump.Dbvt.DOUBLE_STACKSIZE - 4, - stkStack = []; + var depth = 1; + var threshold = Bump.Dbvt.DOUBLE_STACKSIZE - 4; + var stkStack = []; stkStack[ Bump.Dbvt.DOUBLE_STACKSIZE - 1 ] = undefined; // stkStack.resize( DOUBLE_STACKSIZE ); stkStack[ 0 ] = Bump.Dbvt.sStkNN.create( root0, root1 ); @@ -995,8 +1044,7 @@ stkStack[ depth++ ] = Bump.Dbvt.sStkNN.create( p.a.childs[ 1 ], p.b.childs[ 0 ] ); stkStack[ depth++ ] = Bump.Dbvt.sStkNN.create( p.a.childs[ 0 ], p.b.childs[ 1 ] ); stkStack[ depth++ ] = Bump.Dbvt.sStkNN.create( p.a.childs[ 1 ], p.b.childs[ 1 ] ); - } - else { + } else { stkStack[ depth++ ] = Bump.Dbvt.sStkNN.create( p.a.childs[ 0 ], p.b ); stkStack[ depth++ ] = Bump.Dbvt.sStkNN.create( p.a.childs[ 1 ], p.b ); } @@ -1012,13 +1060,14 @@ } } } while ( depth ); + } }, collideTTpersistentStack: function( root0, root1, policy ) { if ( root0 && root1 ) { - var depth = 1, - threshold = Bump.Dbvt.DOUBLE_STACKSIZE - 4; + var depth = 1; + var threshold = Bump.Dbvt.DOUBLE_STACKSIZE - 4; Bump.resize( this.stkStack, Bump.Dbvt.DOUBLE_STACKSIZE, sStkNNZero ); this.stkStack[0].set( root0, root1 ); @@ -1112,21 +1161,25 @@ policy ) { if ( root ) { - var resultNormal = Bump.Vector3.create(); + // not used + // var resultNormal = Bump.Vector3.create(); + + var depth = 1; + var threshold = Bump.Dbvt.DOUBLE_STACKSIZE - 2; + var stack = this.rayTestStack; + var bounds = getArray(); + bounds[ 0 ] = getVector3(); + bounds[ 1 ] = getVector3(); - var depth = 1, - threshold = Bump.Dbvt.DOUBLE_STACKSIZE - 2, - stack = this.rayTestStack, - bounds = [ Bump.Vector3.create(), Bump.Vector3.create() ]; Bump.resize( stack, Bump.Dbvt.DOUBLE_STACKSIZE, undefined ); stack[ 0 ] = root; do { var node = stack[ --depth ]; node.volume.Mins().subtract( aabbMax, bounds[ 0 ] ); node.volume.Maxs().subtract( aabbMin, bounds[ 1 ] ); - var tmin = { tmin: 1 }, // primitive "passed by reference" - lambda_min = 0, - result1 = false; + var tmin = getScalarRef( 1 ); // primitive "passed by reference" + var lambda_min = 0; + var result1 = false; result1 = Bump.RayAabb2( rayFrom, rayDirectionInverse, signs, bounds, tmin, lambda_min, lambda_max ); if ( result1 ) { @@ -1143,8 +1196,12 @@ policy.ProcessNode( node ); } } + + delScalarRef( tmin ); } while ( depth ); + delVector3( bounds[0], bounds[1] ); + delArray( bounds ); } } @@ -1152,29 +1209,31 @@ typeMembers: { maxdepth: function( node ) { - var depth = { value: 0 }; + var depth = getScalarRef( 0 ); if ( node ) { getmaxdepth( node, 1, depth ); } + + // !!!: if delScalarRef changes the value of depth, then + // return value should be saved and then returned. + delScalarRef( depth ); return depth.value; }, countLeaves: function( node ) { if ( node.isinternal() ) { - return( Bump.Dbvt.countLeaves( node.childs[ 0 ] ) + - Bump.Dbvt.countLeaves( node.childs[ 1 ] ) ); - } - else { + return( Bump.Dbvt.countLeaves( node.childs[0] ) + + Bump.Dbvt.countLeaves( node.childs[1] ) ); + } else { return 1; } }, extractLeaves: function( node, leaves ) { if ( node.isinternal() ) { - Bump.Dbvt.extractLeaves( node.childs[ 0 ], leaves ); - Bump.Dbvt.extractLeaves( node.childs[ 1 ], leaves ); - } - else { + Bump.Dbvt.extractLeaves( node.childs[0], leaves ); + Bump.Dbvt.extractLeaves( node.childs[1], leaves ); + } else { leaves.push( node ); } }, @@ -1186,94 +1245,109 @@ enumNodes: function( root, policy ) { policy.ProcessNode( root ); if ( root.isinternal() ) { - Bump.Dbvt.enumNodes( root.childs[ 0 ], policy ); - Bump.Dbvt.enumNodes( root.childs[ 1 ], policy ); + Bump.Dbvt.enumNodes( root.childs[0], policy ); + Bump.Dbvt.enumNodes( root.childs[1], policy ); } }, // iterate over only the leaf nodes and process according to the given policy enumLeaves: function( root, policy ) { if ( root.isinternal() ) { - Bump.Dbvt.enumLeaves( root.childs[ 0 ], policy ); - Bump.Dbvt.enumLeaves( root.childs[ 1 ], policy ); - } - else { + Bump.Dbvt.enumLeaves( root.childs[0], policy ); + Bump.Dbvt.enumLeaves( root.childs[1], policy ); + } else { policy.ProcessNode( root ); } }, rayTest: function( root, rayFrom, rayTo, policy ) { if ( root ) { - var diff = rayTo.subtract( rayFrom ), - rayDir = diff.normalized(); + var tmpV1 = getVector3(); + var tmpV2 = getVector3(); + var tmpV3 = getVector3(); - var rayDirectionInverse = Bump.Vector3.create( + var diff = rayTo.subtract( rayFrom, tmpV1 ), + rayDir = diff.normalized( tmpV2 ); + + var rayDirectionInverse = tmpV3.setValue( rayDir.x === 0 ? Infinity : 1 / rayDir.x, rayDir.y === 0 ? Infinity : 1 / rayDir.y, rayDir.z === 0 ? Infinity : 1 / rayDir.z ); - var signs = [ - rayDirectionInverse.x < 0 ? 1 : 0, - rayDirectionInverse.y < 0 ? 1 : 0, - rayDirectionInverse.z < 0 ? 1 : 0 - ]; + var signs = getArray(); + signs[ 0 ] = rayDirectionInverse.x < 0 ? 1 : 0; + signs[ 1 ] = rayDirectionInverse.y < 0 ? 1 : 0; + signs[ 2 ] = rayDirectionInverse.z < 0 ? 1 : 0; + var lambda_max = rayDir.dot( diff ); var resultNormal; - var stack = []; + var stack = getRayTestStackArray( Bump.Dbvt.DOUBLE_STACKSIZE ); + // note that it is possible that stack's length is greater than the + // requested size, if it has been recycled from a previous call + var depth = 1; - var threshold = Bump.Dbvt.DOUBLE_STACKSIZE - 2; + var threshold = stack.length - 2; - stack[ Bump.Dbvt.DOUBLE_STACKSIZE - 1 ] = undefined; stack[ 0 ] = root; - var bounds = [ Bump.Vector3.create(), Bump.Vector3.create() ]; + var bounds = getArray(); + bounds[ 0 ] = getVector3(); + bounds[ 1 ] = getVector3(); do { var node = stack[ --depth ]; - bounds[ 0 ] = node.volume.Mins(); - bounds[ 1 ] = node.volume.Maxs(); + bounds[ 0 ].assign( node.volume.Mins() ); + bounds[ 1 ].assign( node.volume.Maxs() ); - var tmin = { tmin: 1 }, + var tmin = getScalarRef( 1 ), lambda_min = 0, result1 = Bump.RayAabb2( rayFrom, rayDirectionInverse, signs, bounds, tmin, lambda_min, lambda_max ); if ( result1 ) { if ( node.isinternal() ) { if ( depth > threshold ) { - stack[ stack.length * 2 - 1] = undefined; + // if we are resizing, just discard the old array + Bump.resize( stack, stack.length * 2 ); threshold = stack.length - 2; } stack[ depth++ ] = node.childs[ 0 ]; stack[ depth++ ] = node.childs[ 1 ]; } + else { policy.ProcessNode( node ); } } + + delScalarRef( tmin ); } while ( depth ); + + delVector3( tmpV1, tmpV2, tmpV3, bounds[0], bounds[1] ); + delRayTestStackArray( stack ); + delArray( signs, bounds ); } }, collideKDOP: function( root, normals, offsets, count, policy ) { if ( root ) { - var inside = ( 1 << count ) - 1, - stack = [], - signs = [], - i, j; + var inside = ( 1 << count ) - 1; + var stack = []; + var signs = []; + var i, j; // Bump.Assert( count < sizeof(signs) / sizeof(signs[0]) ); for ( i = 0; i < count; i++ ) { - signs[ i ] = ( ( normals[ i ].x >= 0 ) ? 1 : 0 ) + - ( ( normals[ i ].y >= 0 ) ? 2 : 0 ) + - ( ( normals[ i ].z >= 0 ) ? 4 : 0 ); + signs[ i ] = ( ( normals[i].x >= 0 ) ? 1 : 0 ) + + ( ( normals[i].y >= 0 ) ? 2 : 0 ) + + ( ( normals[i].z >= 0 ) ? 4 : 0 ); } // stack.reserve( SIMPLE_STACKSIZE ); stack.push( Bump.Dbvt.sStkNP.create( root, 0 ) ); do { - var se = stack[ stack.length() - 1 ], - out = false; + var se = stack[ stack.length() - 1 ]; + var out = false; stack.pop(); for ( i = 0, j = 1; ( !out ) && ( i < count ); ++i, j <<= 1 ) { @@ -1287,12 +1361,12 @@ } } } + if ( !out ) { if ( (se.mask !== inside) && (se.node.isinternal()) ) { stack.push( Bump.Dbvt.sStkNP.create( se.node.childs[0],se.mask ) ); stack.push( Bump.Dbvt.sStkNP.create( se.node.childs[1],se.mask ) ); - } - else { + } else { if ( policy.AllLeaves( se.node ) ) { Bump.Dbvt.enumLeaves( se.node, policy ); } @@ -1307,29 +1381,34 @@ if ( root ) { var srtsgns = ( sortaxis.x >= 0 ? 1 : 0 ) + - ( sortaxis.y >= 0 ? 2 : 0 ) + - ( sortaxis.z >= 0 ? 4 : 0 ), - inside = (1 << count ) - 1, - stock = [], - ifree= [], - stack = [], - signs = [], - i, j, k; + ( sortaxis.y >= 0 ? 2 : 0 ) + + ( sortaxis.z >= 0 ? 4 : 0 ); + var inside = ( 1 << count ) - 1; + var stock = []; + var ifree = []; + var stack = []; + var signs = []; + var i, j, k; // Bump.Assert( count < sizeof(signs) / sizeof(signs[0]) ); for ( i = 0; i < count; ++i ) { - signs[ i ] = ( ( normals[ i ].x >= 0 ) ? 1 : 0 ) + - ( ( normals[ i ].y >= 0 ) ? 2 : 0 ) + - ( ( normals[ i ].z >= 0 ) ? 4 : 0 ); + signs[ i ] = ( ( normals[i].x >= 0 ) ? 1 : 0 ) + + ( ( normals[i].y >= 0 ) ? 2 : 0 ) + + ( ( normals[i].z >= 0 ) ? 4 : 0 ); } // stock.reserve(SIMPLE_STACKSIZE); // stack.reserve(SIMPLE_STACKSIZE); // ifree.reserve(SIMPLE_STACKSIZE); - stack.push( Bump.Dbvt.allocate( ifree, - stock, - Bump.Dbvt.sStkNPS.create( root, - 0, - root.volume.ProjectMinimum( - sortaxis, srtsgns ) ) ) ); + + stack.push( Bump.Dbvt.allocate( + ifree, + stock, + Bump.Dbvt.sStkNPS.create( + root, + 0, + root.volume.ProjectMinimum( sortaxis, srtsgns ) + ) + )); + do { var id = stack[ stack.length - 1 ], se = stock[ id ]; @@ -1348,24 +1427,28 @@ } } } + if ( out ) { continue; } } + if ( policy.Descent( se.node ) ) { if ( se.node.isinternal() ) { - var pns = [ se.node.childs[ 0 ], se.node.childs[ 1 ] ], - nes = [ - Bump.Dbvt.sStkNPS.create( pns[ 0 ], - se.mask, - pns[ 0 ].volume.ProjectMinimum( sortaxis, - srtsgns ) ), - Bump.Dbvt.sStkNPS.create( pns[ 1 ], - se.mask, - pns[ 1 ].volume.ProjectMinimum( sortaxis, - srtsgns ) ) - ], - q = nes[ 0 ].value < nes[ 1 ].value ? 1 : 0; + var pns = [ se.node.childs[ 0 ], se.node.childs[ 1 ] ]; + var nes = [ + Bump.Dbvt.sStkNPS.create( + pns[ 0 ], + se.mask, + pns[ 0 ].volume.ProjectMinimum( sortaxis, srtsgns ) + ), + Bump.Dbvt.sStkNPS.create( + pns[ 1 ], + se.mask, + pns[ 1 ].volume.ProjectMinimum( sortaxis, srtsgns ) + ) + ]; + var q = nes[ 0 ].value < nes[ 1 ].value ? 1 : 0; j = stack.length; @@ -1385,11 +1468,13 @@ } stack[ j ] = Bump.Dbvt.allocate( ifree, stock, nes[ 1 - q ] ); } + else { stack.push( Bump.Dbvt.allocate( ifree, stock, nes[ q ] ) ); stack.push( Bump.Dbvt.allocate( ifree, stock, nes[ 1 - q ] ) ); } } + else { policy.Process( se.node, se.value ); } @@ -1410,8 +1495,7 @@ if ( n.isinternal() ) { stack.push( n.childs[ 0 ] ); stack.push( n.childs[ 1 ] ); - } - else { + } else { policy.Process( n ); } } @@ -1429,10 +1513,9 @@ var m = 0; while ( l < h ) { m = ( l + h ) >> 1; - if ( a[ i[ m ] ].value >= v ) { + if ( a[i[ m ]].value >= v ) { l = m + 1; - } - else { + } else { h = m; } } diff --git a/src/BulletCollision/BroadphaseCollision/DbvtBroadphase.js b/src/BulletCollision/BroadphaseCollision/DbvtBroadphase.js index daad35d..4e9d1bd 100644 --- a/src/BulletCollision/BroadphaseCollision/DbvtBroadphase.js +++ b/src/BulletCollision/BroadphaseCollision/DbvtBroadphase.js @@ -122,6 +122,8 @@ } }); + var tmpBroadphaseRayTester = Bump.BroadphaseRayTester.create(); + Bump.DbvtBroadphase = Bump.type({ parent: Bump.BroadphaseInterface, @@ -359,7 +361,9 @@ aabbMin = aabbMin || Bump.Vector3.create(); aabbMax = aabbMin || Bump.Vector3.create(); - var callback = Bump.BroadphaseRayTester.create( rayCallback ); + // var callback = Bump.BroadphaseRayTester.create( rayCallback ); + var callback = tmpBroadphaseRayTester; + callback.init( rayCallback ); this.sets[0].rayTestInternal( this.sets[0].root, diff --git a/src/BulletCollision/CollisionDispatch/CollisionWorld.js b/src/BulletCollision/CollisionDispatch/CollisionWorld.js index 40ad7a6..f2a5b68 100644 --- a/src/BulletCollision/CollisionDispatch/CollisionWorld.js +++ b/src/BulletCollision/CollisionDispatch/CollisionWorld.js @@ -3,18 +3,48 @@ // load: BulletCollision/NarrowPhaseCollision/RaycastCallback.js // load: BulletCollision/BroadphaseCollision/Dbvt.js // load: BulletCollision/BroadphaseCollision/BroadphaseInterface.js +// load: BulletCollision/BroadphaseCollision/BroadphaseProxy.js +// load: BulletCollision/CollisionDispatch/CollisionObject.js +// load: BulletCollision/NarrowPhaseCollision/VoronoiSimplexSolver.js +// load: BulletCollision/NarrowPhaseCollision/SubSimplexConvexCast.js +// load: BulletCollision/CollisionShapes/SphereShape.js // run: LinearMath/Transform.js // run: LinearMath/TransformUtil.js // run: BulletCollision/BroadphaseCollision/BroadphaseProxy.js // run: BulletCollision/BroadphaseCollision/Dispatcher.js -// run: BulletCollision/CollisionDispatch/CollisionObject.js -// run: BulletCollision/NarrowPhaseCollision/VoronoiSimplexSolver.js -// run: BulletCollision/NarrowPhaseCollision/SubSimplexConvexCast.js -// run: BulletCollision/CollisionShapes/SphereShape.js + (function( window, Bump ) { + + // memory pool management + + // dummyArgs is an array of arguments to be used when creating + // a new object of Type, as some Types expect args within their + // create functions + var createGetter = function( Type, pool, dummyArgs ) { + // if there are dummy args, use them + if( dummyArgs ) { + return function() { + return pool.pop() || Type.create.apply( Type, dummyArgs ); + }; + } else { + return function() { + return pool.pop() || Type.create(); + }; + } + }; + + var createDeller = function( pool ) { + return function() { + for ( var i = 0; i < arguments.length; ++i ) { + pool.push( arguments[i] ); + } + }; + }; + var tmpV1 = Bump.Vector3.create(); + var tmpV2 = Bump.Vector3.create(); // port of btCollisionWorld::RayResultCallback (stored on Bump.CollisionWorld after its // definition below @@ -47,13 +77,13 @@ var BridgeTriangleRaycastCallback = Bump.type({ parent: Bump.TriangleRaycastCallback, - init: function BridgeTriangleRaycastCallback( from, - to, - resultCallback, - collisionObject, - triangleMesh, - colObjWorldTransform - ) { + init: function BridgeTriangleRaycastCallback( + from, to, + resultCallback, + collisionObject, + triangleMesh, + colObjWorldTransform + ) { //@BP Mod this._super( from, to, resultCallback.flags ); this.resultCallback = resultCallback; @@ -63,6 +93,25 @@ }, members: { + // ASD: added for easy recycling, since init() calls clone + set: function BridgeTriangleRaycastCallback( + from, to, + resultCallback, + collisionObject, + triangleMesh, + colObjWorldTransform + ) { + //@BP Mod + Bump.TriangleRaycastCallback.set.call( + this, from, to, resultCallback.flags + ); + this.resultCallback = resultCallback; + this.collisionObject = collisionObject; + this.triangleMesh = triangleMesh; + this.colObjWorldTransform.assign( colObjWorldTransform ); + return this; + }, + reportHit: function( hitNormalLocal, hitFraction, partId, triangleIndex ) { var shapeInfo = Bump.CollisionWorld.LocalShapeInfo.create(); shapeInfo.shapePart = partId; @@ -96,6 +145,16 @@ }, members: { + set: function( + i, + user + ) { + this.userCallback = user; + this.i = i; + this.closestHitFraction = this.userCallback.closestHitFraction; + return this; + }, + needsCollision: function( p // Bump.BroadphaseProxy ) { @@ -140,18 +199,40 @@ }, members: { + + // ASD: added for easy recycling of RayTester objects, since init calls + // clone() + set: function( + collisionObject, // btCollisionObject* + compoundShape, // const btCompoundShape* + colObjWorldTransform, // const btTransform& + rayFromTrans, // const btTransform& + rayToTrans, // const btTransform& + resultCallback // RayResultCallback& + ) { + this.collisionObject = collisionObject; + this.compoundShape = compoundShape; + this.colObjWorldTransform.assign( colObjWorldTransform ); + this.rayFromTrans.assign( rayFromTrans ); + this.rayToTrans.assign( rayToTrans ); + this.resultCallback = resultCallback; + + return this; + }, + // ASD: this function actually doesn't overwrite anything in ICollide, so we will leave it named as // `Process` for now... Process: function( i ) { var childCollisionShape = this.compoundShape.getChildShape( i ); var childTrans = this.compoundShape.getChildTransform( i ); - var childWorldTrans = this.colObjWorldTransform.multiplyTransform( childTrans ); + var childWorldTrans = this.colObjWorldTransform.multiplyTransform( childTrans, getTransform() ); // replace collision shape so that callback can determine the triangle var saveCollisionShape = this.collisionObject.getCollisionShape(); this.collisionObject.internalSetTemporaryCollisionShape( childCollisionShape ); - var my_cb = LocalInfoAdder2.create( i, this.resultCallback ); + // var my_cb = LocalInfoAdder2.create( i, this.resultCallback ); + var my_cb = getLocalAddrInfo2().set( i, this.resultCallback ); Bump.CollisionWorld.rayTestSingle( this.rayFromTrans, @@ -164,6 +245,9 @@ // restore this.collisionObject.internalSetTemporaryCollisionShape( saveCollisionShape ); + + delTransform( childWorldTrans ); + delLocalAddrInfo2( my_cb ); }, ProcessNode: function( leaf ) { @@ -172,6 +256,198 @@ } }); + // port of btCollisionWorld::SingleRayCallback, stored on + // Bump.CollisionWorld after its definition below + var SingleRayCallback = Bump.type({ + parent: Bump.BroadphaseRayCallback, + + init: function SingleRayCallback( + rayFromWorld, // const btVector3& + rayToWorld, // const btVector3& + world, // const btCollisionWorld* + resultCallback // btCollisionWorld::RayResultCallback& + ) { + this.rayFromWorld = rayFromWorld.clone(); + this.rayToWorld = rayToWorld.clone(); + this.hitNormal = Bump.Vector3.create(); + + this.world = world; + this.resultCallback = resultCallback; + + this.rayFromTrans = Bump.Transform.getIdentity(); + this.rayFromTrans.setOrigin( this.rayFromWorld ); + this.rayToTrans = Bump.Transform.getIdentity(); + this.rayToTrans.setOrigin( this.rayToWorld ); + + var rayDir = rayToWorld.subtract( rayFromWorld, tmpV1 ).normalize(); + + // what about division by zero? --> just set rayDirection[i] to INF/BT_LARGE_FLOAT + this.rayDirectionInverse = Bump.Vector3.create(); + this.rayDirectionInverse.x = rayDir.x === 0 ? Infinity : 1 / rayDir.x; + this.rayDirectionInverse.y = rayDir.y === 0 ? Infinity : 1 / rayDir.y; + this.rayDirectionInverse.z = rayDir.z === 0 ? Infinity : 1 / rayDir.z; + this.signs = new Array( 3 ); + this.signs[ 0 ] = this.rayDirectionInverse.x < 0 ? 1 : 0; + this.signs[ 1 ] = this.rayDirectionInverse.y < 0 ? 1 : 0; + this.signs[ 2 ] = this.rayDirectionInverse.z < 0 ? 1 : 0; + + this.lambda_max = rayDir.dot( this.rayToWorld.subtract( this.rayFromWorld, tmpV2 )); + }, + + members: { + + // ASD: added for easy recycling without extra allocations + set: function( + rayFromWorld, // const btVector3& + rayToWorld, // const btVector3& + world, // const btCollisionWorld* + resultCallback // btCollisionWorld::RayResultCallback& + ) { + this.rayFromWorld.assign( rayFromWorld ); + this.rayToWorld.assign( rayToWorld ); + this.hitNormal.setZero(); + + this.world = world; + this.resultCallback = resultCallback; + + this.rayFromTrans.setIdentity(); + this.rayFromTrans.setOrigin( this.rayFromWorld ); + this.rayToTrans.setIdentity(); + this.rayToTrans.setOrigin( this.rayToWorld ); + + var rayDir = rayToWorld.subtract( rayFromWorld, tmpV1 ).normalize(); + + // what about division by zero? --> just set rayDirection[i] to INF/BT_LARGE_FLOAT + this.rayDirectionInverse.x = rayDir.x === 0 ? Infinity : 1 / rayDir.x; + this.rayDirectionInverse.y = rayDir.y === 0 ? Infinity : 1 / rayDir.y; + this.rayDirectionInverse.z = rayDir.z === 0 ? Infinity : 1 / rayDir.z; + this.signs[ 0 ] = this.rayDirectionInverse.x < 0 ? 1 : 0; + this.signs[ 1 ] = this.rayDirectionInverse.y < 0 ? 1 : 0; + this.signs[ 2 ] = this.rayDirectionInverse.z < 0 ? 1 : 0; + + this.lambda_max = rayDir.dot( this.rayToWorld.subtract( this.rayFromWorld, tmpV2 )); + + return this; + }, + + process: function( proxy ) { + // terminate further ray tests, once the closestHitFraction reached zero + if ( this.resultCallback.closestHitFraction === 0 ) { + return false; + } + var collisionObject = proxy.clientObject; + + // only perform raycast if filterMask matches + if ( this.resultCallback.needsCollision( collisionObject.getBroadphaseHandle() )) { + // RigidcollisionObject* collisionObject = ctrl.GetRigidcollisionObject(); + // btVector3 collisionObjectAabbMin,collisionObjectAabbMax; + // btScalar hitLambda = this.resultCallback.closestHitFraction; + // culling already done by broadphase + // if ( btRayAabb( this.rayFromWorld, this.rayToWorld, collisionObjectAabbMin, collisionObjectAabbMax, hitLambda, this.hitNormal ) ) + this.world.rayTestSingle( + this.rayFromTrans, + this.rayToTrans, + collisionObject, + collisionObject.getCollisionShape(), + collisionObject.getWorldTransform(), + this.resultCallback + ); + } + return true; + } + } + }); + + // port of btCollisionWorld::LocalRayResult, stored on + // Bump.CollisionWorld after its definition below + var LocalRayResult = Bump.type({ + init: function LocalRayResult ( + collisionObject, // btCollisionObject* + localShapeInfo, // LocalShapeInfo* + hitNormalLocal, // const btVector3& + hitFraction // btScalar + ) { + this.collisionObject = collisionObject; + this.localShapeInfo = localShapeInfo; + this.hitNormalLocal = hitNormalLocal; + this.hitFraction = hitFraction; + } + }); + + + // Collision World memory pools and temporaries + + // for rayTest + var tmpSingleRayCallback = SingleRayCallback.create( + Bump.Vector3.create(), + Bump.Vector3.create(), + undefined, + undefined + ); + + // for rayTestSingle + var vector3Pool = []; + var transformPool = []; + var sphereShapePool = []; + var voronoiPool = []; + var convexCastPool = []; + var castResultPool = []; + var localRayResultPool = []; + var bridgeTriangleRCBPool = []; + var rayTesterPool = []; + var localAddrInfo2Pool = []; + + var getVector3 = createGetter( Bump.Vector3, vector3Pool ); + var getTransform = createGetter( Bump.Transform, transformPool ); + var getSphereShape = createGetter( Bump.SphereShape, sphereShapePool, [ 0.0 ] ); + var getVoronoiSimplexSolver = createGetter( Bump.VoronoiSimplexSolver, voronoiPool ); + var getSubsimplexConvexCast = createGetter( Bump.SubsimplexConvexCast, convexCastPool ); + var getCastResult = createGetter( Bump.ConvexCast.CastResult, castResultPool ); + var getLocalRayResult = createGetter( LocalRayResult, localRayResultPool ); + + var getBridgeTriangleRaycastCallback = createGetter( + BridgeTriangleRaycastCallback, + bridgeTriangleRCBPool, + [ + Bump.Vector3.create(), + Bump.Vector3.create(), + { flags: 0 }, // dummy value for `resultCallback` param + undefined, + undefined, + Bump.Transform.getIdentity() + ] + ); + + var getRayTester = createGetter( RayTester, rayTesterPool, [ + undefined, + undefined, + Bump.Transform.getIdentity(), + Bump.Transform.getIdentity(), + Bump.Transform.getIdentity(), + undefined + ]); + + var getLocalAddrInfo2 = createGetter( LocalInfoAdder2, localAddrInfo2Pool, [ + undefined, + { + closestHitFraction: 0 + } + ] ); + + var delVector3 = createDeller( vector3Pool ); + var delTransform = createDeller( transformPool ); + var delSphereShape = createDeller( sphereShapePool ); + var delVoronoiSimplexSolver = createDeller( voronoiPool ); + var delSubsimplexConvexCast = createDeller( convexCastPool ); + var delCastResult = createDeller( castResultPool ); + var delLocalRayResult = createDeller( localRayResultPool ); + var delBridgeTriangleRaycastCallback = createDeller( bridgeTriangleRCBPool ); + var delRayTester = createDeller( rayTesterPool ); + var delLocalAddrInfo2 = createDeller( localAddrInfo2Pool ); + + // used to reinitialize a SphereShape in rayTestSingle + var emptySphereShape = Bump.SphereShape.create( 0.0 ); + emptySphereShape.setMargin( 0 ); Bump.CollisionWorld = Bump.type({ @@ -319,9 +595,9 @@ // aabb and for each object with ray-aabb overlap, perform an exact ray // test. rayTest: function( rayFromWorld, rayToWorld, resultCallback ) { - var rayCB = Bump.CollisionWorld.SingleRayCallback.create( rayFromWorld, rayToWorld, this, resultCallback ); + var rayCB = tmpSingleRayCallback.set( rayFromWorld, rayToWorld, this, resultCallback ); - this.broadphasePairCache.rayTest( rayFromWorld, rayToWorld, rayCB ); + this.broadphasePairCache.rayTest( rayFromWorld, rayToWorld, rayCB, tmpV1, tmpV2 ); }, // Use the broadphase to accelerate the search for objects, based on their @@ -458,24 +734,37 @@ collisionShape, colObjWorldTransform, resultCallback ) { - - var pointShape = Bump.SphereShape.create( 0.0 ); - pointShape.setMargin( 0 ); - - var castShape = pointShape; + // allocate temporaries + // TODO: not all of these are needed for every rayTestSingle call, + // so allocations could be moved to where they are needed + var tmpVec1 = getVector3(); + var tmpVec2 = getVector3(); + var tmpTrans = getTransform(); + var tmpSS = getSphereShape(); + var tmpCR = getCastResult(); + var tmpVSS = getVoronoiSimplexSolver(); + var tmpSSCC = getSubsimplexConvexCast(); + var tmpLRR = getLocalRayResult(); + var tmpBTRC = getBridgeTriangleRaycastCallback(); + var tmpRT = getRayTester(); + + // re-init the recycled SphereShape using `set` + var castShape = tmpSS.set( emptySphereShape ); var worldTocollisionObject, rayFromLocal, rayToLocal, rcb; if ( collisionShape.isConvex() ) { - var castResult = Bump.ConvexCast.CastResult.create(); + var castResult = tmpCR; castResult.fraction = resultCallback.closestHitFraction; var convexShape = collisionShape; - var simplexSolver = Bump.VoronoiSimplexSolver.create(); + var simplexSolver = tmpVSS; + simplexSolver.reset(); // #define USE_SUBSIMPLEX_CONVEX_CAST 1 // #ifdef USE_SUBSIMPLEX_CONVEX_CAST - var convexCaster = Bump.SubsimplexConvexCast.create( castShape, convexShape, simplexSolver ); + var convexCaster = tmpSSCC; + convexCaster.init( castShape, convexShape, simplexSolver ); // #else // //btGjkConvexCast convexCaster(castShape,convexShape,&simplexSolver); // //btContinuousConvexCollision convexCaster(castShape,convexShape,&simplexSolver,0); @@ -497,7 +786,8 @@ // #endif //USE_SUBSIMPLEX_CONVEX_CAST castResult.normal.normalize(); - var localRayResult = Bump.CollisionWorld.LocalRayResult.create( + var localRayResult = tmpLRR; + localRayResult.init( collisionObject, 0, castResult.normal, @@ -524,7 +814,7 @@ // ConvexCast::CastResult // ASD: in-function declaration of BridgeTriangleRaycastCallback went here - rcb = BridgeTriangleRaycastCallback.create( + rcb = tmpBTRC.set( rayFromLocal, rayToLocal, resultCallback, @@ -551,7 +841,7 @@ // but since it was line-for-line identical to the first declaration, the two were // were consolidated into a single Bump.type() outside of CollisionWorld. - rcb = BridgeTriangleRaycastCallback.create( + rcb = tmpBTRC.set( rayFromLocal, rayToLocal, resultCallback, @@ -561,9 +851,9 @@ ); rcb.hitFraction = resultCallback.closestHitFraction; - var rayAabbMinLocal = rayFromLocal.clone(); + var rayAabbMinLocal = tmpVec1.assign( rayFromLocal ); rayAabbMinLocal.setMin( rayToLocal ); - var rayAabbMaxLocal = rayFromLocal.clone(); + var rayAabbMaxLocal = tmpVec2.assign( rayFromLocal ); rayAabbMaxLocal.setMax( rayToLocal ); concaveShape.processAllTriangles( rcb, rayAabbMinLocal, rayAabbMaxLocal ); @@ -578,8 +868,7 @@ var compoundShape = collisionShape; var dbvt = compoundShape.getDynamicAabbTree(); - - var rayCB = RayTester.create( + var rayCB = tmpRT.set( collisionObject, compoundShape, colObjWorldTransform, @@ -589,88 +878,41 @@ ); if ( dbvt ) { - var localRayFrom = colObjWorldTransform.inverseTimes( rayFromTrans ).getOrigin().clone(); - var localRayTo = colObjWorldTransform.inverseTimes( rayToTrans ).getOrigin().clone(); + var localRayFrom = tmpVec1.assign( + colObjWorldTransform.inverseTimes( rayFromTrans, tmpTrans ).origin + ); + var localRayTo = tmpVec2.assign( + colObjWorldTransform.inverseTimes( rayToTrans, tmpTrans ).origin + ); Bump.Dbvt.rayTest( dbvt.root, localRayFrom, localRayTo, rayCB ); } else { for ( var i = 0, n = compoundShape.getNumChildShapes(); i < n; ++i ) { rayCB.Process( i ); } } - } } } - } - } - }); - - Bump.CollisionWorld.SingleRayCallback = Bump.type({ - parent: Bump.BroadphaseRayCallback, - - init: function SingleRayCallback( - rayFromWorld, // const btVector3& - rayToWorld, // const btVector3& - world, // const btCollisionWorld* - resultCallback // btCollisionWorld::RayResultCallback& - ) { - this.rayFromWorld = rayFromWorld.clone(); - this.rayToWorld = rayToWorld.clone(); - this.hitNormal = Bump.Vector3.create(); - - this.world = world; - this.resultCallback = resultCallback; - - this.rayFromTrans = Bump.Transform.getIdentity(); - this.rayFromTrans.setOrigin( this.rayFromWorld ); - this.rayToTrans = Bump.Transform.getIdentity(); - this.rayToTrans.setOrigin( this.rayToWorld ); - - var rayDir = rayToWorld.subtract( rayFromWorld ).normalize(); - - // what about division by zero? --> just set rayDirection[i] to INF/BT_LARGE_FLOAT - this.rayDirectionInverse = Bump.Vector3.create(); - this.signs = Bump.Vector3.create(); - this.rayDirectionInverse[ 0 ] = rayDir[ 0 ] === 0 ? Infinity : 1 / rayDir[ 0 ]; - this.rayDirectionInverse[ 1 ] = rayDir[ 1 ] === 0 ? Infinity : 1 / rayDir[ 1 ]; - this.rayDirectionInverse[ 2 ] = rayDir[ 2 ] === 0 ? Infinity : 1 / rayDir[ 2 ]; - this.signs[ 0 ] = this.rayDirectionInverse[ 0 ] < 0 ? 1 : 0; - this.signs[ 1 ] = this.rayDirectionInverse[ 1 ] < 0 ? 1 : 0; - this.signs[ 2 ] = this.rayDirectionInverse[ 2 ] < 0 ? 1 : 0; - - this.lambda_max = rayDir.dot( this.rayToWorld.subtract( this.rayFromWorld )); - }, - - members: { - process: function( proxy ) { - // terminate further ray tests, once the closestHitFraction reached zero - if ( this.resultCallback.closestHitFraction === 0 ) { - return false; - } - var collisionObject = proxy.clientObject; - // only perform raycast if filterMask matches - if ( this.resultCallback.needsCollision( collisionObject.getBroadphaseHandle() )) { - // RigidcollisionObject* collisionObject = ctrl.GetRigidcollisionObject(); - // btVector3 collisionObjectAabbMin,collisionObjectAabbMax; - // btScalar hitLambda = this.resultCallback.closestHitFraction; - // culling already done by broadphase - // if ( btRayAabb( this.rayFromWorld, this.rayToWorld, collisionObjectAabbMin, collisionObjectAabbMax, hitLambda, this.hitNormal ) ) - this.world.rayTestSingle( - this.rayFromTrans, - this.rayToTrans, - collisionObject, - collisionObject.getCollisionShape(), - collisionObject.getWorldTransform(), - this.resultCallback - ); - } - return true; + // free temporaries + delVector3( tmpVec1 ); + delVector3( tmpVec2 ); + delTransform( tmpTrans ); + delSphereShape( tmpSS ); + delCastResult( tmpCR ); + delVoronoiSimplexSolver( tmpVSS ); + delSubsimplexConvexCast( tmpSSCC ); + delLocalRayResult( tmpLRR ); + delBridgeTriangleRaycastCallback( tmpBTRC ); + delRayTester( tmpRT ); } } }); + // defined above for dependency reasons + Bump.CollisionWorld.SingleRayCallback = SingleRayCallback; + // port of btCollisionWorld::LocalShapeInfo Bump.CollisionWorld.LocalShapeInfo = Bump.type({ init: function LocalShapeInfo () { @@ -679,21 +921,8 @@ } }); - // port of btCollisionWorld::LocalRayResult - Bump.CollisionWorld.LocalRayResult = Bump.type({ - init: function LocalRayResult ( - collisionObject, // btCollisionObject* - localShapeInfo, // LocalShapeInfo* - hitNormalLocal, // const btVector3& - hitFraction // btScalar - ) { - this.collisionObject = collisionObject; - this.localShapeInfo = localShapeInfo; - this.hitNormalLocal = hitNormalLocal; - this.hitFraction = hitFraction; - } - }); - + // defined above for dependency reasons + Bump.CollisionWorld.LocalRayResult = LocalRayResult; Bump.CollisionWorld.RayResultCallback = RayResultCallback; // port of btCollisionWorld::ClosestRayResultCallback diff --git a/src/BulletCollision/CollisionShapes/SphereShape.js b/src/BulletCollision/CollisionShapes/SphereShape.js index fdff379..e6d41b7 100644 --- a/src/BulletCollision/CollisionShapes/SphereShape.js +++ b/src/BulletCollision/CollisionShapes/SphereShape.js @@ -6,6 +6,8 @@ (function( window, Bump ) { + var tmpV1 = Bump.Vector3.create(); + Bump.SphereShape = Bump.type({ parent: Bump.ConvexInternalShape, @@ -19,6 +21,24 @@ }, members: { + // !!!: added for fast, easy initialization of recycled SphereShapes + set: function( other ) { + // !!!: unroll the calls to super for performance + // from CollisionShape: + this.userPointer = other.userPointer; + + // nothing from ConvexShape + + // from ConvexInternalShape: + this.localScaling.assign( other.localScaling ); + + this.shapeType = other.shapeType; + this.implicitShapeDimensions.x = other.implicitShapeDimensions.x; + this.collisionMargin = other.collisionMargin; + + return this; + }, + clone: function( dest ) { dest = dest || Bump.SphereShape.create( this.collisionMargin ); @@ -32,12 +52,12 @@ var supVertex = dest; supVertex = this.localGetSupportingVertexWithoutMargin( vec, supVertex ); - var vecnorm = vec.clone(); + var vecnorm = tmpV1.assign( vec ); if ( vecnorm.length2() < Bump.SIMD_EPSILON * Bump.SIMD_EPSILON ) { vecnorm.setValue( -1, -1, -1 ); } vecnorm.normalize(); - supVertex.addSelf( vecnorm.multiplyScalar( this.getMargin() ) ); + supVertex.addSelf( vecnorm.multiplyScalar( this.getMargin(), tmpV1 ) ); return supVertex; }, diff --git a/src/BulletCollision/NarrowPhaseCollision/RaycastCallback.js b/src/BulletCollision/NarrowPhaseCollision/RaycastCallback.js index a0124ff..163aa39 100644 --- a/src/BulletCollision/NarrowPhaseCollision/RaycastCallback.js +++ b/src/BulletCollision/NarrowPhaseCollision/RaycastCallback.js @@ -16,6 +16,17 @@ }, members: { + + // ASD: added for easy recycling, since init() calls clone + set: function( from, to, flags ) { + this.from.assign( from ); + this.to.assign( to ); + this.flags = flags || 0; + this.hitFraction = 1; + + return this; + }, + processTriangle: function( triangle, // originally btVector3*, ported as array partId, diff --git a/src/BulletCollision/NarrowPhaseCollision/SubSimplexConvexCast.js b/src/BulletCollision/NarrowPhaseCollision/SubSimplexConvexCast.js index 68daf99..f6ee107 100644 --- a/src/BulletCollision/NarrowPhaseCollision/SubSimplexConvexCast.js +++ b/src/BulletCollision/NarrowPhaseCollision/SubSimplexConvexCast.js @@ -1,17 +1,26 @@ // load: bump.js // load: BulletCollision/NarrowPhaseCollision/ConvexCast.js +// load: LinearMath/Vector3.js +// load: LinearMath/Transform.js -// run: LinearMath/Vector3.js -// run: LinearMath/Transform.js // run: BulletCollision/CollisionShapes/ConvexShape.js (function( window, Bump ) { - var MAX_ITERATIONS = 64; - var tmpV1 = Bump.Vector3.create(); var tmpV2 = Bump.Vector3.create(); - var tmpV3 = Bump.Vector3.create(); + + var tmpTITA = Bump.Transform.getIdentity(); + var tmpTITB = Bump.Transform.getIdentity(); + + var tmpVr = Bump.Vector3.create(); + var tmpVSVA = Bump.Vector3.create(); + var tmpVSVB = Bump.Vector3.create(); + var tmpVv = Bump.Vector3.create(); + var tmpVn = Bump.Vector3.create(); + var tmpVw = Bump.Vector3.create(); + + var MAX_ITERATIONS = 64; Bump.SubsimplexConvexCast = Bump.type({ parent: Bump.ConvexCast, @@ -34,19 +43,18 @@ toB, // Bump.Transform result // CastResult ) { - this.simplexSolver.reset(); - var linVelA = toA.getOrigin().subtract( fromA.getOrigin() ); - var linVelB = toB.getOrigin().subtract( fromB.getOrigin() ); + var linVelA = toA.getOrigin().subtract( fromA.getOrigin(), tmpV1 ); + var linVelB = toB.getOrigin().subtract( fromB.getOrigin(), tmpV2 ); var lambda = 0; - var interpolatedTransA = fromA.clone(); - var interpolatedTransB = fromB.clone(); + var interpolatedTransA = tmpTITA.assign( fromA ); + var interpolatedTransB = tmpTITB.assign( fromB ); // take relative motion - var r = linVelA.subtract( linVelB ); + var r = linVelA.subtract( linVelB, tmpVr ); var v; var supVertexA = fromA.transform( @@ -54,7 +62,7 @@ fromA.getBasis().vectorMultiply( r.negate( tmpV1 ), tmpV1 ), tmpV1 ), - tmpV1 + tmpVSVA ); var supVertexB = fromB.transform( @@ -62,16 +70,16 @@ fromB.getBasis().vectorMultiply( r, tmpV2 ), tmpV2 ), - tmpV2 + tmpVSVB ); - v = supVertexA.subtract( supVertexB ); + v = supVertexA.subtract( supVertexB, tmpVv ); var maxIter = MAX_ITERATIONS; - var n = Bump.Vector3.create(); + var n = tmpVn.setZero(); // n.setValue( 0, 0, 0 ); var hasResult = false; - var c = Bump.Vector3.create(); + // var c = tmpVc.setZero(); // ASD: not used? var lastLambda = lambda; @@ -82,8 +90,8 @@ // #else // btScalar epsilon = btScalar(0.0001); // #endif //BT_USE_DOUBLE_PRECISION - var w = Bump.Vector3.create(); - var p = Bump.Vector3.create(); + var w = tmpVw.setZero(); + // var p = tmpVp.setZero(); // ASD: not used? var VdotR; while ( ( dist2 > epsilon ) && maxIter-- ) { @@ -92,14 +100,14 @@ interpolatedTransA.getBasis().vectorMultiply( v.negate( tmpV1 ), tmpV1 ), tmpV1 ), - tmpV1 + tmpVSVA ); supVertexB = interpolatedTransB.transform( this.convexB.localGetSupportingVertex( interpolatedTransB.getBasis().vectorMultiply( v, tmpV2 ), tmpV2 ), - tmpV2 + tmpVSVB ); supVertexA.subtract( supVertexB, w ); @@ -172,7 +180,7 @@ // var hitB = Bump.Vector3.create(); // this.simplexSolver.compute_points( hitA, hitB ); // result.hitPoint = hitB; - this.simplexSolver.compute_points( tmpV3, result.hitPoint ); + this.simplexSolver.compute_points( tmpV1, result.hitPoint ); return true; } diff --git a/src/BulletCollision/NarrowPhaseCollision/VoronoiSimplexSolver.js b/src/BulletCollision/NarrowPhaseCollision/VoronoiSimplexSolver.js index bf3a99e..b967459 100644 --- a/src/BulletCollision/NarrowPhaseCollision/VoronoiSimplexSolver.js +++ b/src/BulletCollision/NarrowPhaseCollision/VoronoiSimplexSolver.js @@ -3,6 +3,26 @@ // run: LinearMath/Vector3.js (function( window, Bump ) { + var createGetter = function( Type, pool ) { + return function() { + return pool.pop() || Type.create(); + }; + }; + + var createDeller = function( pool ) { + return function() { + for ( var i = 0; i < arguments.length; ++i ) { + pool.push( arguments[i] ); + } + }; + }; + + // memory pool for recycling Vector3 objects + var vector3Pool = []; + + var getVector3 = createGetter( Bump.Vector3, vector3Pool ); + var delVector3 = createDeller( vector3Pool ); + var VORONOI_SIMPLEX_MAX_VERTS = 5, VORONOI_DEFAULT_EQUAL_VERTEX_THRESHOLD = 0.0001, VERTA = 0, @@ -105,13 +125,14 @@ this.degenerate = false; this.setBarycentricCoordinates(); this.usedVertices.reset(); + return this; }, isValid: function() { - var valid = ( ( this.barycentricCoords[0] >= 0 ) && - ( this.barycentricCoords[1] >= 0 ) && - ( this.barycentricCoords[2] >= 0 ) && - ( this.barycentricCoords[3] >= 0 ) ); + var valid = (( this.barycentricCoords[0] >= 0 ) && + ( this.barycentricCoords[1] >= 0 ) && + ( this.barycentricCoords[2] >= 0 ) && + ( this.barycentricCoords[3] >= 0 )); return valid; }, @@ -130,6 +151,17 @@ } }); + + // memory pool for recycling SubSimplexClosestResult objects + var subSimplexClosestResultPool = []; + + var getSubSimplexClosestResult = createGetter( + Bump.SubSimplexClosestResult, + subSimplexClosestResultPool + ); + var delSubSimplexClosestResult = createDeller( subSimplexClosestResultPool ); + + Bump.VoronoiSimplexSolver = Bump.type({ init: function VoronoiSimplexSolver() { this.numVertices = 0; @@ -185,7 +217,12 @@ }, updateClosestVectorAndPoints: function() { - var tmpV1, tmpV2, p, a, b, c, d; + var tmpV1 = getVector3().setZero(); + var tmpV2 = getVector3().setZero(); + var tmpV3 = getVector3().setZero(); + var tmpV4 = getVector3().setZero(); + var p = getVector3().setZero(); + var a, b, c, d; if ( this.needsUpdate ) { this.cachedBC.reset(); @@ -198,11 +235,9 @@ break; case 1: - tmpV1 = Bump.Vector3.create(); - this.cachedP1.assign( this.simplexPointsP[0] ); this.cachedP2.assign( this.simplexPointsQ[0] ); - this.cachedV.assign( this.cachedP1.subtract( this.cachedP2 ) ); + this.cachedV.assign( this.cachedP1.subtract( this.cachedP2, tmpV1 )); this.cachedBC.reset(); this.cachedBC.setBarycentricCoordinates( 1, 0, 0, 0 ); this.cachedValidClosest = this.cachedBC.isValid(); @@ -210,16 +245,13 @@ // Closest point origin from line segment. case 2: - tmpV1 = Bump.Vector3.create(); - - var from = this.simplexVectorW[0], - to = this.simplexVectorW[1], - nearest = Bump.Vector3.create(); + var from = this.simplexVectorW[0]; + var to = this.simplexVectorW[1]; + // nearest = Bump.Vector3.create(); - p = Bump.Vector3.create( 0, 0, 0 ); - var diff = p.subtract( from ), - v = to.subtract( from ), - t = v.dot( diff ); + var diff = p.subtract( from, tmpV3 ); + var v = to.subtract( from, tmpV4 ); + var t = v.dot( diff ); if ( t > 0 ) { var dotVV = v.dot( v ); @@ -240,7 +272,8 @@ this.cachedBC.usedVertices.usedVertexA = true; } this.cachedBC.setBarycentricCoordinates( 1 - t, t ); - nearest.assign( from.add( v.multiplyScalar( t, tmpV1 ), tmpV1 ) ); + // !!!: ASD: commented this out since it is apparently never used + // nearest.assign( from.add( v.multiplyScalar( t, tmpV1 ), tmpV1 ) ); this.cachedP1.assign( this.simplexPointsP[0].add( this.simplexPointsP[1].subtract( this.simplexPointsP[0], tmpV1 ).multiplyScalar( t, tmpV1 ), tmpV1 ) ); this.cachedP2.assign( this.simplexPointsQ[0].add( this.simplexPointsQ[1].subtract( this.simplexPointsQ[0], tmpV1 ).multiplyScalar( t, tmpV1 ), tmpV1 ) ); @@ -253,11 +286,6 @@ // Closest point origin from triangle. case 3: - tmpV1 = Bump.Vector3.create(); - tmpV2 = Bump.Vector3.create(); - - p = Bump.Vector3.create( 0, 0, 0 ); - a = this.simplexVectorW[0]; b = this.simplexVectorW[1]; c = this.simplexVectorW[2]; @@ -283,11 +311,6 @@ break; case 4: - tmpV1 = Bump.Vector3.create(); - tmpV2 = Bump.Vector3.create(); - - p = Bump.Vector3.create( 0, 0, 0 ); - a = this.simplexVectorW[0]; b = this.simplexVectorW[1]; c = this.simplexVectorW[2]; @@ -310,7 +333,7 @@ .add( this.simplexPointsQ[3].multiplyScalar( this.cachedBC.barycentricCoords[3], tmpV1 ), tmpV2 ) ); - this.cachedV.assign( this.cachedP1.subtract( this.cachedP2 ) ); + this.cachedV.assign( this.cachedP1.subtract( this.cachedP2, tmpV1 ) ); this.reduceVertices( this.cachedBC.usedVertices ); } else { // console.log( 'sub distance got penetration' ); @@ -335,16 +358,22 @@ } } + delVector3( tmpV1 ); + delVector3( tmpV2 ); + delVector3( tmpV3 ); + delVector3( tmpV4 ); + delVector3( p ); + return this.cachedValidClosest; }, closestPtPointTetrahedron: function( p, a, b, c, d, finalResult ) { - var tempResult = Bump.SubSimplexClosestResult.create(); - // Start out assuming point inside all halfspaces, so closest to itself. finalResult.closestPointOnSimplex.assign( p ); - finalResult.usedVertices.reset(); + // ASD: removed reset() since this is immediately undone by setting + // all the usedVertex values to true + // finalResult.usedVertices.reset(); finalResult.usedVertices.usedVertexA = true; finalResult.usedVertices.usedVertexB = true; finalResult.usedVertices.usedVertexC = true; @@ -364,9 +393,11 @@ return false; } - var tmpV1 = Bump.Vector3.create(), - tmpV2 = Bump.Vector3.create(), - tmpV3 = Bump.Vector3.create(); + // get some recycled temporaries + var tempResult = getSubSimplexClosestResult().reset(); + var tmpV1 = getVector3().setZero(); + var tmpV2 = getVector3().setZero(); + var tmpV3 = getVector3().setZero(); var q, sqDist, bestSqDist = Infinity; // If point outside face abc then compute closest point on abc @@ -453,7 +484,7 @@ q = tempResult.closestPointOnSimplex.clone( tmpV1 ); // Convert result bitmask! - sqDist = q.subtract( p ).dot( q.subtract( p ) ); + sqDist = q.subtract( p, tmpV2 ).dot( q.subtract( p, tmpV3 ) ); if ( sqDist < bestSqDist ) { bestSqDist = sqDist; finalResult.closestPointOnSimplex.assign( q ); @@ -473,21 +504,26 @@ } } + // ASD: commented out because unnecessary // Help! We ended up full! - if ( finalResult.usedVertices.usedVertexA && - finalResult.usedVertices.usedVertexB && - finalResult.usedVertices.usedVertexC && - finalResult.usedVertices.usedVertexD ) - { - return true; - } + // if ( finalResult.usedVertices.usedVertexA && + // finalResult.usedVertices.usedVertexB && + // finalResult.usedVertices.usedVertexC && + // finalResult.usedVertices.usedVertexD ) + // { + // return true; + // } + + // deallocate the temporaries + delSubSimplexClosestResult( tempResult ); + delVector3( tmpV1, tmpV2, tmpV3 ); return true; }, pointOutsideOfPlane: function( p, a, b, c, d ) { - var tmpV1 = Bump.Vector3.create(), - tmpV2 = Bump.Vector3.create(); + var tmpV1 = getVector3(); + var tmpV2 = getVector3(); // `normal` uses `tmpV2`. var normal = b.subtract( a, tmpV1 ).cross( c.subtract( a, tmpV2 ), tmpV2 ); @@ -495,6 +531,8 @@ var signp = p.subtract( a, tmpV1 ).dot( normal ), // [AP AB AC] signd = d.subtract( a, tmpV1 ).dot( normal ); // [AD AB AC] + delVector3( tmpV1, tmpV2 ); + if ( signd * signd < ( 1e-8 * 1e-8 ) ) { return -1; } @@ -507,9 +545,13 @@ result.usedVertices.reset(); - var tmpV1 = Bump.Vector3.create(), - ab = b.subtract( a ), - ac = c.subtract( a ); + var tmpV1 = getVector3(); + var tmpV2 = getVector3(); + var tmpVab = getVector3(); + var tmpVac = getVector3(); + + var ab = b.subtract( a, tmpVab ); + var ac = c.subtract( a, tmpVac ); // Check if P in vertex region outside A. var ap = p.subtract( a, tmpV1 ), @@ -519,6 +561,8 @@ result.closestPointOnSimplex.assign( a ); result.usedVertices.usedVertexA = true; result.setBarycentricCoordinates( 1, 0, 0 ); + + delVector3( tmpV1, tmpV2, tmpVab, tmpVac ); return true; } @@ -531,6 +575,7 @@ result.usedVertices.usedVertexB = true; result.setBarycentricCoordinates( 0, 1, 0 ); + delVector3( tmpV1, tmpV2, tmpVab, tmpVac ); return true; } @@ -545,6 +590,8 @@ result.usedVertices.usedVertexA = true; result.usedVertices.usedVertexB = true; result.setBarycentricCoordinates( 1 - v, v, 0 ); + + delVector3( tmpV1, tmpV2, tmpVab, tmpVac ); return true; } @@ -556,6 +603,8 @@ result.closestPointOnSimplex.assign( c ); result.usedVertices.usedVertexC = true; result.setBarycentricCoordinates( 0, 0, 1 ); + + delVector3( tmpV1, tmpV2, tmpVab, tmpVac ); return true; } @@ -568,6 +617,8 @@ result.usedVertices.usedVertexA = true; result.usedVertices.usedVertexC = true; result.setBarycentricCoordinates( 1 - w, 0, w ); + + delVector3( tmpV1, tmpV2, tmpVab, tmpVac ); return true; } @@ -585,6 +636,8 @@ result.usedVertices.usedVertexB = true; result.usedVertices.usedVertexC = true; result.setBarycentricCoordinates( 0, 1 - w, w ); + + delVector3( tmpV1, tmpV2, tmpVab, tmpVac ); return true; } @@ -594,7 +647,6 @@ v = vb * denom; w = vc * denom; - var tmpV2 = Bump.Vector3.create(); result.closestPointOnSimplex.assign( a .add( ab.multiplyScalar( v, tmpV2 ), tmpV1 ) @@ -605,6 +657,7 @@ result.usedVertices.usedVertexC = true; result.setBarycentricCoordinates( 1 - v - w, v, w ); + delVector3( tmpV1, tmpV2, tmpVab, tmpVac ); return true; }, diff --git a/src/LinearMath/AabbUtil2.js b/src/LinearMath/AabbUtil2.js index 0956702..2ed8adc 100644 --- a/src/LinearMath/AabbUtil2.js +++ b/src/LinearMath/AabbUtil2.js @@ -66,17 +66,17 @@ AabbUtil2.RayAabb2 = function( rayFrom, rayInvDirection, raySign, bounds, tmin, lambda_min, lambda_max ) { var tmax, tymin, tymax, tzmin, tzmax; - tmin.tmin = ( bounds[ raySign[0] ].x - rayFrom.x ) * rayInvDirection.x; + tmin.value = ( bounds[ raySign[0] ].x - rayFrom.x ) * rayInvDirection.x; tmax = ( bounds[ 1 - raySign[0] ].x - rayFrom.x ) * rayInvDirection.x; tymin = ( bounds[ raySign[1] ].y - rayFrom.y ) * rayInvDirection.y; tymax = ( bounds[ 1 - raySign[1] ].y - rayFrom.y ) * rayInvDirection.y; - if ( ( tmin.tmin > tymax ) || ( tymin > tmax ) ) { + if ( ( tmin.value > tymax ) || ( tymin > tmax ) ) { return false; } - if ( tymin > tmin.tmin ) { - tmin.tmin = tymin; + if ( tymin > tmin.value ) { + tmin.value = tymin; } if ( tymax < tmax ) { @@ -86,19 +86,19 @@ tzmin = ( bounds[ raySign[2] ].z - rayFrom.z ) * rayInvDirection.z; tzmax = ( bounds[ 1 - raySign[2] ].z - rayFrom.z ) * rayInvDirection.z; - if ( ( tmin.tmin > tzmax ) || ( tzmin > tmax ) ) { + if ( ( tmin.value > tzmax ) || ( tzmin > tmax ) ) { return false; } - if ( tzmin > tmin.tmin ) { - tmin.tmin = tzmin; + if ( tzmin > tmin.value ) { + tmin.value = tzmin; } if ( tzmax < tmax ) { tmax = tzmax; } - return ( ( tmin.tmin < lambda_max ) && ( tmax > lambda_min ) ); + return ( tmin.value < lambda_max ) && ( tmax > lambda_min ); }; AabbUtil2.RayAabb = function( rayFrom, rayTo, aabbMin, aabbMax, param, normal ) { diff --git a/tests/js/performance.js b/tests/js/performance.js index c7d63e4..73460bb 100644 --- a/tests/js/performance.js +++ b/tests/js/performance.js @@ -103,6 +103,7 @@ var groundRot = Bump.Quaternion.createWithEuler( 0, 0, Math.PI * 0.003 ); var quat = Bump.Quaternion.create(); var newTransform = Bump.Transform.create(); + var tmpMat = Bump.Matrix3x3.create(); var rate = Math.PI / 60 / 5; var amp = rate / 2; @@ -113,13 +114,14 @@ var step = function () { time += 16; - stats.begin(); groundRot.setEuler( 0, 0, rate + amp * Math.sin( time / 500 ) ); groundBody.getMotionState().getWorldTransform( newTransform ); - newTransform.basis.multiplyMatrix( Bump.Matrix3x3.createWithQuaternion( groundRot ), newTransform.basis ); + newTransform.basis.multiplyMatrix( tmpMat.setRotation( groundRot ), newTransform.basis ); groundBody.getMotionState().setWorldTransform( newTransform ); + stats.begin(); dynamicsWorld.stepSimulation( 0.016 ); + stats.end(); for ( var i = 0; i < dynamicsWorld.getNumCollisionObjects(); ++i ) { var colObj = dynamicsWorld.getCollisionObjectArray()[i]; @@ -132,7 +134,6 @@ } renderer.render(); - stats.end(); window.requestAnimationFrame( step ); }; diff --git a/tests/js/ray-performance.js b/tests/js/ray-performance.js new file mode 100644 index 0000000..a56515c --- /dev/null +++ b/tests/js/ray-performance.js @@ -0,0 +1,201 @@ +/*global Stats:false THREEWrapper:false*/ + +(function() { + var stats = new Stats(); + // stats.setMode(1); + + // Align top-left + stats.domElement.style.position = 'absolute'; + stats.domElement.style.left = '0px'; + stats.domElement.style.top = '0px'; + + document.body.appendChild( stats.domElement ); + + var collisionConfiguration = Bump.DefaultCollisionConfiguration.create(); + var dispatcher = Bump.CollisionDispatcher.create( collisionConfiguration ); + var overlappingPairCache = Bump.DbvtBroadphase.create(); + var solver = Bump.SequentialImpulseConstraintSolver.create(); + var dynamicsWorld = Bump.DiscreteDynamicsWorld.create( dispatcher, overlappingPairCache, solver, collisionConfiguration ); + dynamicsWorld.setGravity( Bump.Vector3.create( 0, -9.8, 0 ) ); + + var collisionShapes = []; + + (function(){ + var _DOWN = Bump.Vector3.create( 0, -1, 0 ); + var _down = Bump.Vector3.create(); + var _from = Bump.Vector3.create(); + var _to = Bump.Vector3.create(); + + // for now test the 4 corners + var testPoints = [ + Bump.Vector3.create( -1, -1, -1 ), + Bump.Vector3.create( -1, -1, 1 ), + Bump.Vector3.create( 1, -1, -1 ), + Bump.Vector3.create( 1, -1, 1 ) + ]; + + var boxTrans = Bump.Transform.getIdentity(); + + var rayCallback = Bump.CollisionWorld.ClosestRayResultCallback.create( _from, _to ); + rayCallback.collisionFilterMask = + Bump.BroadphaseProxy.CollisionFilterGroups.AllFilter; + + // set up a internal pre-tick callback to test a bunch of raycasts per object + dynamicsWorld.setInternalTickCallback( function() { + + // cast rays for each collision object, slipping object 0, + // which is the dryer + for ( var i = 1; i < dynamicsWorld.getNumCollisionObjects(); ++i ) { + var colObj = dynamicsWorld.getCollisionObjectArray()[ i ]; + var body = Bump.RigidBody.upcast( colObj ); + body.getMotionState().getWorldTransform( boxTrans ); + + boxTrans.multiplyVector( _DOWN, _down ); + + for ( var j = 0; j < testPoints.length; j++ ) { + boxTrans.multiplyVector( testPoints[ j ], _from ); + _from.add( _down, _to ); + rayCallback.rayFromWorld = _from; + rayCallback.rayToWorld = _to; + + dynamicsWorld.rayTest( _from, _to, rayCallback ); + + if( rayCallback.hasHit() ) { + // do something + } + } + } + }, undefined, true ); + }()); + var groundBody; + (function( size ) { + var groundHalfExtents = Bump.Vector3.create( size, size, size ); + var groundBoxShape = Bump.BoxShape.create( groundHalfExtents ); + var groundShape = Bump.CompoundShape.create(); + + collisionShapes.push( groundShape ); + collisionShapes.push( groundBoxShape ); + + var sizeAndHalf = 1.5 * size; + [ + Bump.Vector3.create( 0, -sizeAndHalf, 0 ), + Bump.Vector3.create( 0, sizeAndHalf, 0 ), + Bump.Vector3.create( -sizeAndHalf, 0, 0 ), + Bump.Vector3.create( sizeAndHalf, 0, 0 ), + Bump.Vector3.create( 0, 0, -sizeAndHalf ), + Bump.Vector3.create( 0, 0, sizeAndHalf ) + ].forEach(function( position ) { + var localTransform = Bump.Transform.getIdentity(); + localTransform.setOrigin( position ); + + groundShape.addChildShape( localTransform, groundBoxShape ); + }); + + var groundTransform = Bump.Transform.getIdentity(); + + var myMotionState = Bump.DefaultMotionState.create( groundTransform ); + var rbInfo = Bump.RigidBody.RigidBodyConstructionInfo.create( 0, myMotionState, groundShape, Bump.Vector3.create() ); + // var rbInfo = Bump.RigidBody.RigidBodyConstructionInfo.create( 0, myMotionState, groundBoxShape, Bump.Vector3.create() ); + groundBody = Bump.RigidBody.create( rbInfo ); + + groundBody.setCollisionFlags( groundBody.getCollisionFlags() | Bump.CollisionObject.CollisionFlags.CF_KINEMATIC_OBJECT ); + groundBody.setActivationState( Bump.CollisionObject.DISABLE_DEACTIVATION ); + + dynamicsWorld.addRigidBody( groundBody ); + }( 20 )); + + var boxCubeShape = Bump.BoxShape.create( Bump.Vector3.create( 0.5, 0.5, 0.5 ) ); + collisionShapes.push( boxCubeShape ); + + var createCube = function( i, j, k, startRotation ) { + if ( !startRotation ) { + startRotation = Bump.Quaternion.getIdentity(); + } + + var localInertia = Bump.Vector3.create(); + boxCubeShape.calculateLocalInertia( 1, localInertia ); + + var startTransform = Bump.Transform.create(); + startTransform.setIdentity(); + startTransform.setRotation( startRotation ); + startTransform.setOrigin( Bump.Vector3.create( i, j, k ) ); + + var myMotionState = Bump.DefaultMotionState.create( startTransform ); + var rbInfo = Bump.RigidBody.RigidBodyConstructionInfo.create( 1, myMotionState, boxCubeShape, localInertia ); + var body = Bump.RigidBody.create( rbInfo ); + + body.setFriction( 0.1 ); + + dynamicsWorld.addRigidBody( body ); + }; + + var renderer = new THREEWrapper(); + renderer.init(); + + renderer.addBox({ size: 20, wireframe: true }); + + (function() { + var num = 2; + var j = 4; + for ( var i = 0; i < num; ++i ) { + for ( var k = 0; k < num; ++k ) { + createCube( i - (num - 1) / 2, j, k - (num - 1) / 2 ); + + renderer.addBox({ size: 1 }); + + } + } + }()); + + var groundRot = Bump.Quaternion.createWithEuler( 0, 0, Math.PI * 0.003 ); + var quat = Bump.Quaternion.create(); + var newTransform = Bump.Transform.create(); + + var rate = Math.PI / 60 / 5; + var amp = rate / 2; + + var startSimulation = function() { + var time = 0; + + var step = function () { + time += 160; + + groundRot.setEuler( 0, 0, rate + amp * Math.sin( time / 500 ) ); + groundBody.getMotionState().getWorldTransform( newTransform ); + newTransform.basis.multiplyMatrix( Bump.Matrix3x3.createWithQuaternion( groundRot ), newTransform.basis ); + groundBody.getMotionState().setWorldTransform( newTransform ); + + stats.begin(); + dynamicsWorld.stepSimulation( 0.16, 20, 0.016 ); + stats.end(); + + for ( var i = 0; i < dynamicsWorld.getNumCollisionObjects(); ++i ) { + var colObj = dynamicsWorld.getCollisionObjectArray()[i]; + var body = Bump.RigidBody.upcast( colObj ); + body.getMotionState().getWorldTransform( newTransform ); + + var mesh = renderer.meshes[ i ]; + mesh.position.copy( newTransform.origin ); + mesh.quaternion.copy( newTransform.getRotation( quat ) ); + } + + renderer.render(); + + window.requestAnimationFrame( step ); + }; + + window.requestAnimationFrame( step ); + }; + + var keylistener = function( evt ) { + // Enter key + if ( evt.keyCode === 13 ) { + document.body.removeEventListener( 'keyup', keylistener ); + + startSimulation(); + } + }; + + document.body.addEventListener( 'keyup', keylistener ); + +}()); diff --git a/tests/ray-performance.html b/tests/ray-performance.html new file mode 100644 index 0000000..3180046 --- /dev/null +++ b/tests/ray-performance.html @@ -0,0 +1,25 @@ + + +
+ +