Dies ist das Repository meines kleinen Portfolios.
Im Hintergrund läuft eine Planetensimulation, geschrieben in JavaScript und Three.js.
Die zu sehenden Texturen stammen von:
https://www.solarsystemscope.com/textures/
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
967 lines
20 KiB
967 lines
20 KiB
import { |
|
Box3, |
|
Color, |
|
DoubleSide, |
|
Frustum, |
|
Matrix3, |
|
Matrix4, |
|
Vector2, |
|
Vector3, |
|
Vector4 |
|
} from 'three'; |
|
|
|
class RenderableObject { |
|
|
|
constructor() { |
|
|
|
this.id = 0; |
|
|
|
this.object = null; |
|
this.z = 0; |
|
this.renderOrder = 0; |
|
|
|
} |
|
|
|
} |
|
|
|
// |
|
|
|
class RenderableFace { |
|
|
|
constructor() { |
|
|
|
this.id = 0; |
|
|
|
this.v1 = new RenderableVertex(); |
|
this.v2 = new RenderableVertex(); |
|
this.v3 = new RenderableVertex(); |
|
|
|
this.normalModel = new Vector3(); |
|
|
|
this.vertexNormalsModel = [ new Vector3(), new Vector3(), new Vector3() ]; |
|
this.vertexNormalsLength = 0; |
|
|
|
this.color = new Color(); |
|
this.material = null; |
|
this.uvs = [ new Vector2(), new Vector2(), new Vector2() ]; |
|
|
|
this.z = 0; |
|
this.renderOrder = 0; |
|
|
|
} |
|
|
|
} |
|
|
|
// |
|
|
|
class RenderableVertex { |
|
|
|
constructor() { |
|
|
|
this.position = new Vector3(); |
|
this.positionWorld = new Vector3(); |
|
this.positionScreen = new Vector4(); |
|
|
|
this.visible = true; |
|
|
|
} |
|
|
|
copy( vertex ) { |
|
|
|
this.positionWorld.copy( vertex.positionWorld ); |
|
this.positionScreen.copy( vertex.positionScreen ); |
|
|
|
} |
|
|
|
} |
|
|
|
// |
|
|
|
class RenderableLine { |
|
|
|
constructor() { |
|
|
|
this.id = 0; |
|
|
|
this.v1 = new RenderableVertex(); |
|
this.v2 = new RenderableVertex(); |
|
|
|
this.vertexColors = [ new Color(), new Color() ]; |
|
this.material = null; |
|
|
|
this.z = 0; |
|
this.renderOrder = 0; |
|
|
|
} |
|
|
|
} |
|
|
|
// |
|
|
|
class RenderableSprite { |
|
|
|
constructor() { |
|
|
|
this.id = 0; |
|
|
|
this.object = null; |
|
|
|
this.x = 0; |
|
this.y = 0; |
|
this.z = 0; |
|
|
|
this.rotation = 0; |
|
this.scale = new Vector2(); |
|
|
|
this.material = null; |
|
this.renderOrder = 0; |
|
|
|
} |
|
|
|
} |
|
|
|
// |
|
|
|
class Projector { |
|
|
|
constructor() { |
|
|
|
let _object, _objectCount, _objectPoolLength = 0, |
|
_vertex, _vertexCount, _vertexPoolLength = 0, |
|
_face, _faceCount, _facePoolLength = 0, |
|
_line, _lineCount, _linePoolLength = 0, |
|
_sprite, _spriteCount, _spritePoolLength = 0, |
|
_modelMatrix; |
|
|
|
const |
|
|
|
_renderData = { objects: [], lights: [], elements: [] }, |
|
|
|
_vector3 = new Vector3(), |
|
_vector4 = new Vector4(), |
|
|
|
_clipBox = new Box3( new Vector3( - 1, - 1, - 1 ), new Vector3( 1, 1, 1 ) ), |
|
_boundingBox = new Box3(), |
|
_points3 = new Array( 3 ), |
|
|
|
_viewMatrix = new Matrix4(), |
|
_viewProjectionMatrix = new Matrix4(), |
|
|
|
_modelViewProjectionMatrix = new Matrix4(), |
|
|
|
_frustum = new Frustum(), |
|
|
|
_objectPool = [], _vertexPool = [], _facePool = [], _linePool = [], _spritePool = []; |
|
|
|
// |
|
|
|
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 () { |
|
|
|
console.error( 'THREE.Projector: .pickingRay() is now raycaster.setFromCamera().' ); |
|
|
|
}; |
|
|
|
// |
|
|
|
function RenderList() { |
|
|
|
const normals = []; |
|
const colors = []; |
|
const uvs = []; |
|
|
|
let object = null; |
|
|
|
const normalMatrix = new Matrix3(); |
|
|
|
function setObject( value ) { |
|
|
|
object = value; |
|
|
|
normalMatrix.getNormalMatrix( object.matrixWorld ); |
|
|
|
normals.length = 0; |
|
colors.length = 0; |
|
uvs.length = 0; |
|
|
|
} |
|
|
|
function projectVertex( vertex ) { |
|
|
|
const position = vertex.position; |
|
const positionWorld = vertex.positionWorld; |
|
const positionScreen = vertex.positionScreen; |
|
|
|
positionWorld.copy( position ).applyMatrix4( _modelMatrix ); |
|
positionScreen.copy( positionWorld ).applyMatrix4( _viewProjectionMatrix ); |
|
|
|
const 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; |
|
|
|
} |
|
|
|
function pushVertex( x, y, z ) { |
|
|
|
_vertex = getNextVertexInPool(); |
|
_vertex.position.set( x, y, z ); |
|
|
|
projectVertex( _vertex ); |
|
|
|
} |
|
|
|
function pushNormal( x, y, z ) { |
|
|
|
normals.push( x, y, z ); |
|
|
|
} |
|
|
|
function pushColor( r, g, b ) { |
|
|
|
colors.push( r, g, b ); |
|
|
|
} |
|
|
|
function pushUv( x, y ) { |
|
|
|
uvs.push( x, y ); |
|
|
|
} |
|
|
|
function checkTriangleVisibility( 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.intersectsBox( _boundingBox.setFromPoints( _points3 ) ); |
|
|
|
} |
|
|
|
function checkBackfaceCulling( 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; |
|
|
|
} |
|
|
|
function pushLine( a, b ) { |
|
|
|
const v1 = _vertexPool[ a ]; |
|
const v2 = _vertexPool[ b ]; |
|
|
|
// Clip |
|
|
|
v1.positionScreen.copy( v1.position ).applyMatrix4( _modelViewProjectionMatrix ); |
|
v2.positionScreen.copy( v2.position ).applyMatrix4( _modelViewProjectionMatrix ); |
|
|
|
if ( clipLine( v1.positionScreen, v2.positionScreen ) === true ) { |
|
|
|
// Perform the perspective divide |
|
v1.positionScreen.multiplyScalar( 1 / v1.positionScreen.w ); |
|
v2.positionScreen.multiplyScalar( 1 / v2.positionScreen.w ); |
|
|
|
_line = getNextLineInPool(); |
|
_line.id = object.id; |
|
_line.v1.copy( v1 ); |
|
_line.v2.copy( v2 ); |
|
_line.z = Math.max( v1.positionScreen.z, v2.positionScreen.z ); |
|
_line.renderOrder = object.renderOrder; |
|
|
|
_line.material = object.material; |
|
|
|
if ( object.material.vertexColors ) { |
|
|
|
_line.vertexColors[ 0 ].fromArray( colors, a * 3 ); |
|
_line.vertexColors[ 1 ].fromArray( colors, b * 3 ); |
|
|
|
} |
|
|
|
_renderData.elements.push( _line ); |
|
|
|
} |
|
|
|
} |
|
|
|
function pushTriangle( a, b, c, material ) { |
|
|
|
const v1 = _vertexPool[ a ]; |
|
const v2 = _vertexPool[ b ]; |
|
const v3 = _vertexPool[ c ]; |
|
|
|
if ( checkTriangleVisibility( v1, v2, v3 ) === false ) return; |
|
|
|
if ( material.side === 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; |
|
_face.renderOrder = object.renderOrder; |
|
|
|
// face normal |
|
_vector3.subVectors( v3.position, v2.position ); |
|
_vector4.subVectors( v1.position, v2.position ); |
|
_vector3.cross( _vector4 ); |
|
_face.normalModel.copy( _vector3 ); |
|
_face.normalModel.applyMatrix3( normalMatrix ).normalize(); |
|
|
|
for ( let i = 0; i < 3; i ++ ) { |
|
|
|
const normal = _face.vertexNormalsModel[ i ]; |
|
normal.fromArray( normals, arguments[ i ] * 3 ); |
|
normal.applyMatrix3( normalMatrix ).normalize(); |
|
|
|
const uv = _face.uvs[ i ]; |
|
uv.fromArray( uvs, arguments[ i ] * 2 ); |
|
|
|
} |
|
|
|
_face.vertexNormalsLength = 3; |
|
|
|
_face.material = material; |
|
|
|
if ( material.vertexColors ) { |
|
|
|
_face.color.fromArray( colors, a * 3 ); |
|
|
|
} |
|
|
|
_renderData.elements.push( _face ); |
|
|
|
} |
|
|
|
} |
|
|
|
return { |
|
setObject: setObject, |
|
projectVertex: projectVertex, |
|
checkTriangleVisibility: checkTriangleVisibility, |
|
checkBackfaceCulling: checkBackfaceCulling, |
|
pushVertex: pushVertex, |
|
pushNormal: pushNormal, |
|
pushColor: pushColor, |
|
pushUv: pushUv, |
|
pushLine: pushLine, |
|
pushTriangle: pushTriangle |
|
}; |
|
|
|
} |
|
|
|
const renderList = new RenderList(); |
|
|
|
function projectObject( object ) { |
|
|
|
if ( object.visible === false ) return; |
|
|
|
if ( object.isLight ) { |
|
|
|
_renderData.lights.push( object ); |
|
|
|
} else if ( object.isMesh || object.isLine || object.isPoints ) { |
|
|
|
if ( object.material.visible === false ) return; |
|
if ( object.frustumCulled === true && _frustum.intersectsObject( object ) === false ) return; |
|
|
|
addObject( object ); |
|
|
|
} else if ( object.isSprite ) { |
|
|
|
if ( object.material.visible === false ) return; |
|
if ( object.frustumCulled === true && _frustum.intersectsSprite( object ) === false ) return; |
|
|
|
addObject( object ); |
|
|
|
} |
|
|
|
const children = object.children; |
|
|
|
for ( let i = 0, l = children.length; i < l; i ++ ) { |
|
|
|
projectObject( children[ i ] ); |
|
|
|
} |
|
|
|
} |
|
|
|
function addObject( object ) { |
|
|
|
_object = getNextObjectInPool(); |
|
_object.id = object.id; |
|
_object.object = object; |
|
|
|
_vector3.setFromMatrixPosition( object.matrixWorld ); |
|
_vector3.applyMatrix4( _viewProjectionMatrix ); |
|
_object.z = _vector3.z; |
|
_object.renderOrder = object.renderOrder; |
|
|
|
_renderData.objects.push( _object ); |
|
|
|
} |
|
|
|
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 === null ) camera.updateMatrixWorld(); |
|
|
|
_viewMatrix.copy( camera.matrixWorldInverse ); |
|
_viewProjectionMatrix.multiplyMatrices( camera.projectionMatrix, _viewMatrix ); |
|
|
|
_frustum.setFromProjectionMatrix( _viewProjectionMatrix ); |
|
|
|
// |
|
|
|
_objectCount = 0; |
|
|
|
_renderData.objects.length = 0; |
|
_renderData.lights.length = 0; |
|
|
|
projectObject( scene ); |
|
|
|
if ( sortObjects === true ) { |
|
|
|
_renderData.objects.sort( painterSort ); |
|
|
|
} |
|
|
|
// |
|
|
|
const objects = _renderData.objects; |
|
|
|
for ( let o = 0, ol = objects.length; o < ol; o ++ ) { |
|
|
|
const object = objects[ o ].object; |
|
const geometry = object.geometry; |
|
|
|
renderList.setObject( object ); |
|
|
|
_modelMatrix = object.matrixWorld; |
|
|
|
_vertexCount = 0; |
|
|
|
if ( object.isMesh ) { |
|
|
|
if ( geometry.isBufferGeometry ) { |
|
|
|
let material = object.material; |
|
|
|
const isMultiMaterial = Array.isArray( material ); |
|
|
|
const attributes = geometry.attributes; |
|
const groups = geometry.groups; |
|
|
|
if ( attributes.position === undefined ) continue; |
|
|
|
const positions = attributes.position.array; |
|
|
|
for ( let i = 0, l = positions.length; i < l; i += 3 ) { |
|
|
|
let x = positions[ i ]; |
|
let y = positions[ i + 1 ]; |
|
let z = positions[ i + 2 ]; |
|
|
|
const morphTargets = geometry.morphAttributes.position; |
|
|
|
if ( morphTargets !== undefined ) { |
|
|
|
const morphTargetsRelative = geometry.morphTargetsRelative; |
|
const morphInfluences = object.morphTargetInfluences; |
|
|
|
for ( let t = 0, tl = morphTargets.length; t < tl; t ++ ) { |
|
|
|
const influence = morphInfluences[ t ]; |
|
|
|
if ( influence === 0 ) continue; |
|
|
|
const target = morphTargets[ t ]; |
|
|
|
if ( morphTargetsRelative ) { |
|
|
|
x += target.getX( i / 3 ) * influence; |
|
y += target.getY( i / 3 ) * influence; |
|
z += target.getZ( i / 3 ) * influence; |
|
|
|
} else { |
|
|
|
x += ( target.getX( i / 3 ) - positions[ i ] ) * influence; |
|
y += ( target.getY( i / 3 ) - positions[ i + 1 ] ) * influence; |
|
z += ( target.getZ( i / 3 ) - positions[ i + 2 ] ) * influence; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
renderList.pushVertex( x, y, z ); |
|
|
|
} |
|
|
|
if ( attributes.normal !== undefined ) { |
|
|
|
const normals = attributes.normal.array; |
|
|
|
for ( let i = 0, l = normals.length; i < l; i += 3 ) { |
|
|
|
renderList.pushNormal( normals[ i ], normals[ i + 1 ], normals[ i + 2 ] ); |
|
|
|
} |
|
|
|
} |
|
|
|
if ( attributes.color !== undefined ) { |
|
|
|
const colors = attributes.color.array; |
|
|
|
for ( let i = 0, l = colors.length; i < l; i += 3 ) { |
|
|
|
renderList.pushColor( colors[ i ], colors[ i + 1 ], colors[ i + 2 ] ); |
|
|
|
} |
|
|
|
} |
|
|
|
if ( attributes.uv !== undefined ) { |
|
|
|
const uvs = attributes.uv.array; |
|
|
|
for ( let i = 0, l = uvs.length; i < l; i += 2 ) { |
|
|
|
renderList.pushUv( uvs[ i ], uvs[ i + 1 ] ); |
|
|
|
} |
|
|
|
} |
|
|
|
if ( geometry.index !== null ) { |
|
|
|
const indices = geometry.index.array; |
|
|
|
if ( groups.length > 0 ) { |
|
|
|
for ( let g = 0; g < groups.length; g ++ ) { |
|
|
|
const group = groups[ g ]; |
|
|
|
material = isMultiMaterial === true |
|
? object.material[ group.materialIndex ] |
|
: object.material; |
|
|
|
if ( material === undefined ) continue; |
|
|
|
for ( let i = group.start, l = group.start + group.count; i < l; i += 3 ) { |
|
|
|
renderList.pushTriangle( indices[ i ], indices[ i + 1 ], indices[ i + 2 ], material ); |
|
|
|
} |
|
|
|
} |
|
|
|
} else { |
|
|
|
for ( let i = 0, l = indices.length; i < l; i += 3 ) { |
|
|
|
renderList.pushTriangle( indices[ i ], indices[ i + 1 ], indices[ i + 2 ], material ); |
|
|
|
} |
|
|
|
} |
|
|
|
} else { |
|
|
|
if ( groups.length > 0 ) { |
|
|
|
for ( let g = 0; g < groups.length; g ++ ) { |
|
|
|
const group = groups[ g ]; |
|
|
|
material = isMultiMaterial === true |
|
? object.material[ group.materialIndex ] |
|
: object.material; |
|
|
|
if ( material === undefined ) continue; |
|
|
|
for ( let i = group.start, l = group.start + group.count; i < l; i += 3 ) { |
|
|
|
renderList.pushTriangle( i, i + 1, i + 2, material ); |
|
|
|
} |
|
|
|
} |
|
|
|
} else { |
|
|
|
for ( let i = 0, l = positions.length / 3; i < l; i += 3 ) { |
|
|
|
renderList.pushTriangle( i, i + 1, i + 2, material ); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} else if ( geometry.isGeometry ) { |
|
|
|
console.error( 'THREE.Projector no longer supports Geometry. Use THREE.BufferGeometry instead.' ); |
|
return; |
|
|
|
} |
|
|
|
} else if ( object.isLine ) { |
|
|
|
_modelViewProjectionMatrix.multiplyMatrices( _viewProjectionMatrix, _modelMatrix ); |
|
|
|
if ( geometry.isBufferGeometry ) { |
|
|
|
const attributes = geometry.attributes; |
|
|
|
if ( attributes.position !== undefined ) { |
|
|
|
const positions = attributes.position.array; |
|
|
|
for ( let i = 0, l = positions.length; i < l; i += 3 ) { |
|
|
|
renderList.pushVertex( positions[ i ], positions[ i + 1 ], positions[ i + 2 ] ); |
|
|
|
} |
|
|
|
if ( attributes.color !== undefined ) { |
|
|
|
const colors = attributes.color.array; |
|
|
|
for ( let i = 0, l = colors.length; i < l; i += 3 ) { |
|
|
|
renderList.pushColor( colors[ i ], colors[ i + 1 ], colors[ i + 2 ] ); |
|
|
|
} |
|
|
|
} |
|
|
|
if ( geometry.index !== null ) { |
|
|
|
const indices = geometry.index.array; |
|
|
|
for ( let i = 0, l = indices.length; i < l; i += 2 ) { |
|
|
|
renderList.pushLine( indices[ i ], indices[ i + 1 ] ); |
|
|
|
} |
|
|
|
} else { |
|
|
|
const step = object.isLineSegments ? 2 : 1; |
|
|
|
for ( let i = 0, l = ( positions.length / 3 ) - 1; i < l; i += step ) { |
|
|
|
renderList.pushLine( i, i + 1 ); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} else if ( geometry.isGeometry ) { |
|
|
|
console.error( 'THREE.Projector no longer supports Geometry. Use THREE.BufferGeometry instead.' ); |
|
return; |
|
|
|
} |
|
|
|
} else if ( object.isPoints ) { |
|
|
|
_modelViewProjectionMatrix.multiplyMatrices( _viewProjectionMatrix, _modelMatrix ); |
|
|
|
if ( geometry.isGeometry ) { |
|
|
|
console.error( 'THREE.Projector no longer supports Geometry. Use THREE.BufferGeometry instead.' ); |
|
return; |
|
|
|
} else if ( geometry.isBufferGeometry ) { |
|
|
|
const attributes = geometry.attributes; |
|
|
|
if ( attributes.position !== undefined ) { |
|
|
|
const positions = attributes.position.array; |
|
|
|
for ( let i = 0, l = positions.length; i < l; i += 3 ) { |
|
|
|
_vector4.set( positions[ i ], positions[ i + 1 ], positions[ i + 2 ], 1 ); |
|
_vector4.applyMatrix4( _modelViewProjectionMatrix ); |
|
|
|
pushPoint( _vector4, object, camera ); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} else if ( object.isSprite ) { |
|
|
|
object.modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, object.matrixWorld ); |
|
_vector4.set( _modelMatrix.elements[ 12 ], _modelMatrix.elements[ 13 ], _modelMatrix.elements[ 14 ], 1 ); |
|
_vector4.applyMatrix4( _viewProjectionMatrix ); |
|
|
|
pushPoint( _vector4, object, camera ); |
|
|
|
} |
|
|
|
} |
|
|
|
if ( sortElements === true ) { |
|
|
|
_renderData.elements.sort( painterSort ); |
|
|
|
} |
|
|
|
return _renderData; |
|
|
|
}; |
|
|
|
function pushPoint( _vector4, object, camera ) { |
|
|
|
const 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.renderOrder = object.renderOrder; |
|
_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 ); |
|
|
|
} |
|
|
|
} |
|
|
|
// Pools |
|
|
|
function getNextObjectInPool() { |
|
|
|
if ( _objectCount === _objectPoolLength ) { |
|
|
|
const object = new RenderableObject(); |
|
_objectPool.push( object ); |
|
_objectPoolLength ++; |
|
_objectCount ++; |
|
return object; |
|
|
|
} |
|
|
|
return _objectPool[ _objectCount ++ ]; |
|
|
|
} |
|
|
|
function getNextVertexInPool() { |
|
|
|
if ( _vertexCount === _vertexPoolLength ) { |
|
|
|
const vertex = new RenderableVertex(); |
|
_vertexPool.push( vertex ); |
|
_vertexPoolLength ++; |
|
_vertexCount ++; |
|
return vertex; |
|
|
|
} |
|
|
|
return _vertexPool[ _vertexCount ++ ]; |
|
|
|
} |
|
|
|
function getNextFaceInPool() { |
|
|
|
if ( _faceCount === _facePoolLength ) { |
|
|
|
const face = new RenderableFace(); |
|
_facePool.push( face ); |
|
_facePoolLength ++; |
|
_faceCount ++; |
|
return face; |
|
|
|
} |
|
|
|
return _facePool[ _faceCount ++ ]; |
|
|
|
|
|
} |
|
|
|
function getNextLineInPool() { |
|
|
|
if ( _lineCount === _linePoolLength ) { |
|
|
|
const line = new RenderableLine(); |
|
_linePool.push( line ); |
|
_linePoolLength ++; |
|
_lineCount ++; |
|
return line; |
|
|
|
} |
|
|
|
return _linePool[ _lineCount ++ ]; |
|
|
|
} |
|
|
|
function getNextSpriteInPool() { |
|
|
|
if ( _spriteCount === _spritePoolLength ) { |
|
|
|
const sprite = new RenderableSprite(); |
|
_spritePool.push( sprite ); |
|
_spritePoolLength ++; |
|
_spriteCount ++; |
|
return sprite; |
|
|
|
} |
|
|
|
return _spritePool[ _spriteCount ++ ]; |
|
|
|
} |
|
|
|
// |
|
|
|
function painterSort( a, b ) { |
|
|
|
if ( a.renderOrder !== b.renderOrder ) { |
|
|
|
return a.renderOrder - b.renderOrder; |
|
|
|
} else 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 ) { |
|
|
|
let alpha1 = 0, alpha2 = 1; |
|
|
|
// Calculate the boundary coordinate of each vertex for the near and far clip planes, |
|
// Z = -1 and Z = +1, respectively. |
|
|
|
const 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; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
export { RenderableObject, RenderableFace, RenderableVertex, RenderableLine, RenderableSprite, Projector };
|
|
|