diff --git a/src/core/Vector4.js b/src/core/Vector4.js index d3e46c04..2020580c 100644 --- a/src/core/Vector4.js +++ b/src/core/Vector4.js @@ -30,7 +30,7 @@ THREE.Vector4.prototype = { this.x = v.x; this.y = v.y; this.z = v.z; - this.w = v.w; + this.w = v.w || 1.0; return this; @@ -101,6 +101,14 @@ THREE.Vector4.prototype = { return this; }, + + lerpSelf: function ( v, alpha ) { + + this.x = this.x + (v.x - this.x) * alpha; + this.y = this.y + (v.y - this.y) * alpha; + this.z = this.z + (v.z - this.z) * alpha; + this.w = this.w + (v.w - this.w) * alpha; + } clone: function () { diff --git a/src/core/Vertex.js b/src/core/Vertex.js index 81ba946a..0fab06ee 100644 --- a/src/core/Vertex.js +++ b/src/core/Vertex.js @@ -6,7 +6,7 @@ THREE.Vertex = function ( position, normal ) { this.position = position || new THREE.Vector3(); this.positionWorld = new THREE.Vector3(); - this.positionScreen = new THREE.Vector3(); + this.positionScreen = new THREE.Vector4(); this.normal = normal || new THREE.Vector3(); this.normalWorld = new THREE.Vector3(); diff --git a/src/renderers/Projector.js b/src/renderers/Projector.js index d73397e0..327fb998 100644 --- a/src/renderers/Projector.js +++ b/src/renderers/Projector.js @@ -14,15 +14,74 @@ THREE.Projector = function() { _vector4 = new THREE.Vector4(), _projScreenMatrix = new THREE.Matrix4(), _projScreenObjectMatrix = new THREE.Matrix4(); + + function clipLineSegmentAgainstNearAndFarPlanes ( s0, s1 ) { + + var visible, + alpha0 = 0, alpha1 = 1, + + // Calculate the boundary coordinate of each vertex for the near and far clip planes, + // Z = -1 and Z = +1, respectively. + bc0near = s0.z + s0.w, + bc1near = s1.z + s1.w, + bc0far = -s0.z + s0.w, + bc1far = -s1.z + s1.w; + + if (bc0near >= 0 && bc1near >= 0 && bc0far >= 0 && bc1far >= 0) { + // Both vertices lie entirely within all clip planes. + visible = true; + } else if ((bc0near < 0 && bc1near < 0) || (bc0far < 0 && bc1far < 0)) { + // Both vertices lie entirely outside one of the clip planes. + visible = false; + } else { + + // The line segment spans at least one clip plane. + + if (bc0near < 0) { + // vertex0 lies outside the near plane, vertex1 inside + alpha0 = Math.max(alpha0, bc0near / (bc0near - bc1near)); + } else if (bc1near < 0) { + // vertex1 lies outside the near plane, vertex0 inside + alpha1 = Math.min(alpha1, bc0near / (bc0near - bc1near)); + } + + if (bc0far < 0) { + // vertex0 lies outside the far plane, vertex1 inside + alpha0 = Math.max(alpha0, bc0far / (bc0far - bc1far)); + } else if (bc1far < 0) { + // vertex1 lies outside the far plane, vertex1 inside + alpha1 = Math.min(alpha1, bc0far / (bc0far - bc1far)); + } + + if (alpha1 < alpha0) { + // 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.) + visible = false; + } else { + + // Update the s0 and s1 vertices to match the clipped line segment. + s0.lerpSelf(s1, alpha0); + s1.lerpSelf(s0, 1 - alpha1); + + visible = true; + } + } + + return visible; + } this.projectScene = function ( scene, camera ) { var o, ol, v, vl, f, fl, objects, object, objectMatrix, - vertices, vertex, vertexPositionScreen, vertex2, + vertices, vertex, vertex0, vertex1, vertexPositionScreen, faces, face, v1, v2, v3, v4; _renderList = []; - _face3Count = 0, _face4Count = 0, _lineCount = 0, _particleCount = 0; + _face3Count = 0; + _face4Count = 0; + _lineCount = 0; + _particleCount = 0; if( camera.autoUpdateMatrix ) { @@ -60,6 +119,11 @@ THREE.Projector = function() { vertexPositionScreen = vertex.positionScreen; vertexPositionScreen.copy( vertex.position ); _projScreenObjectMatrix.transform( vertexPositionScreen ); + + // Perform the perspective divide. TODO: This should be be performend + // post clipping (imagine if the vertex lies at the same location as + // the camera, causing a divide by w = 0). + vertexPositionScreen.multiplyScalar( 1.0 / vertexPositionScreen.w ); vertex.__visible = vertexPositionScreen.z > 0 && vertexPositionScreen.z < 1; @@ -170,32 +234,33 @@ THREE.Projector = function() { vertex = vertices[ v ]; - vertexPositionScreen = vertex.positionScreen; - vertexPositionScreen.copy( vertex.position ); - _projScreenObjectMatrix.transform( vertexPositionScreen ); + vertex.positionScreen.copy( vertex.position ); + _projScreenObjectMatrix.transform( vertex.positionScreen ); + } - vertex.__visible = vertexPositionScreen.z > 0 && vertexPositionScreen.z < 1; + for ( v = 1, vl = vertices.length; v < vl; v++ ) { - if ( v > 0 ) { + vertex0 = vertices[ v ]; + vertex1 = vertices[ v - 1 ]; + + if (clipLineSegmentAgainstNearAndFarPlanes(vertex0.positionScreen, vertex1.positionScreen)) { + + // Perform the perspective divide + vertex0.positionScreen.multiplyScalar( 1.0 / vertex0.positionScreen.w ); + vertex1.positionScreen.multiplyScalar( 1.0 / vertex1.positionScreen.w ); - vertex2 = object.geometry.vertices[ v - 1 ]; + _line = _linePool[ _lineCount ] = _linePool[ _lineCount ] || new THREE.RenderableLine(); + _line.v1.copy( vertex0.positionScreen ); + _line.v2.copy( vertex1.positionScreen ); - if ( vertex.__visible && vertex2.__visible ) { + // TODO: Use centriums here too. + _line.z = Math.max( vertex0.positionScreen.z, vertex1.positionScreen.z ); - _line = _linePool[ _lineCount ] = _linePool[ _lineCount ] || new THREE.RenderableLine(); - _line.v1.copy( vertex.positionScreen ); - _line.v2.copy( vertex2.positionScreen ); + _line.material = object.material; - // TODO: Use centriums here too. - _line.z = Math.max( vertex.positionScreen.z, vertex2.positionScreen.z ); + _renderList.push( _line ); - _line.material = object.material; - - _renderList.push( _line ); - - _lineCount ++; - - } + _lineCount ++; } } @@ -248,5 +313,4 @@ THREE.Projector = function() { return vector; }; - };