Projector class
@author mr.doob / http://mrdoob.com/ @author supereggbert / http://www.paulbrunt.co.uk/ @author julianwa / https://github.com/julianwa
Ported to Dart from JS by: @author rob silverton / http://www.unwrong.com/ @author nelson silva / http://www.inevo.pt
updated to 81ef5c3b32 - Made Projector.projectObject more open for custom rendererers.
class Projector {
List<RenderableObject>_objectPool;
List<RenderableVertex> _vertexPool;
List<RenderableFace4> _face4Pool;
List<RenderableFace3> _face3Pool;
List<RenderableLine> _linePool;
List<RenderableParticle> _particlePool;
int _objectCount, _vertexCount, _face3Count, _face4Count, _lineCount, _particleCount;
RenderableObject _object;
RenderableVertex _vertex;
RenderableLine _line;
RenderableParticle _particle;
Vector3 _vector3;
Vector4 _vector4;
Vector4 _clippedVertex1PositionScreen;
Vector4 _clippedVertex2PositionScreen;
ProjectorRenderData _renderData;
Matrix4 _viewProjectionMatrix, _modelViewProjectionMatrix;
Frustum _frustum;
Projector()
: _objectPool = [],
_vertexPool = [],
_face3Pool = [],
_face4Pool = [],
_linePool = [],
_particlePool = [],
//_renderData = { "objects": [], "sprites": [], "lights": [], "elements": [] };
_renderData = new ProjectorRenderData(),
_vector3 = new Vector3.zero(),
_vector4 = new Vector4(0.0, 0.0, 0.0, 1.0),
_viewProjectionMatrix = new Matrix4.identity(),
_modelViewProjectionMatrix = new Matrix4.identity(),
_frustum = new Frustum(),
_clippedVertex1PositionScreen = new Vector4(0.0, 0.0, 0.0, 1.0),
_clippedVertex2PositionScreen = new Vector4(0.0, 0.0, 0.0, 1.0);
Vector3 projectVector( Vector3 vector, Camera camera ) {
camera.matrixWorldInverse.copyInverse(camera.matrixWorld);
_viewProjectionMatrix = camera.projectionMatrix * camera.matrixWorldInverse;
return vector.applyProjection(_viewProjectionMatrix);
}
Vector3 unprojectVector( Vector3 vector, Camera camera ) {
camera.projectionMatrixInverse.copyInverse(camera.projectionMatrix);
_viewProjectionMatrix = camera.matrixWorld * camera.projectionMatrixInverse;
return vector.applyProjection(_viewProjectionMatrix);
}
/**
* Translates a 2D point from NDC to a THREE.Ray
* that can be used for picking.
* @vector - THREE.Vector3 that represents 2D point
* @camera - THREE.Camera
*/
Ray pickingRay( Vector3 vector, Camera camera ) {
Vector3 end, ray, t;
// set two vectors with opposing z values
vector.z = -1.0;
end = new Vector3( vector.x, vector.y, 1.0 );
unprojectVector( vector, camera );
unprojectVector( end, camera );
// find direction from vector to end
end.sub( vector ).normalize();
return new Ray( vector, end );
}
_projectObject( Object3D parent ) {
var cl = parent.children.length;
for ( var c = 0; c < cl; c ++ ) {
var object = parent.children[ c ];
if ( !object.visible ) continue;
if ( object is Light ) {
_renderData.lights.add( object );
} else if ( object is Mesh || object is Line ) {
if ( !object.frustumCulled || _frustum.contains( object ) ) {
_object = getNextObjectInPool();
_object.object = object;
if ( object.renderDepth != null ) {
_object.z = object.renderDepth;
} else {
_vector3 = object.matrixWorld.getTranslation();
_vector3.applyProjection(_viewProjectionMatrix);
_object.z = _vector3.z;
}
_renderData.objects.add( _object );
}
} else if ( object is Sprite || object is Particle ) {
_object = getNextObjectInPool();
_object.object = object;
// TODO: Find an elegant and performant solution and remove this dupe code.
if ( object.renderDepth != null ) {
_object.z = object.renderDepth;
} else {
_vector3 = object.matrixWorld.getTranslation();
_vector3.applyProjection(_viewProjectionMatrix);
_object.z = _vector3.z;
}
_renderData.sprites.add( _object );
} else {
_object = getNextObjectInPool();
_object.object = object;
if ( object.renderDepth != null ) {
_object.z = object.renderDepth;
} else {
_vector3 = object.matrixWorld.getTranslation();
_vector3.applyProjection(_viewProjectionMatrix);
_object.z = _vector3.z;
}
_renderData.objects.add( _object );
}
_projectObject( object );
}
}
ProjectorRenderData projectGraph( Object3D root, bool sort ) {
_objectCount = 0;
_renderData.objects = [];
_renderData.sprites = [];
_renderData.lights = [];
_projectObject( root );
//TODO: assuming this is a form of 'if' statement.
//sort && _renderData.objects.sort( painterSort );
if (sort) {
_renderData.objects.sort( painterSort );
}
return _renderData;
}
ProjectorRenderData projectScene( Scene scene, Camera camera, bool sort ) {
num near = camera.near, far = camera.far;
bool visible = false;
int o, ol, v, vl, f, fl, n, nl, c, cl, u, ul;
Object3D object;
Matrix4 modelMatrix, rotationMatrix;
Geometry geometry;
List geometryMaterials;
List<Vector3> vertices;
Vector3 vertex;
Vector3 vertexPositionScreen, normal;
List<Face> faces;
Face face;
RenderableFace _face;
List faceVertexNormals;
List<List> faceVertexUvs;
RenderableVertex v1, v2, v3, v4;
bool isFaceMaterial;
Material material;
int side;
_face3Count = 0;
_face4Count = 0;
_lineCount = 0;
_particleCount = 0;
_renderData.elements = [];
scene.updateMatrixWorld();
if ( camera.parent == null ) {
// console.warn( 'DEPRECATED: Camera hasn\'t been added to a Scene. Adding it...' );
// scene.add( camera );
camera.updateMatrixWorld();
}
camera.matrixWorldInverse.copyInverse(camera.matrixWorld);
_viewProjectionMatrix = camera.projectionMatrix * camera.matrixWorldInverse;
_frustum.setFromMatrix( _viewProjectionMatrix );
_renderData = projectGraph( scene, false );
_renderData.objects.forEach((o) {
object = o.object;
modelMatrix = object.matrixWorld;
_vertexCount = 0;
if ( object is Mesh ) {
geometry = object.geometry;
geometryMaterials = object.geometry.materials;
vertices = geometry.vertices;
faces = geometry.faces;
faceVertexUvs = geometry.faceVertexUvs;
extractRotation( object.matrixRotationWorld, modelMatrix );
rotationMatrix = object.matrixRotationWorld;
isFaceMaterial = (object.material is MeshFaceMaterial);
side = object.material.side;
vertices.forEach((Vector3 v) {
_vertex = getNextVertexInPool();
_vertex.positionWorld.setFrom(v);
_vertex.positionWorld.applyProjection(modelMatrix);
_vertex.positionScreen = new Vector4(_vertex.positionWorld.x,
_vertex.positionWorld.y,
_vertex.positionWorld.z, 1.0); // _vertex.positionWorld.clone();
_viewProjectionMatrix.transform( _vertex.positionScreen );
_vertex.positionScreen.x /= _vertex.positionScreen.w;
_vertex.positionScreen.y /= _vertex.positionScreen.w;
_vertex.visible = _vertex.positionScreen.z > near && _vertex.positionScreen.z < far;
});
fl = faces.length;
for ( f = 0; f < fl; f ++ ) {
face = faces[ f ];
material = isFaceMaterial == true ? geometryMaterials[ face.materialIndex ] : object.material;
if ( material == null ) continue;
side = material.side;
var vtx = face.indices.map((idx) => _vertexPool[idx]).toList();
var allVtxVisible = vtx.every((v) => v.visible);
if (allVtxVisible) {
if (face.size == 3) {
visible = (
( ( vtx[2].positionScreen.x - vtx[0].positionScreen.x ) * ( vtx[1].positionScreen.y - vtx[0].positionScreen.y ) -
( vtx[2].positionScreen.y - vtx[0].positionScreen.y ) * ( vtx[1].positionScreen.x - vtx[0].positionScreen.x ) ) < 0);
} else if (face.size == 4) {
visible = ( vtx[3].positionScreen.x - vtx[0].positionScreen.x ) * ( vtx[1].positionScreen.y - vtx[0].positionScreen.y ) -
( vtx[3].positionScreen.y - vtx[0].positionScreen.y ) * ( vtx[1].positionScreen.x - vtx[0].positionScreen.x ) < 0 ||
( vtx[1].positionScreen.x - vtx[2].positionScreen.x ) * ( vtx[3].positionScreen.y - vtx[2].positionScreen.y ) -
( vtx[1].positionScreen.y - vtx[2].positionScreen.y ) * ( vtx[3].positionScreen.x - vtx[2].positionScreen.x ) < 0;
}
if ( side == DoubleSide || visible == ( side == FrontSide ) ) {
_face = (face.size == 3) ? getNextFace3InPool() : getNextFace4InPool();
_face.vertices = vtx.map((v) => v.clone()).toList(growable: false);
} else {
continue;
}
} else {
continue;
}
_face.normalWorld.setFrom( face.normal );
if ( visible == false && ( side == BackSide || side == DoubleSide ) ) _face.normalWorld.negate();
_face.normalWorld.applyProjection(rotationMatrix);
_face.centroidWorld.setFrom( face.centroid );
_face.centroidWorld.applyProjection(modelMatrix);
_face.centroidScreen.setFrom( _face.centroidWorld );
_face.centroidScreen.applyProjection(_viewProjectionMatrix);
faceVertexNormals = face.vertexNormals;
nl = faceVertexNormals.length;
for ( n = 0; n < nl; n ++ ) {
normal = _face.vertexNormalsWorld[ n ];
normal.setFrom( faceVertexNormals[ n ] );
if ( !visible && ( side == BackSide || side == DoubleSide ) ) normal.negate();
normal.applyProjection(rotationMatrix);
}
cl = faceVertexUvs.length;
for ( c = 0; c < cl; c ++ ) {
if ( faceVertexUvs[c].length == 0 ) continue;
List<UV> uvs = faceVertexUvs[ c ][ f ];
if ( uvs == null ) continue;
//TODO: interpreting this code as dynamically creating arrays.
ul = uvs.length;
for ( u = 0; u < ul; u ++ ) {
List faceUVs = _face.uvs[c];
faceUVs.add(uvs[u]);
//_face.uvs[ c ][ u ] = uvs[ u ];
}
}
_face.material = material;
_face.faceMaterial = face.materialIndex != null ? geometryMaterials[ face.materialIndex ] : null;
_face.z = _face.centroidScreen.z;
_renderData.elements.add( _face );
}
} else if ( object is Line ) {
_modelViewProjectionMatrix = _viewProjectionMatrix * modelMatrix;
vertices = object.geometry.vertices;
v1 = getNextVertexInPool();
Vector3 vec = vertices[ 0 ];
v1.positionScreen = new Vector4(vec.x, vec.y, vec.z, 1.0);
_modelViewProjectionMatrix.transform( v1.positionScreen );
// Handle LineStrip and LinePieces
var step = object.type == LinePieces ? 2 : 1;
vl = vertices.length;
for ( v = 1; v < vl; v++ ) {
v1 = getNextVertexInPool();
Vector3 vec = vertices[ v ];
v1.positionScreen = new Vector4(vec.x, vec.y, vec.z, 1.0);
_modelViewProjectionMatrix.transform( v1.positionScreen );
if ( ( v + 1 ) % step > 0 ) continue;
v2 = _vertexPool[ _vertexCount - 2 ];
_clippedVertex1PositionScreen.setFrom(v1.positionScreen);
_clippedVertex2PositionScreen.setFrom(v2.positionScreen);
if ( clipLine( _clippedVertex1PositionScreen, _clippedVertex2PositionScreen ) ) {
// Perform the perspective divide
_clippedVertex1PositionScreen /= _clippedVertex1PositionScreen.w;
_clippedVertex2PositionScreen /= _clippedVertex2PositionScreen.w;
_line = getNextLineInPool();
_line.v1.positionScreen.setFrom(_clippedVertex1PositionScreen);
_line.v2.positionScreen.setFrom(_clippedVertex2PositionScreen);
_line.z = Math.max( _clippedVertex1PositionScreen.z, _clippedVertex2PositionScreen.z );
_line.material = object.material;
_renderData.elements.add( _line );
}
}
}
});
_renderData.sprites.forEach((o) {
object = o.object;
modelMatrix = object.matrixWorld;
if ( object is Particle ) {
_vector4.setValues ( modelMatrix[12], modelMatrix[13], modelMatrix[14], 1.0 );
_viewProjectionMatrix.transform( _vector4 );
_vector4.z /= _vector4.w;
if ( _vector4.z > 0 && _vector4.z < 1 ) {
_particle = getNextParticleInPool();
_particle.x = _vector4.x / _vector4.w;
_particle.y = _vector4.y / _vector4.w;
_particle.z = _vector4.z;
_particle.rotation = object.rotation.z;
_particle.scale.x = object.scale.x * ( _particle.x - ( _vector4.x + camera.projectionMatrix[0] ) / ( _vector4.w + camera.projectionMatrix[12] ) ).abs();
_particle.scale.y = object.scale.y * ( _particle.y - ( _vector4.y + camera.projectionMatrix[5] ) / ( _vector4.w + camera.projectionMatrix[13] ) ).abs();
_particle.material = object.material as Material;
_renderData.elements.add( _particle );
}
}
});
if ( sort ) {
_renderData.elements.sort( painterSort );
}
return _renderData;
}
// Pools
RenderableObject getNextObjectInPool() {
//TODO: make sure I've interpreted this logic correctly
// RenderableObject object = _objectPool[ _objectCount ] = _objectPool[ _objectCount ] || new RenderableObject();
RenderableObject object;
if ( _objectCount < _objectPool.length ) {
object = ( _objectPool[ _objectCount ] != null ) ? _objectPool[ _objectCount ] : new RenderableObject();
} else {
object = new RenderableObject();
_objectPool.add(object);
}
_objectCount ++;
return object;
}
RenderableVertex getNextVertexInPool() {
//TODO: make sure I've interpreted this logic correctly
// var vertex = _vertexPool[ _vertexCount ] = _vertexPool[ _vertexCount ] || new THREE.RenderableVertex();
RenderableVertex vertex;
// Vertex is already within List
if ( _vertexCount < _vertexPool.length ) {
vertex = ( _vertexPool[ _vertexCount ] != null ) ? _vertexPool[ _vertexCount ] : new RenderableVertex();
} else {
vertex = new RenderableVertex();
_vertexPool.add(vertex);
}
_vertexCount ++;
return vertex;
}
RenderableFace getNextFace3InPool() {
//TODO: make sure I've interpreted this logic correctly
// RenderableFace3 face = _face3Pool[ _face3Count ] = _face3Pool[ _face3Count ] || new RenderableFace3();
RenderableFace3 face;
if ( _face3Count < _face3Pool.length ) {
face = ( _face3Pool[ _face3Count ] != null ) ? _face3Pool[ _face3Count ] : new RenderableFace3();
} else {
face = new RenderableFace3();
_face3Pool.add(face);
}
_face3Count ++;
return face;
}
RenderableFace getNextFace4InPool() {
RenderableFace4 face;
if ( _face4Count < _face4Pool.length ) {
face = ( _face4Pool[ _face4Count ] != null ) ? _face4Pool[ _face4Count ] : new RenderableFace4();
} else {
face = new RenderableFace4();
_face4Pool.add(face);
}
_face4Count ++;
return face;
}
RenderableLine getNextLineInPool() {
//TODO: make sure I've interpreted this logic correctly
//RenderableLine line = _linePool[ _lineCount ] = _linePool[ _lineCount ] || new RenderableLine();
RenderableLine line;
if ( _lineCount < _linePool.length ) {
line = ( _linePool[ _lineCount ] != null ) ? _linePool[ _lineCount ] : new RenderableLine();
} else {
line = new RenderableLine();
_linePool.add(line);
}
_lineCount ++;
return line;
}
RenderableParticle getNextParticleInPool() {
//TODO: make sure I've interpreted this logic correctly
//RenderableParticle particle = _particlePool[ _particleCount ] = _particlePool[ _particleCount ] || new RenderableParticle();
RenderableParticle particle;
if ( _particleCount < _particlePool.length ) {
particle = ( _particlePool[ _particleCount ] != null ) ? _particlePool[ _particleCount ] : new RenderableParticle();
} else {
particle = new RenderableParticle();
_particlePool.add(particle);
}
_particleCount ++;
return particle;
}
int painterSort( a, b ) => b.z.compareTo(a.z);
bool clipLine( Vector4 s1, Vector4 s2 ) {
double alpha1 = 0.0, alpha2 = 1.0,
// 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 = lerp4(s1, s2, alpha1 );
s2 = lerp4(s2, s1, 1.0 - alpha2 );
return true;
}
}
}
}
Constructors
new Projector() #
Creates a new Object instance.
Object instances have no meaningful state, and are only useful through their identity. An Object instance is equal to itself only.
docs inherited from Object
Projector()
: _objectPool = [],
_vertexPool = [],
_face3Pool = [],
_face4Pool = [],
_linePool = [],
_particlePool = [],
//_renderData = { "objects": [], "sprites": [], "lights": [], "elements": [] };
_renderData = new ProjectorRenderData(),
_vector3 = new Vector3.zero(),
_vector4 = new Vector4(0.0, 0.0, 0.0, 1.0),
_viewProjectionMatrix = new Matrix4.identity(),
_modelViewProjectionMatrix = new Matrix4.identity(),
_frustum = new Frustum(),
_clippedVertex1PositionScreen = new Vector4(0.0, 0.0, 0.0, 1.0),
_clippedVertex2PositionScreen = new Vector4(0.0, 0.0, 0.0, 1.0);
Methods
bool clipLine(Vector4 s1, Vector4 s2) #
bool clipLine( Vector4 s1, Vector4 s2 ) {
double alpha1 = 0.0, alpha2 = 1.0,
// 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 = lerp4(s1, s2, alpha1 );
s2 = lerp4(s2, s1, 1.0 - alpha2 );
return true;
}
}
}
RenderableFace getNextFace3InPool() #
RenderableFace getNextFace3InPool() {
//TODO: make sure I've interpreted this logic correctly
// RenderableFace3 face = _face3Pool[ _face3Count ] = _face3Pool[ _face3Count ] || new RenderableFace3();
RenderableFace3 face;
if ( _face3Count < _face3Pool.length ) {
face = ( _face3Pool[ _face3Count ] != null ) ? _face3Pool[ _face3Count ] : new RenderableFace3();
} else {
face = new RenderableFace3();
_face3Pool.add(face);
}
_face3Count ++;
return face;
}
RenderableFace getNextFace4InPool() #
RenderableFace getNextFace4InPool() {
RenderableFace4 face;
if ( _face4Count < _face4Pool.length ) {
face = ( _face4Pool[ _face4Count ] != null ) ? _face4Pool[ _face4Count ] : new RenderableFace4();
} else {
face = new RenderableFace4();
_face4Pool.add(face);
}
_face4Count ++;
return face;
}
RenderableLine getNextLineInPool() #
RenderableLine getNextLineInPool() {
//TODO: make sure I've interpreted this logic correctly
//RenderableLine line = _linePool[ _lineCount ] = _linePool[ _lineCount ] || new RenderableLine();
RenderableLine line;
if ( _lineCount < _linePool.length ) {
line = ( _linePool[ _lineCount ] != null ) ? _linePool[ _lineCount ] : new RenderableLine();
} else {
line = new RenderableLine();
_linePool.add(line);
}
_lineCount ++;
return line;
}
RenderableObject getNextObjectInPool() #
RenderableObject getNextObjectInPool() {
//TODO: make sure I've interpreted this logic correctly
// RenderableObject object = _objectPool[ _objectCount ] = _objectPool[ _objectCount ] || new RenderableObject();
RenderableObject object;
if ( _objectCount < _objectPool.length ) {
object = ( _objectPool[ _objectCount ] != null ) ? _objectPool[ _objectCount ] : new RenderableObject();
} else {
object = new RenderableObject();
_objectPool.add(object);
}
_objectCount ++;
return object;
}
RenderableParticle getNextParticleInPool() #
RenderableParticle getNextParticleInPool() {
//TODO: make sure I've interpreted this logic correctly
//RenderableParticle particle = _particlePool[ _particleCount ] = _particlePool[ _particleCount ] || new RenderableParticle();
RenderableParticle particle;
if ( _particleCount < _particlePool.length ) {
particle = ( _particlePool[ _particleCount ] != null ) ? _particlePool[ _particleCount ] : new RenderableParticle();
} else {
particle = new RenderableParticle();
_particlePool.add(particle);
}
_particleCount ++;
return particle;
}
RenderableVertex getNextVertexInPool() #
RenderableVertex getNextVertexInPool() {
//TODO: make sure I've interpreted this logic correctly
// var vertex = _vertexPool[ _vertexCount ] = _vertexPool[ _vertexCount ] || new THREE.RenderableVertex();
RenderableVertex vertex;
// Vertex is already within List
if ( _vertexCount < _vertexPool.length ) {
vertex = ( _vertexPool[ _vertexCount ] != null ) ? _vertexPool[ _vertexCount ] : new RenderableVertex();
} else {
vertex = new RenderableVertex();
_vertexPool.add(vertex);
}
_vertexCount ++;
return vertex;
}
Ray pickingRay(Vector3 vector, Camera camera) #
Translates a 2D point from NDC to a THREE.Ray that can be used for picking. @vector - THREE.Vector3 that represents 2D point @camera - THREE.Camera
Ray pickingRay( Vector3 vector, Camera camera ) {
Vector3 end, ray, t;
// set two vectors with opposing z values
vector.z = -1.0;
end = new Vector3( vector.x, vector.y, 1.0 );
unprojectVector( vector, camera );
unprojectVector( end, camera );
// find direction from vector to end
end.sub( vector ).normalize();
return new Ray( vector, end );
}
ProjectorRenderData projectGraph(Object3D root, bool sort) #
ProjectorRenderData projectGraph( Object3D root, bool sort ) {
_objectCount = 0;
_renderData.objects = [];
_renderData.sprites = [];
_renderData.lights = [];
_projectObject( root );
//TODO: assuming this is a form of 'if' statement.
//sort && _renderData.objects.sort( painterSort );
if (sort) {
_renderData.objects.sort( painterSort );
}
return _renderData;
}
ProjectorRenderData projectScene(Scene scene, Camera camera, bool sort) #
ProjectorRenderData projectScene( Scene scene, Camera camera, bool sort ) {
num near = camera.near, far = camera.far;
bool visible = false;
int o, ol, v, vl, f, fl, n, nl, c, cl, u, ul;
Object3D object;
Matrix4 modelMatrix, rotationMatrix;
Geometry geometry;
List geometryMaterials;
List<Vector3> vertices;
Vector3 vertex;
Vector3 vertexPositionScreen, normal;
List<Face> faces;
Face face;
RenderableFace _face;
List faceVertexNormals;
List<List> faceVertexUvs;
RenderableVertex v1, v2, v3, v4;
bool isFaceMaterial;
Material material;
int side;
_face3Count = 0;
_face4Count = 0;
_lineCount = 0;
_particleCount = 0;
_renderData.elements = [];
scene.updateMatrixWorld();
if ( camera.parent == null ) {
// console.warn( 'DEPRECATED: Camera hasn\'t been added to a Scene. Adding it...' );
// scene.add( camera );
camera.updateMatrixWorld();
}
camera.matrixWorldInverse.copyInverse(camera.matrixWorld);
_viewProjectionMatrix = camera.projectionMatrix * camera.matrixWorldInverse;
_frustum.setFromMatrix( _viewProjectionMatrix );
_renderData = projectGraph( scene, false );
_renderData.objects.forEach((o) {
object = o.object;
modelMatrix = object.matrixWorld;
_vertexCount = 0;
if ( object is Mesh ) {
geometry = object.geometry;
geometryMaterials = object.geometry.materials;
vertices = geometry.vertices;
faces = geometry.faces;
faceVertexUvs = geometry.faceVertexUvs;
extractRotation( object.matrixRotationWorld, modelMatrix );
rotationMatrix = object.matrixRotationWorld;
isFaceMaterial = (object.material is MeshFaceMaterial);
side = object.material.side;
vertices.forEach((Vector3 v) {
_vertex = getNextVertexInPool();
_vertex.positionWorld.setFrom(v);
_vertex.positionWorld.applyProjection(modelMatrix);
_vertex.positionScreen = new Vector4(_vertex.positionWorld.x,
_vertex.positionWorld.y,
_vertex.positionWorld.z, 1.0); // _vertex.positionWorld.clone();
_viewProjectionMatrix.transform( _vertex.positionScreen );
_vertex.positionScreen.x /= _vertex.positionScreen.w;
_vertex.positionScreen.y /= _vertex.positionScreen.w;
_vertex.visible = _vertex.positionScreen.z > near && _vertex.positionScreen.z < far;
});
fl = faces.length;
for ( f = 0; f < fl; f ++ ) {
face = faces[ f ];
material = isFaceMaterial == true ? geometryMaterials[ face.materialIndex ] : object.material;
if ( material == null ) continue;
side = material.side;
var vtx = face.indices.map((idx) => _vertexPool[idx]).toList();
var allVtxVisible = vtx.every((v) => v.visible);
if (allVtxVisible) {
if (face.size == 3) {
visible = (
( ( vtx[2].positionScreen.x - vtx[0].positionScreen.x ) * ( vtx[1].positionScreen.y - vtx[0].positionScreen.y ) -
( vtx[2].positionScreen.y - vtx[0].positionScreen.y ) * ( vtx[1].positionScreen.x - vtx[0].positionScreen.x ) ) < 0);
} else if (face.size == 4) {
visible = ( vtx[3].positionScreen.x - vtx[0].positionScreen.x ) * ( vtx[1].positionScreen.y - vtx[0].positionScreen.y ) -
( vtx[3].positionScreen.y - vtx[0].positionScreen.y ) * ( vtx[1].positionScreen.x - vtx[0].positionScreen.x ) < 0 ||
( vtx[1].positionScreen.x - vtx[2].positionScreen.x ) * ( vtx[3].positionScreen.y - vtx[2].positionScreen.y ) -
( vtx[1].positionScreen.y - vtx[2].positionScreen.y ) * ( vtx[3].positionScreen.x - vtx[2].positionScreen.x ) < 0;
}
if ( side == DoubleSide || visible == ( side == FrontSide ) ) {
_face = (face.size == 3) ? getNextFace3InPool() : getNextFace4InPool();
_face.vertices = vtx.map((v) => v.clone()).toList(growable: false);
} else {
continue;
}
} else {
continue;
}
_face.normalWorld.setFrom( face.normal );
if ( visible == false && ( side == BackSide || side == DoubleSide ) ) _face.normalWorld.negate();
_face.normalWorld.applyProjection(rotationMatrix);
_face.centroidWorld.setFrom( face.centroid );
_face.centroidWorld.applyProjection(modelMatrix);
_face.centroidScreen.setFrom( _face.centroidWorld );
_face.centroidScreen.applyProjection(_viewProjectionMatrix);
faceVertexNormals = face.vertexNormals;
nl = faceVertexNormals.length;
for ( n = 0; n < nl; n ++ ) {
normal = _face.vertexNormalsWorld[ n ];
normal.setFrom( faceVertexNormals[ n ] );
if ( !visible && ( side == BackSide || side == DoubleSide ) ) normal.negate();
normal.applyProjection(rotationMatrix);
}
cl = faceVertexUvs.length;
for ( c = 0; c < cl; c ++ ) {
if ( faceVertexUvs[c].length == 0 ) continue;
List<UV> uvs = faceVertexUvs[ c ][ f ];
if ( uvs == null ) continue;
//TODO: interpreting this code as dynamically creating arrays.
ul = uvs.length;
for ( u = 0; u < ul; u ++ ) {
List faceUVs = _face.uvs[c];
faceUVs.add(uvs[u]);
//_face.uvs[ c ][ u ] = uvs[ u ];
}
}
_face.material = material;
_face.faceMaterial = face.materialIndex != null ? geometryMaterials[ face.materialIndex ] : null;
_face.z = _face.centroidScreen.z;
_renderData.elements.add( _face );
}
} else if ( object is Line ) {
_modelViewProjectionMatrix = _viewProjectionMatrix * modelMatrix;
vertices = object.geometry.vertices;
v1 = getNextVertexInPool();
Vector3 vec = vertices[ 0 ];
v1.positionScreen = new Vector4(vec.x, vec.y, vec.z, 1.0);
_modelViewProjectionMatrix.transform( v1.positionScreen );
// Handle LineStrip and LinePieces
var step = object.type == LinePieces ? 2 : 1;
vl = vertices.length;
for ( v = 1; v < vl; v++ ) {
v1 = getNextVertexInPool();
Vector3 vec = vertices[ v ];
v1.positionScreen = new Vector4(vec.x, vec.y, vec.z, 1.0);
_modelViewProjectionMatrix.transform( v1.positionScreen );
if ( ( v + 1 ) % step > 0 ) continue;
v2 = _vertexPool[ _vertexCount - 2 ];
_clippedVertex1PositionScreen.setFrom(v1.positionScreen);
_clippedVertex2PositionScreen.setFrom(v2.positionScreen);
if ( clipLine( _clippedVertex1PositionScreen, _clippedVertex2PositionScreen ) ) {
// Perform the perspective divide
_clippedVertex1PositionScreen /= _clippedVertex1PositionScreen.w;
_clippedVertex2PositionScreen /= _clippedVertex2PositionScreen.w;
_line = getNextLineInPool();
_line.v1.positionScreen.setFrom(_clippedVertex1PositionScreen);
_line.v2.positionScreen.setFrom(_clippedVertex2PositionScreen);
_line.z = Math.max( _clippedVertex1PositionScreen.z, _clippedVertex2PositionScreen.z );
_line.material = object.material;
_renderData.elements.add( _line );
}
}
}
});
_renderData.sprites.forEach((o) {
object = o.object;
modelMatrix = object.matrixWorld;
if ( object is Particle ) {
_vector4.setValues ( modelMatrix[12], modelMatrix[13], modelMatrix[14], 1.0 );
_viewProjectionMatrix.transform( _vector4 );
_vector4.z /= _vector4.w;
if ( _vector4.z > 0 && _vector4.z < 1 ) {
_particle = getNextParticleInPool();
_particle.x = _vector4.x / _vector4.w;
_particle.y = _vector4.y / _vector4.w;
_particle.z = _vector4.z;
_particle.rotation = object.rotation.z;
_particle.scale.x = object.scale.x * ( _particle.x - ( _vector4.x + camera.projectionMatrix[0] ) / ( _vector4.w + camera.projectionMatrix[12] ) ).abs();
_particle.scale.y = object.scale.y * ( _particle.y - ( _vector4.y + camera.projectionMatrix[5] ) / ( _vector4.w + camera.projectionMatrix[13] ) ).abs();
_particle.material = object.material as Material;
_renderData.elements.add( _particle );
}
}
});
if ( sort ) {
_renderData.elements.sort( painterSort );
}
return _renderData;
}
Vector3 projectVector(Vector3 vector, Camera camera) #
Vector3 projectVector( Vector3 vector, Camera camera ) {
camera.matrixWorldInverse.copyInverse(camera.matrixWorld);
_viewProjectionMatrix = camera.projectionMatrix * camera.matrixWorldInverse;
return vector.applyProjection(_viewProjectionMatrix);
}
Vector3 unprojectVector(Vector3 vector, Camera camera) #
Vector3 unprojectVector( Vector3 vector, Camera camera ) {
camera.projectionMatrixInverse.copyInverse(camera.projectionMatrix);
_viewProjectionMatrix = camera.matrixWorld * camera.projectionMatrixInverse;
return vector.applyProjection(_viewProjectionMatrix);
}