/** * @author mrdoob / http://mrdoob.com/ * @author supereggbert / http://www.paulbrunt.co.uk/ * @author julianwa / https://github.com/julianwa */ three.renderableobject = function () { this.id = 0; this.object = null; this.z = 0; }; // three.renderableface = function () { this.id = 0; this.v1 = new three.renderablevertex(); this.v2 = new three.renderablevertex(); this.v3 = new three.renderablevertex(); this.normalmodel = new three.vector3(); this.vertexnormalsmodel = [ new three.vector3(), new three.vector3(), new three.vector3() ]; this.vertexnormalslength = 0; this.color = new three.color(); this.material = null; this.uvs = [ new three.vector2(), new three.vector2(), new three.vector2() ]; this.z = 0; }; // three.renderablevertex = function () { this.position = new three.vector3(); this.positionworld = new three.vector3(); this.positionscreen = new three.vector4(); this.visible = true; }; three.renderablevertex.prototype.copy = function ( vertex ) { this.positionworld.copy( vertex.positionworld ); this.positionscreen.copy( vertex.positionscreen ); }; // three.renderableline = function () { this.id = 0; this.v1 = new three.renderablevertex(); this.v2 = new three.renderablevertex(); this.vertexcolors = [ new three.color(), new three.color() ]; this.material = null; this.z = 0; }; // three.renderablesprite = function () { this.id = 0; this.object = null; this.x = 0; this.y = 0; this.z = 0; this.rotation = 0; this.scale = new three.vector2(); this.material = null; }; // three.projector = function () { var _object, _objectcount, _objectpool = [], _objectpoollength = 0, _vertex, _vertexcount, _vertexpool = [], _vertexpoollength = 0, _face, _facecount, _facepool = [], _facepoollength = 0, _line, _linecount, _linepool = [], _linepoollength = 0, _sprite, _spritecount, _spritepool = [], _spritepoollength = 0, _renderdata = { objects: [], lights: [], elements: [] }, _vector3 = new three.vector3(), _vector4 = new three.vector4(), _clipbox = new three.box3( new three.vector3( - 1, - 1, - 1 ), new three.vector3( 1, 1, 1 ) ), _boundingbox = new three.box3(), _points3 = new array( 3 ), _points4 = new array( 4 ), _viewmatrix = new three.matrix4(), _viewprojectionmatrix = new three.matrix4(), _modelmatrix, _modelviewprojectionmatrix = new three.matrix4(), _normalmatrix = new three.matrix3(), _frustum = new three.frustum(), _clippedvertex1positionscreen = new three.vector4(), _clippedvertex2positionscreen = new three.vector4(); // this.projectvector = function ( vector, camera ) { console.warn( 'three.projector: .projectvector() is now vector.project().' ); vector.project( camera ); }; this.unprojectvector = function ( vector, camera ) { console.warn( 'three.projector: .unprojectvector() is now vector.unproject().' ); vector.unproject( camera ); }; this.pickingray = function ( vector, camera ) { console.error( 'three.projector: .pickingray() is now raycaster.setfromcamera().' ); }; // var renderlist = function () { var normals = []; var uvs = []; var object = null; var material = null; var normalmatrix = new three.matrix3(); var setobject = function ( value ) { object = value; material = object.material; normalmatrix.getnormalmatrix( object.matrixworld ); normals.length = 0; uvs.length = 0; }; var projectvertex = function ( vertex ) { var position = vertex.position; var positionworld = vertex.positionworld; var positionscreen = vertex.positionscreen; positionworld.copy( position ).applymatrix4( _modelmatrix ); positionscreen.copy( positionworld ).applymatrix4( _viewprojectionmatrix ); var invw = 1 / positionscreen.w; positionscreen.x *= invw; positionscreen.y *= invw; positionscreen.z *= invw; vertex.visible = positionscreen.x >= - 1 && positionscreen.x <= 1 && positionscreen.y >= - 1 && positionscreen.y <= 1 && positionscreen.z >= - 1 && positionscreen.z <= 1; }; var pushvertex = function ( x, y, z ) { _vertex = getnextvertexinpool(); _vertex.position.set( x, y, z ); projectvertex( _vertex ); }; var pushnormal = function ( x, y, z ) { normals.push( x, y, z ); }; var pushuv = function ( x, y ) { uvs.push( x, y ); }; var checktrianglevisibility = function ( v1, v2, v3 ) { if ( v1.visible === true || v2.visible === true || v3.visible === true ) return true; _points3[ 0 ] = v1.positionscreen; _points3[ 1 ] = v2.positionscreen; _points3[ 2 ] = v3.positionscreen; return _clipbox.isintersectionbox( _boundingbox.setfrompoints( _points3 ) ); }; var checkbackfaceculling = function ( v1, v2, v3 ) { return ( ( v3.positionscreen.x - v1.positionscreen.x ) * ( v2.positionscreen.y - v1.positionscreen.y ) - ( v3.positionscreen.y - v1.positionscreen.y ) * ( v2.positionscreen.x - v1.positionscreen.x ) ) < 0; }; var pushline = function ( a, b ) { var v1 = _vertexpool[ a ]; var v2 = _vertexpool[ b ]; _line = getnextlineinpool(); _line.id = object.id; _line.v1.copy( v1 ); _line.v2.copy( v2 ); _line.z = ( v1.positionscreen.z + v2.positionscreen.z ) / 2; _line.material = object.material; _renderdata.elements.push( _line ); }; var pushtriangle = function ( a, b, c ) { var v1 = _vertexpool[ a ]; var v2 = _vertexpool[ b ]; var v3 = _vertexpool[ c ]; if ( checktrianglevisibility( v1, v2, v3 ) === false ) return; if ( material.side === three.doubleside || checkbackfaceculling( v1, v2, v3 ) === true ) { _face = getnextfaceinpool(); _face.id = object.id; _face.v1.copy( v1 ); _face.v2.copy( v2 ); _face.v3.copy( v3 ); _face.z = ( v1.positionscreen.z + v2.positionscreen.z + v3.positionscreen.z ) / 3; for ( var i = 0; i < 3; i ++ ) { var offset = arguments[ i ] * 3; var normal = _face.vertexnormalsmodel[ i ]; normal.set( normals[ offset ], normals[ offset + 1 ], normals[ offset + 2 ] ); normal.applymatrix3( normalmatrix ).normalize(); var offset2 = arguments[ i ] * 2; var uv = _face.uvs[ i ]; uv.set( uvs[ offset2 ], uvs[ offset2 + 1 ] ); } _face.vertexnormalslength = 3; _face.material = object.material; _renderdata.elements.push( _face ); } }; return { setobject: setobject, projectvertex: projectvertex, checktrianglevisibility: checktrianglevisibility, checkbackfaceculling: checkbackfaceculling, pushvertex: pushvertex, pushnormal: pushnormal, pushuv: pushuv, pushline: pushline, pushtriangle: pushtriangle } }; var renderlist = new renderlist(); this.projectscene = function ( scene, camera, sortobjects, sortelements ) { _facecount = 0; _linecount = 0; _spritecount = 0; _renderdata.elements.length = 0; if ( scene.autoupdate === true ) scene.updatematrixworld(); if ( camera.parent === undefined ) camera.updatematrixworld(); _viewmatrix.copy( camera.matrixworldinverse.getinverse( camera.matrixworld ) ); _viewprojectionmatrix.multiplymatrices( camera.projectionmatrix, _viewmatrix ); _frustum.setfrommatrix( _viewprojectionmatrix ); // _objectcount = 0; _renderdata.objects.length = 0; _renderdata.lights.length = 0; scene.traversevisible( function ( object ) { if ( object instanceof three.light ) { _renderdata.lights.push( object ); } else if ( object instanceof three.mesh || object instanceof three.line || object instanceof three.sprite ) { if ( object.material.visible === false ) return; if ( object.frustumculled === false || _frustum.intersectsobject( object ) === true ) { _object = getnextobjectinpool(); _object.id = object.id; _object.object = object; _vector3.setfrommatrixposition( object.matrixworld ); _vector3.applyprojection( _viewprojectionmatrix ); _object.z = _vector3.z; _renderdata.objects.push( _object ); } } } ); if ( sortobjects === true ) { _renderdata.objects.sort( paintersort ); } // for ( var o = 0, ol = _renderdata.objects.length; o < ol; o ++ ) { var object = _renderdata.objects[ o ].object; var geometry = object.geometry; renderlist.setobject( object ); _modelmatrix = object.matrixworld; _vertexcount = 0; if ( object instanceof three.mesh ) { if ( geometry instanceof three.buffergeometry ) { var attributes = geometry.attributes; var offsets = geometry.offsets; if ( attributes.position === undefined ) continue; var positions = attributes.position.array; for ( var i = 0, l = positions.length; i < l; i += 3 ) { renderlist.pushvertex( positions[ i ], positions[ i + 1 ], positions[ i + 2 ] ); } if ( attributes.normal !== undefined ) { var normals = attributes.normal.array; for ( var i = 0, l = normals.length; i < l; i += 3 ) { renderlist.pushnormal( normals[ i ], normals[ i + 1 ], normals[ i + 2 ] ); } } if ( attributes.uv !== undefined ) { var uvs = attributes.uv.array; for ( var i = 0, l = uvs.length; i < l; i += 2 ) { renderlist.pushuv( uvs[ i ], uvs[ i + 1 ] ); } } if ( attributes.index !== undefined ) { var indices = attributes.index.array; if ( offsets.length > 0 ) { for ( var o = 0; o < offsets.length; o ++ ) { var offset = offsets[ o ]; var index = offset.index; for ( var i = offset.start, l = offset.start + offset.count; i < l; i += 3 ) { renderlist.pushtriangle( indices[ i ] + index, indices[ i + 1 ] + index, indices[ i + 2 ] + index ); } } } else { for ( var i = 0, l = indices.length; i < l; i += 3 ) { renderlist.pushtriangle( indices[ i ], indices[ i + 1 ], indices[ i + 2 ] ); } } } else { for ( var i = 0, l = positions.length / 3; i < l; i += 3 ) { renderlist.pushtriangle( i, i + 1, i + 2 ); } } } else if ( geometry instanceof three.geometry ) { var vertices = geometry.vertices; var faces = geometry.faces; var facevertexuvs = geometry.facevertexuvs[ 0 ]; _normalmatrix.getnormalmatrix( _modelmatrix ); var material = object.material; var isfacematerial = material instanceof three.meshfacematerial; var objectmaterials = isfacematerial === true ? object.material : null; for ( var v = 0, vl = vertices.length; v < vl; v ++ ) { var vertex = vertices[ v ]; _vector3.copy( vertex ); if ( material.morphtargets === true ) { var morphtargets = geometry.morphtargets; var morphinfluences = object.morphtargetinfluences; for ( var t = 0, tl = morphtargets.length; t < tl; t ++ ) { var influence = morphinfluences[ t ]; if ( influence === 0 ) continue; var target = morphtargets[ t ]; var targetvertex = target.vertices[ v ]; _vector3.x += ( targetvertex.x - vertex.x ) * influence; _vector3.y += ( targetvertex.y - vertex.y ) * influence; _vector3.z += ( targetvertex.z - vertex.z ) * influence; } } renderlist.pushvertex( _vector3.x, _vector3.y, _vector3.z ); } for ( var f = 0, fl = faces.length; f < fl; f ++ ) { var face = faces[ f ]; var material = isfacematerial === true ? objectmaterials.materials[ face.materialindex ] : object.material; if ( material === undefined ) continue; var side = material.side; var v1 = _vertexpool[ face.a ]; var v2 = _vertexpool[ face.b ]; var v3 = _vertexpool[ face.c ]; if ( renderlist.checktrianglevisibility( v1, v2, v3 ) === false ) continue; var visible = renderlist.checkbackfaceculling( v1, v2, v3 ); if ( side !== three.doubleside ) { if ( side === three.frontside && visible === false ) continue; if ( side === three.backside && visible === true ) continue; } _face = getnextfaceinpool(); _face.id = object.id; _face.v1.copy( v1 ); _face.v2.copy( v2 ); _face.v3.copy( v3 ); _face.normalmodel.copy( face.normal ); if ( visible === false && ( side === three.backside || side === three.doubleside ) ) { _face.normalmodel.negate(); } _face.normalmodel.applymatrix3( _normalmatrix ).normalize(); var facevertexnormals = face.vertexnormals; for ( var n = 0, nl = math.min( facevertexnormals.length, 3 ); n < nl; n ++ ) { var normalmodel = _face.vertexnormalsmodel[ n ]; normalmodel.copy( facevertexnormals[ n ] ); if ( visible === false && ( side === three.backside || side === three.doubleside ) ) { normalmodel.negate(); } normalmodel.applymatrix3( _normalmatrix ).normalize(); } _face.vertexnormalslength = facevertexnormals.length; var vertexuvs = facevertexuvs[ f ]; if ( vertexuvs !== undefined ) { for ( var u = 0; u < 3; u ++ ) { _face.uvs[ u ].copy( vertexuvs[ u ] ); } } _face.color = face.color; _face.material = material; _face.z = ( v1.positionscreen.z + v2.positionscreen.z + v3.positionscreen.z ) / 3; _renderdata.elements.push( _face ); } } } else if ( object instanceof three.line ) { if ( geometry instanceof three.buffergeometry ) { var attributes = geometry.attributes; if ( attributes.position !== undefined ) { var positions = attributes.position.array; for ( var i = 0, l = positions.length; i < l; i += 3 ) { renderlist.pushvertex( positions[ i ], positions[ i + 1 ], positions[ i + 2 ] ); } if ( attributes.index !== undefined ) { var indices = attributes.index.array; for ( var i = 0, l = indices.length; i < l; i += 2 ) { renderlist.pushline( indices[ i ], indices[ i + 1 ] ); } } else { var step = object.mode === three.linepieces ? 2 : 1; for ( var i = 0, l = ( positions.length / 3 ) - 1; i < l; i += step ) { renderlist.pushline( i, i + 1 ); } } } } else if ( geometry instanceof three.geometry ) { _modelviewprojectionmatrix.multiplymatrices( _viewprojectionmatrix, _modelmatrix ); var vertices = object.geometry.vertices; if ( vertices.length === 0 ) continue; v1 = getnextvertexinpool(); v1.positionscreen.copy( vertices[ 0 ] ).applymatrix4( _modelviewprojectionmatrix ); // handle linestrip and linepieces var step = object.mode === three.linepieces ? 2 : 1; for ( var v = 1, vl = vertices.length; v < vl; v ++ ) { v1 = getnextvertexinpool(); v1.positionscreen.copy( vertices[ v ] ).applymatrix4( _modelviewprojectionmatrix ); if ( ( v + 1 ) % step > 0 ) continue; v2 = _vertexpool[ _vertexcount - 2 ]; _clippedvertex1positionscreen.copy( v1.positionscreen ); _clippedvertex2positionscreen.copy( v2.positionscreen ); if ( clipline( _clippedvertex1positionscreen, _clippedvertex2positionscreen ) === true ) { // perform the perspective divide _clippedvertex1positionscreen.multiplyscalar( 1 / _clippedvertex1positionscreen.w ); _clippedvertex2positionscreen.multiplyscalar( 1 / _clippedvertex2positionscreen.w ); _line = getnextlineinpool(); _line.id = object.id; _line.v1.positionscreen.copy( _clippedvertex1positionscreen ); _line.v2.positionscreen.copy( _clippedvertex2positionscreen ); _line.z = math.max( _clippedvertex1positionscreen.z, _clippedvertex2positionscreen.z ); _line.material = object.material; if ( object.material.vertexcolors === three.vertexcolors ) { _line.vertexcolors[ 0 ].copy( object.geometry.colors[ v ] ); _line.vertexcolors[ 1 ].copy( object.geometry.colors[ v - 1 ] ); } _renderdata.elements.push( _line ); } } } } else if ( object instanceof three.sprite ) { _vector4.set( _modelmatrix.elements[ 12 ], _modelmatrix.elements[ 13 ], _modelmatrix.elements[ 14 ], 1 ); _vector4.applymatrix4( _viewprojectionmatrix ); var invw = 1 / _vector4.w; _vector4.z *= invw; if ( _vector4.z >= - 1 && _vector4.z <= 1 ) { _sprite = getnextspriteinpool(); _sprite.id = object.id; _sprite.x = _vector4.x * invw; _sprite.y = _vector4.y * invw; _sprite.z = _vector4.z; _sprite.object = object; _sprite.rotation = object.rotation; _sprite.scale.x = object.scale.x * math.abs( _sprite.x - ( _vector4.x + camera.projectionmatrix.elements[ 0 ] ) / ( _vector4.w + camera.projectionmatrix.elements[ 12 ] ) ); _sprite.scale.y = object.scale.y * math.abs( _sprite.y - ( _vector4.y + camera.projectionmatrix.elements[ 5 ] ) / ( _vector4.w + camera.projectionmatrix.elements[ 13 ] ) ); _sprite.material = object.material; _renderdata.elements.push( _sprite ); } } } if ( sortelements === true ) { _renderdata.elements.sort( paintersort ); } return _renderdata; }; // pools function getnextobjectinpool() { if ( _objectcount === _objectpoollength ) { var object = new three.renderableobject(); _objectpool.push( object ); _objectpoollength ++; _objectcount ++; return object; } return _objectpool[ _objectcount ++ ]; } function getnextvertexinpool() { if ( _vertexcount === _vertexpoollength ) { var vertex = new three.renderablevertex(); _vertexpool.push( vertex ); _vertexpoollength ++; _vertexcount ++; return vertex; } return _vertexpool[ _vertexcount ++ ]; } function getnextfaceinpool() { if ( _facecount === _facepoollength ) { var face = new three.renderableface(); _facepool.push( face ); _facepoollength ++; _facecount ++; return face; } return _facepool[ _facecount ++ ]; } function getnextlineinpool() { if ( _linecount === _linepoollength ) { var line = new three.renderableline(); _linepool.push( line ); _linepoollength ++; _linecount ++ return line; } return _linepool[ _linecount ++ ]; } function getnextspriteinpool() { if ( _spritecount === _spritepoollength ) { var sprite = new three.renderablesprite(); _spritepool.push( sprite ); _spritepoollength ++; _spritecount ++ return sprite; } return _spritepool[ _spritecount ++ ]; } // function paintersort( a, b ) { if ( a.z !== b.z ) { return b.z - a.z; } else if ( a.id !== b.id ) { return a.id - b.id; } else { return 0; } } function clipline( s1, s2 ) { var alpha1 = 0, alpha2 = 1, // calculate the boundary coordinate of each vertex for the near and far clip planes, // z = -1 and z = +1, respectively. bc1near = s1.z + s1.w, bc2near = s2.z + s2.w, bc1far = - s1.z + s1.w, bc2far = - s2.z + s2.w; if ( bc1near >= 0 && bc2near >= 0 && bc1far >= 0 && bc2far >= 0 ) { // both vertices lie entirely within all clip planes. return true; } else if ( ( bc1near < 0 && bc2near < 0 ) || ( bc1far < 0 && bc2far < 0 ) ) { // both vertices lie entirely outside one of the clip planes. return false; } else { // the line segment spans at least one clip plane. if ( bc1near < 0 ) { // v1 lies outside the near plane, v2 inside alpha1 = math.max( alpha1, bc1near / ( bc1near - bc2near ) ); } else if ( bc2near < 0 ) { // v2 lies outside the near plane, v1 inside alpha2 = math.min( alpha2, bc1near / ( bc1near - bc2near ) ); } if ( bc1far < 0 ) { // v1 lies outside the far plane, v2 inside alpha1 = math.max( alpha1, bc1far / ( bc1far - bc2far ) ); } else if ( bc2far < 0 ) { // v2 lies outside the far plane, v2 inside alpha2 = math.min( alpha2, bc1far / ( bc1far - bc2far ) ); } if ( alpha2 < alpha1 ) { // the line segment spans two boundaries, but is outside both of them. // (this can't happen when we're only clipping against just near/far but good // to leave the check here for future usage if other clip planes are added.) return false; } else { // update the s1 and s2 vertices to match the clipped line segment. s1.lerp( s2, alpha1 ); s2.lerp( s1, 1 - alpha2 ); return true; } } } };