return function ( dx, dy ) { var proj = this._camera.getProjectionMatrix(); // modulate panning speed with verticalFov value // if it's an orthographic we don't change the panning speed // TODO : manipulators in osgjs don't support well true orthographic camera anyway because they // manage the view matrix (and you need to edit the projection matrix to 'zoom' for true ortho camera) var vFov = proj[ 15 ] === 1 ? 1.0 : 2.00 / proj[ 5 ]; var speed = this.getSpeedFactor() * vFov; dy *= speed; dx *= speed; Matrix.inverse( this._rotation, inv ); x[ 0 ] = Matrix.get( inv, 0, 0 ); x[ 1 ] = Matrix.get( inv, 0, 1 ); x[ 2 ] = Matrix.get( inv, 0, 2 ); Vec3.normalize( x, x ); y[ 0 ] = Matrix.get( inv, 2, 0 ); y[ 1 ] = Matrix.get( inv, 2, 1 ); y[ 2 ] = Matrix.get( inv, 2, 2 ); Vec3.normalize( y, y ); Vec3.mult( x, -dx, x ); Vec3.mult( y, dy, y ); Vec3.add( this._target, x, this._target ); Vec3.add( this._target, y, this._target ); };
return function ( intersections, start, end, nodePath ) { var d = Vec3.sub( end, start, dir ); var len = Vec3.length( d ); var invLen = 0.0; if ( len !== 0.0 ) invLen = 1.0 / len; Vec3.mult( d, invLen, d ); if ( d[ 0 ] !== 0.0 ) Vec3.mult( d, 1.0 / d[ 0 ], this._dInvX ); if ( d[ 1 ] !== 0.0 ) Vec3.mult( d, 1.0 / d[ 1 ], this._dInvY ); if ( d[ 2 ] !== 0.0 ) Vec3.mult( d, 1.0 / d[ 2 ], this._dInvZ ); this._intersector._intersections = intersections; this._intersector.setNodePath( nodePath ); this._intersector.set( start, end ); };
return function ( yawDelta, pitchDelta ) { Quat.transformVec3( this._orientation, this._right, right ); Vec3.normalize( right, rightNormalized ); Vec3.sub( this._eye, this._pivotPoint, dir ); var scalar = Vec3.dot( rightNormalized, dir ); Vec3.sub( dir, Vec3.mult( rightNormalized, scalar, rightScalar ), offset ); var xy = Vec3.createAndSet( -offset[ 0 ], -offset[ 1 ], 0 ); var positionPitch = Math.atan2( -offset[ 2 ], Vec3.length( xy ) ); pitchDelta = Math.max( -Math.PI / 2 + 0.01, Math.min( Math.PI / 2 - 0.01, ( positionPitch + pitchDelta ) ) ) - positionPitch; Quat.makeRotate( pitchDelta * this._rotateFactor, right[ 0 ], right[ 1 ], right[ 2 ], pitchQuat ); Quat.makeRotate( yawDelta * this._rotateFactor, this._upz[ 0 ], this._upz[ 1 ], this._upz[ 2 ], yawQuat ); Quat.mult( yawQuat, pitchQuat, pitchyawQuat ); Quat.transformVec3( pitchyawQuat, dir, tmp ); Vec3.add( tmp, this._pivotPoint, this._eye ); // Find rotation offset and target Quat.mult( yawQuat, this._orientation, this._orientation ); Quat.transformVec3( this._orientation, this._right, right ); Quat.makeRotate( pitchDelta * this._rotateFactor, right[ 0 ], right[ 1 ], right[ 2 ], pitchQuat ); Quat.mult( pitchQuat, this._orientation, this._orientation ); };
generate: function ( geometry, texCoordUnit ) { this._texCoordUnit = texCoordUnit; if ( this._texCoordUnit === undefined ) this._texCoordUnit = 0; var size = geometry.getAttributes().Vertex.getElements().length; this._T = new osg.Float32Array( size ); this._B = new osg.Float32Array( size ); this._N = new osg.Float32Array( size ); geometry.getPrimitiveSetList().forEach( function ( primitiveSet ) { this.computePrimitiveSet( geometry, primitiveSet ); }, this ); var nbElements = size / 3; var tangents = new osg.Float32Array( nbElements * 4 ); var tmp0 = Vec3.create(); var tmp1 = Vec3.create(); var t3 = Vec3.create(); for ( var i = 0; i < nbElements; i++ ) { var t = this._T.subarray( i * 3, i * 3 + 3 ); var n = this._N.subarray( i * 3, i * 3 + 3 ); var b = this._B.subarray( i * 3, i * 3 + 3 ); Vec3.normalize( n, n ); // Gram-Schmidt orthogonalize // Vec3 t3 = (t - n * (n * t)); // t3.normalize(); // finalTangent = Vec4(t3, 0.0); // Calculate handedness // finalTangent[3] = (((n ^ t) * b) < 0.0) ? -1.0 : 1.0; // The bitangent vector B is then given by B = (N × T) · Tw var nt = Vec3.dot( n, t ); Vec3.mult( n, nt, tmp1 ); Vec3.sub( t, tmp1, tmp0 ); Vec3.normalize( tmp0, t3 ); Vec3.cross( n, t, tmp0 ); var sign = Vec3.dot( tmp0, b ); sign = sign < 0.0 ? -1.0 : 0.0; // TODO perf : cache index var id = i * 4; tangents[ i * 4 ] = t3[ 0 ]; tangents[ i * 4 + 1 ] = t3[ 1 ]; tangents[ i * 4 + 2 ] = t3[ 2 ]; tangents[ i * 4 + 3 ] = sign; } geometry.getAttributes().Normal.setElements( this._N ); geometry.getAttributes().Tangent = new BufferArray( 'ARRAY_BUFFER', tangents, 4 ); },
set: function ( start, end ) { this._start = start; this._end = end; this._dir = Vec3.sub( end, start, this._dir ); this._length = Vec3.length( this._dir ); this._invLength = 1.0 / this._length; Vec3.mult( this._dir, this._invLength, this._dir ); },
return function () { if ( this._lines.length > 0 ) return this._lines; // Polytope lines already calculated var selectorMask = 0x1; for ( var i = 0, j = this._planes.length; i < j; i++, selectorMask <<= 1 ) { Vec3.copy( this.getNormal( this._planes[ i ] ), normal1 ); Vec3.mult( normal1, -this._planes[ i ][ 3 ], point1 ); // canonical point on plane[ i ] var subSelectorMask = ( selectorMask << 1 ); for ( var jt = i + 1, k = this._planes.length; jt < k; ++jt, subSelectorMask <<= 1 ) { Vec3.copy( this.getNormal( this._planes[ jt ] ), normal2 ); if ( Math.abs( Vec3.dot( normal1, normal2 ) ) > ( 1.0 - epsilon ) ) continue; Vec3.cross( normal1, normal2, lineDirection ); Vec3.cross( lineDirection, normal1, searchDirection ); //-plane2.distance(point1)/(searchDirection*normal2); var searchDist = -this.distance( this._planes[ jt ], point1 ) / Vec3.dot( searchDirection, normal2 ); if ( osgMath.isNaN( searchDist ) ) continue; Vec3.mult( searchDirection, searchDist, linePoint ); Vec3.add( point1, lineDirection, lineDirection ); this._lines.push( new PlanesLine( selectorMask | subSelectorMask, Vec3.copy( linePoint, Vec3.create() ), Vec3.copy( lineDirection, Vec3.create() ) ) ); } } return this._lines; };
return function ( ratio ) { var newValue = this._distance + this.getSpeedFactor() * ( ratio - 1.0 ); if ( newValue < this._minDistance ) { if ( this._autoPushTarget ) { // push the target instead of zooming on it Vec3.sub( this._target, this.getEyePosition( dir ), dir ); Vec3.normalize( dir, dir ); Vec3.mult( dir, this._minDistance - newValue, dir ); Vec3.add( this._target, dir, this._target ); } newValue = this._minDistance; } if ( newValue > this._maxDistance ) newValue = this._maxDistance; this._distance = newValue; };
var PolytopeIntersection = function ( index, candidates, candidatesMasks, referencePlane, nodePath ) { this._index = index - 1; ///< primitive index this._distance = 0; ///< distance from reference plane this._maxDistance = -1; ///< maximum distance of intersection points from reference plane this._numPoints = 0; this._points = []; this._maxNumIntersections = 6; this._center = Vec3.create(); for ( var i = 0, j = candidates.length; i < j; i++ ) { if ( candidatesMasks[ i ] === 0 ) continue; this._points[ this._numPoints++ ] = Vec3.copy( candidates[ i ], Vec3.create() ); Vec3.add( this._center, candidates[ i ], this._center ); var distance = referencePlane[ 0 ] * candidates[ i ][ 0 ] + referencePlane[ 1 ] * candidates[ i ][ 1 ] + referencePlane[ 2 ] * candidates[ i ][ 2 ] + referencePlane[ 3 ]; if ( distance > this._maxDistance ) this._maxDistance = distance; if ( this._numPoints === this._maxNumIntesections ) break; } Vec3.mult( this._center, 1 / this._numPoints, this._center ); this._distance = referencePlane[ 0 ] * this._center[ 0 ] + referencePlane[ 1 ] * this._center[ 1 ] + referencePlane[ 2 ] * this._center[ 2 ] + referencePlane[ 3 ]; this.nodePath = nodePath; };
return function ( bb ) { if ( !bb.valid() ) return; if ( this.valid() ) { Vec3.copy( bb._min, newbb._min ); Vec3.copy( bb._max, newbb._max ); for ( var i = 0; i < 8; i++ ) { Vec3.sub( bb.corner( i, v ), this._center, v ); // get the direction vector from corner Vec3.normalize( v, v ); // normalise it. Vec3.mult( v, -this._radius, v ); // move the vector in the opposite direction distance radius. Vec3.add( v, this._center, v ); // move to absolute position. newbb.expandByVec3( v ); // add it into the new bounding box. } newbb.center( this._center ); this._radius = newbb.radius(); } else { bb.center( this._center ); this._radius = bb.radius(); } };
return function ( s, e, bb ) { var min = bb._min; var xmin = min[ 0 ]; var ymin = min[ 1 ]; var zmin = min[ 2 ]; var max = bb._max; var xmax = max[ 0 ]; var ymax = max[ 1 ]; var zmax = max[ 2 ]; var invX = this._dInvX; var invY = this._dInvY; var invZ = this._dInvZ; if ( s[ 0 ] <= e[ 0 ] ) { // trivial reject of segment wholely outside. if ( e[ 0 ] < xmin ) return false; if ( s[ 0 ] > xmax ) return false; if ( s[ 0 ] < xmin ) { // clip s to xMin. Vec3.mult( invX, xmin - s[ 0 ], tmp ); Vec3.add( s, tmp, s ); } if ( e[ 0 ] > xmax ) { // clip e to xMax. Vec3.mult( invX, xmax - s[ 0 ], tmp ); Vec3.add( s, tmp, e ); } } else { if ( s[ 0 ] < xmin ) return false; if ( e[ 0 ] > xmax ) return false; if ( e[ 0 ] < xmin ) { // clip s to xMin. Vec3.mult( invX, xmin - s[ 0 ], tmp ); Vec3.add( s, tmp, e ); } if ( s[ 0 ] > xmax ) { // clip e to xMax. Vec3.mult( invX, xmax - s[ 0 ], tmp ); Vec3.add( s, tmp, s ); } } // compate s and e against the yMin to yMax range of bb. if ( s[ 1 ] <= e[ 1 ] ) { // trivial reject of segment wholely outside. if ( e[ 1 ] < ymin ) return false; if ( s[ 1 ] > ymax ) return false; if ( s[ 1 ] < ymin ) { // clip s to yMin. Vec3.mult( invY, ymin - s[ 1 ], tmp ); Vec3.add( s, tmp, s ); } if ( e[ 1 ] > ymax ) { // clip e to yMax. Vec3.mult( invY, ymax - s[ 1 ], tmp ); Vec3.add( s, tmp, e ); } } else { if ( s[ 1 ] < ymin ) return false; if ( e[ 1 ] > ymax ) return false; if ( e[ 1 ] < ymin ) { // clip s to yMin. Vec3.mult( invY, ymin - s[ 1 ], tmp ); Vec3.add( s, tmp, e ); } if ( s[ 1 ] > ymax ) { // clip e to yMax. Vec3.mult( invY, ymax - s[ 1 ], tmp ); Vec3.add( s, tmp, s ); } } // compate s and e against the zMin to zMax range of bb. if ( s[ 2 ] <= e[ 2 ] ) { // trivial reject of segment wholely outside. if ( e[ 2 ] < zmin ) return false; if ( s[ 2 ] > zmax ) return false; if ( s[ 2 ] < zmin ) { // clip s to zMin. Vec3.mult( invZ, zmin - s[ 2 ], tmp ); Vec3.add( s, tmp, s ); } if ( e[ 2 ] > zmax ) { // clip e to zMax. Vec3.mult( invZ, zmax - s[ 2 ], tmp ); Vec3.add( s, tmp, e ); } } else { if ( s[ 2 ] < zmin ) return false; if ( e[ 2 ] > zmax ) return false; if ( e[ 2 ] < zmin ) { // clip s to zMin. Vec3.mult( invZ, zmin - s[ 2 ], tmp ); Vec3.add( s, tmp, e ); } if ( s[ 2 ] > zmax ) { // clip e to zMax. Vec3.mult( invZ, zmax - s[ 2 ], tmp ); Vec3.add( s, tmp, s ); } } return true; };
return function ( v1, v2, v3 ) { this._index++; if ( ( this._dimensionMask & ( 1 << 2 ) ) === 0 ) return; if ( this._limitOneIntersection && this._intersections.length > 0 ) return; var selectorMask = 0x1; var insideMask = 0x0; this._candidates = []; this._candidatesMasks = []; var d1, d2, d3, d1IsNegative, d2IsNegative, d3IsNegative; for ( var i = 0, j = this._planes.length; i < j; ++i, selectorMask <<= 1 ) { d1 = this.distance( this._planes[ i ], v1 ); d2 = this.distance( this._planes[ i ], v2 ); d3 = this.distance( this._planes[ i ], v3 ); d1IsNegative = ( d1 < 0.0 ); d2IsNegative = ( d2 < 0.0 ); d3IsNegative = ( d3 < 0.0 ); if ( d1IsNegative && d2IsNegative && d3IsNegative ) return; // Triangle outside if ( !d1IsNegative && !d2IsNegative && !d3IsNegative ) { // completly inside this plane insideMask |= selectorMask; continue; } // edge v1-v2 intersects if ( d1 === 0.0 ) { Vec3.copy( v1, tmpHit ); this._candidates.push( Vec3.copy( tmpHit, Vec3.create() ) ); this._candidatesMasks.push( selectorMask ); } else if ( d2 === 0.0 ) { Vec3.copy( v2, tmpHit ); this._candidates.push( Vec3.copy( tmpHit, Vec3.create() ) ); this._candidatesMasks.push( selectorMask ); } else if ( d1IsNegative && !d2IsNegative ) { //v1-(v2-v1)*(d1/(-d1+d2))) ) Vec3.sub( v2, v1, tmpHit ); Vec3.mult( tmpHit, d1 / ( -d1 + d2 ), tmpHit ); Vec3.sub( v1, tmpHit, tmpHit ); this._candidates.push( Vec3.copy( tmpHit, Vec3.create() ) ); this._candidatesMasks.push( selectorMask ); } else if ( !d1IsNegative && d2IsNegative ) { //(v1+(v2-v1)*(d1/(d1-d2))) Vec3.sub( v2, v1, tmpHit ); Vec3.mult( tmpHit, d1 / ( d1 - d2 ), tmpHit ); Vec3.add( v1, tmpHit, tmpHit ); this._candidates.push( Vec3.copy( tmpHit, Vec3.create() ) ); this._candidatesMasks.push( selectorMask ); } // edge v1-v3 intersects if ( d3 === 0.0 ) { Vec3.copy( v3, tmpHit ); this._candidates.push( Vec3.copy( tmpHit, Vec3.create() ) ); this._candidatesMasks.push( selectorMask ); } else if ( d1IsNegative && !d3IsNegative ) { // v1-(v3-v1)*(d1/(-d1+d3)) Vec3.sub( v3, v1, tmpHit ); Vec3.mult( tmpHit, d1 / ( -d1 + d3 ), tmpHit ); Vec3.sub( v1, tmpHit, tmpHit ); this._candidates.push( Vec3.copy( tmpHit, Vec3.create() ) ); this._candidatesMasks.push( selectorMask ); } else if ( !d1IsNegative && d3IsNegative ) { // v1+(v3-v1)*(d1/(d1-d3)) Vec3.sub( v3, v1, tmpHit ); Vec3.mult( tmpHit, d1 / ( d1 - d3 ), tmpHit ); Vec3.add( v1, tmpHit, tmpHit ); this._candidates.push( Vec3.copy( tmpHit, Vec3.create() ) ); this._candidatesMasks.push( selectorMask ); } // edge v2-v3 intersects if ( d2IsNegative && !d3IsNegative ) { // v2-(v3-v2)*(d2/(-d2+d3)) Vec3.sub( v3, v2, tmpHit ); Vec3.mult( tmpHit, d2 / ( -d2 + d3 ), tmpHit ); Vec3.sub( v2, tmpHit, tmpHit ); this._candidates.push( Vec3.copy( tmpHit, Vec3.create() ) ); this._candidatesMasks.push( selectorMask ); } else if ( !d2IsNegative && d3IsNegative ) { //v2+(v3-v2)*(d2/(d2-d3)) Vec3.sub( v3, v2, tmpHit ); Vec3.mult( tmpHit, d2 / ( d2 - d3 ), tmpHit ); Vec3.add( v2, tmpHit, tmpHit ); this._candidates.push( Vec3.copy( tmpHit, Vec3.create() ) ); this._candidatesMasks.push( selectorMask ); } } if ( insideMask === this._planesMask ) { // triangle lies inside of all planes this._candidates.push( Vec3.copy( v1, Vec3.create() ) ); this._candidatesMasks.push( this._planesMask ); this._candidates.push( Vec3.copy( v2, Vec3.create() ) ); this._candidatesMasks.push( this._planesMask ); this._candidates.push( Vec3.copy( v3, Vec3.create() ) ); this._candidatesMasks.push( this._planesMask ); this._intersections.push( new PolytopeIntersection( this._index, this._candidates, this._candidatesMasks, this._referencePlane, this._nodePath.slice( 0 ) ) ); return; } var numCands = this.checkCandidatePoints( insideMask ); if ( numCands > 0 ) { this._intersections.push( new PolytopeIntersection( this._index, this._candidates, this._candidatesMasks, this._referencePlane, this._nodePath.slice( 0 ) ) ); return; } // handle case where the polytope goes through the triangle // without containing any point of it // Probably it can be moved to other function and do the relevant closures. var lines = this.getPolytopeLines(); this._candidates = []; // check all polytope lines against the triangle // use algorithm from "Real-time rendering" (second edition) pp.580 //var e1= Vec3.create(); //var e2= Vec3.create(); Vec3.sub( v2, v1, e1 ); Vec3.sub( v3, v1, e2 ); for ( i = 0; i < lines.length; ++i ) { //var point = Vec3.create(); //var p = Vec3.create(); Vec3.cross( lines[ i ]._dir, e2, p ); var a = Vec3.dot( e1, p ); if ( Math.abs( a ) < 1E-6 ) continue; var f = 1.0 / a; //var s = Vec3.create(); Vec3.sub( lines[ i ]._pos, v1, s ); var u = f * ( Vec3.dot( s, p ) ); if ( u < 0.0 || u > 1.0 ) continue; //var q = Vec3.create(); Vec3.cross( s, e1, q ); var v = f * ( Vec3.dot( lines[ i ]._dir, q ) ); if ( v < 0.0 || u + v > 1.0 ) continue; var t = f * ( Vec3.dot( e2, q ) ); Vec3.mult( lines[ i ]._dir, t, point ); Vec3.add( lines[ i ]._pos, point, point ); this._candidates.push( Vec3.copy( point, Vec3.create() ) ); this._candidatesMasks.push( lines[ i ]._planeMask ); } numCands = this.checkCandidatePoints( insideMask ); if ( numCands > 0 ) { this._intersections.push( new PolytopeIntersection( this._index, this._candidates, this._candidatesMasks, this._referencePlane, this._nodePath.slice( 0 ) ) ); return; } };
return function ( v1, v2 ) { this._index++; if ( ( this._dimensionMask & ( 1 << 1 ) ) === 0 ) return; if ( this._limitOneIntersection && this._intersections.length > 0 ) return; var v1Inside = true; var v2Inside = true; var selectorMask = 0x1; var insideMask = 0x0; this._candidates = []; this._candidatesMasks = []; var d1, d2, d1IsNegative, d2IsNegative; for ( var i = 0, j = this._planes.length; i < j; ++i, selectorMask <<= 1 ) { d1 = this.distance( this._planes[ i ], v1 ); d2 = this.distance( this._planes[ i ], v2 ); d1IsNegative = ( d1 < 0.0 ); d2IsNegative = ( d2 < 0.0 ); if ( d1IsNegative && d2IsNegative ) return; // line outside if ( !d1IsNegative && !d2IsNegative ) { // completly inside this plane insideMask |= selectorMask; continue; } if ( d1IsNegative ) v1Inside = false; if ( d2IsNegative ) v2Inside = false; if ( d1 === 0.0 ) { Vec3.copy( v1, hit ); this._candidates.push( hit ); this._candidatesMasks.push( selectorMask ); } else if ( d2 === 0.0 ) { Vec3.copy( v2, hit ); this._candidates.push( hit ); this._candidatesMasks.push( selectorMask ); } else if ( d1IsNegative && !d2IsNegative ) { //v1-(v2-v1)*(d1/(-d1+d2))) ) Vec3.sub( v2, v1, hit ); Vec3.mult( hit, d1 / ( -d1 + d2 ), hit ); Vec3.sub( v1, hit, hit ); this._candidates.push( hit ); this._candidatesMasks.push( selectorMask ); } else if ( !d1IsNegative && d2IsNegative ) { //(v1+(v2-v1)*(d1/(d1-d2))) Vec3.sub( v2, v1, hit ); Vec3.mult( hit, d1 / ( d1 - d2 ), hit ); Vec3.add( v1, hit, hit ); this._candidates.push( hit ); this._candidatesMasks.push( selectorMask ); } } if ( insideMask === this._planesMask ) { this._candidates.push( Vec3.copy( v1, Vec3.create() ) ); this._candidatesMasks.push( this._planesMask ); this._candidates.push( Vec3.copy( v2, Vec3.create() ) ); this._candidatesMasks.push( this._planesMask ); this._intersections.push( new PolytopeIntersection( this._index, this._candidates, this._candidatesMasks, this._referencePlane, this._nodePath.slice( 0 ) ) ); return; } var numCands = this.checkCandidatePoints( insideMask ); if ( numCands > 0 ) { if ( v1Inside ) { this._candidatesMasks.push( this._planesMask ); this._candidates.push( Vec3.copy( v1, Vec3.create() ) ); } if ( v2Inside ) { this._candidatesMasks.push( this._planesMask ); this._candidates.push( Vec3.copy( v2, Vec3.create() ) ); } this._intersections.push( new PolytopeIntersection( this._index, this._candidates, this._candidatesMasks, this._referencePlane, this._nodePath.slice( 0 ) ) ); } };
return function ( dz ) { var zoomSpeed = dz * this._zoomFactor; Vec3.sub( this._pivotPoint, this._eye, vectorDistance ); Vec3.add( this._eye, Vec3.mult( vectorDistance, zoomSpeed, speedDist ), this._eye ); };