Projector.js 26 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174
  1. import {
  2. Box3,
  3. Color,
  4. DoubleSide,
  5. Frustum,
  6. Matrix3,
  7. Matrix4,
  8. Vector2,
  9. Vector3,
  10. Vector4
  11. } from 'three';
  12. class RenderableObject {
  13. constructor() {
  14. this.id = 0;
  15. this.object = null;
  16. this.z = 0;
  17. this.renderOrder = 0;
  18. }
  19. }
  20. //
  21. class RenderableFace {
  22. constructor() {
  23. this.id = 0;
  24. this.v1 = new RenderableVertex();
  25. this.v2 = new RenderableVertex();
  26. this.v3 = new RenderableVertex();
  27. this.normalModel = new Vector3();
  28. this.vertexNormalsModel = [ new Vector3(), new Vector3(), new Vector3() ];
  29. this.vertexNormalsLength = 0;
  30. this.color = new Color();
  31. this.material = null;
  32. this.uvs = [ new Vector2(), new Vector2(), new Vector2() ];
  33. this.z = 0;
  34. this.renderOrder = 0;
  35. }
  36. }
  37. //
  38. class RenderableVertex {
  39. constructor() {
  40. this.position = new Vector3();
  41. this.positionWorld = new Vector3();
  42. this.positionScreen = new Vector4();
  43. this.visible = true;
  44. }
  45. copy( vertex ) {
  46. this.positionWorld.copy( vertex.positionWorld );
  47. this.positionScreen.copy( vertex.positionScreen );
  48. }
  49. }
  50. //
  51. class RenderableLine {
  52. constructor() {
  53. this.id = 0;
  54. this.v1 = new RenderableVertex();
  55. this.v2 = new RenderableVertex();
  56. this.vertexColors = [ new Color(), new Color() ];
  57. this.material = null;
  58. this.z = 0;
  59. this.renderOrder = 0;
  60. }
  61. }
  62. //
  63. class RenderableSprite {
  64. constructor() {
  65. this.id = 0;
  66. this.object = null;
  67. this.x = 0;
  68. this.y = 0;
  69. this.z = 0;
  70. this.rotation = 0;
  71. this.scale = new Vector2();
  72. this.material = null;
  73. this.renderOrder = 0;
  74. }
  75. }
  76. /**
  77. * This class can project a given scene in 3D space into a 2D representation
  78. * used for rendering with a 2D API. `Projector` is currently used by {@link SVGRenderer}
  79. * and was previously used by the legacy `CanvasRenderer`.
  80. *
  81. * @three_import import { Projector } from 'three/addons/renderers/Projector.js';
  82. */
  83. class Projector {
  84. /**
  85. * Constructs a new projector.
  86. */
  87. constructor() {
  88. let _object, _objectCount, _objectPoolLength = 0,
  89. _vertex, _vertexCount, _vertexPoolLength = 0,
  90. _face, _faceCount, _facePoolLength = 0,
  91. _line, _lineCount, _linePoolLength = 0,
  92. _sprite, _spriteCount, _spritePoolLength = 0,
  93. _modelMatrix, _clipInput = [], _clipOutput = [];
  94. const
  95. _renderData = { objects: [], lights: [], elements: [] },
  96. _vector3 = new Vector3(),
  97. _vector4 = new Vector4(),
  98. _clipBox = new Box3( new Vector3( - 1, - 1, - 1 ), new Vector3( 1, 1, 1 ) ),
  99. _boundingBox = new Box3(),
  100. _points3 = new Array( 3 ),
  101. _viewMatrix = new Matrix4(),
  102. _viewProjectionMatrix = new Matrix4(),
  103. _modelViewProjectionMatrix = new Matrix4(),
  104. _frustum = new Frustum(),
  105. _objectPool = [], _vertexPool = [], _facePool = [], _linePool = [], _spritePool = [],
  106. _clipVertexPool = [],
  107. _clipPos1 = new Vector4(),
  108. _clipPos2 = new Vector4(),
  109. _clipPos3 = new Vector4(),
  110. _screenVertexPool = [],
  111. _clipInputVertices = [ null, null, null ],
  112. _clipPlanes = [
  113. { sign: + 1 },
  114. { sign: - 1 }
  115. ];
  116. //
  117. function RenderList() {
  118. const normals = [];
  119. const colors = [];
  120. const uvs = [];
  121. let object = null;
  122. const normalMatrix = new Matrix3();
  123. function setObject( value ) {
  124. object = value;
  125. normalMatrix.getNormalMatrix( object.matrixWorld );
  126. normals.length = 0;
  127. colors.length = 0;
  128. uvs.length = 0;
  129. }
  130. function projectVertex( vertex ) {
  131. const position = vertex.position;
  132. const positionWorld = vertex.positionWorld;
  133. const positionScreen = vertex.positionScreen;
  134. positionWorld.copy( position ).applyMatrix4( _modelMatrix );
  135. positionScreen.copy( positionWorld ).applyMatrix4( _viewProjectionMatrix );
  136. const invW = 1 / positionScreen.w;
  137. positionScreen.x *= invW;
  138. positionScreen.y *= invW;
  139. positionScreen.z *= invW;
  140. vertex.visible = positionScreen.x >= - 1 && positionScreen.x <= 1 &&
  141. positionScreen.y >= - 1 && positionScreen.y <= 1 &&
  142. positionScreen.z >= - 1 && positionScreen.z <= 1;
  143. }
  144. function pushVertex( x, y, z ) {
  145. _vertex = getNextVertexInPool();
  146. _vertex.position.set( x, y, z );
  147. projectVertex( _vertex );
  148. }
  149. function pushNormal( x, y, z ) {
  150. normals.push( x, y, z );
  151. }
  152. function pushColor( r, g, b ) {
  153. colors.push( r, g, b );
  154. }
  155. function pushUv( x, y ) {
  156. uvs.push( x, y );
  157. }
  158. function checkTriangleVisibility( v1, v2, v3 ) {
  159. if ( v1.visible === true || v2.visible === true || v3.visible === true ) return true;
  160. _points3[ 0 ] = v1.positionScreen;
  161. _points3[ 1 ] = v2.positionScreen;
  162. _points3[ 2 ] = v3.positionScreen;
  163. return _clipBox.intersectsBox( _boundingBox.setFromPoints( _points3 ) );
  164. }
  165. function checkBackfaceCulling( v1, v2, v3 ) {
  166. return ( ( v3.positionScreen.x - v1.positionScreen.x ) *
  167. ( v2.positionScreen.y - v1.positionScreen.y ) -
  168. ( v3.positionScreen.y - v1.positionScreen.y ) *
  169. ( v2.positionScreen.x - v1.positionScreen.x ) ) < 0;
  170. }
  171. function pushLine( a, b ) {
  172. const v1 = _vertexPool[ a ];
  173. const v2 = _vertexPool[ b ];
  174. // Clip
  175. v1.positionScreen.copy( v1.position ).applyMatrix4( _modelViewProjectionMatrix );
  176. v2.positionScreen.copy( v2.position ).applyMatrix4( _modelViewProjectionMatrix );
  177. if ( clipLine( v1.positionScreen, v2.positionScreen ) === true ) {
  178. // Perform the perspective divide
  179. v1.positionScreen.multiplyScalar( 1 / v1.positionScreen.w );
  180. v2.positionScreen.multiplyScalar( 1 / v2.positionScreen.w );
  181. _line = getNextLineInPool();
  182. _line.id = object.id;
  183. _line.v1.copy( v1 );
  184. _line.v2.copy( v2 );
  185. _line.z = Math.max( v1.positionScreen.z, v2.positionScreen.z );
  186. _line.renderOrder = object.renderOrder;
  187. _line.material = object.material;
  188. if ( object.material.vertexColors ) {
  189. _line.vertexColors[ 0 ].fromArray( colors, a * 3 );
  190. _line.vertexColors[ 1 ].fromArray( colors, b * 3 );
  191. }
  192. _renderData.elements.push( _line );
  193. }
  194. }
  195. function pushTriangle( a, b, c, material ) {
  196. const v1 = _vertexPool[ a ];
  197. const v2 = _vertexPool[ b ];
  198. const v3 = _vertexPool[ c ];
  199. // Get homogeneous clip space positions (before perspective divide)
  200. _clipPos1.copy( v1.positionWorld ).applyMatrix4( _viewProjectionMatrix );
  201. _clipPos2.copy( v2.positionWorld ).applyMatrix4( _viewProjectionMatrix );
  202. _clipPos3.copy( v3.positionWorld ).applyMatrix4( _viewProjectionMatrix );
  203. // Check if triangle needs clipping
  204. const nearDist1 = _clipPos1.z + _clipPos1.w;
  205. const nearDist2 = _clipPos2.z + _clipPos2.w;
  206. const nearDist3 = _clipPos3.z + _clipPos3.w;
  207. const farDist1 = - _clipPos1.z + _clipPos1.w;
  208. const farDist2 = - _clipPos2.z + _clipPos2.w;
  209. const farDist3 = - _clipPos3.z + _clipPos3.w;
  210. // Check if completely outside
  211. if ( ( nearDist1 < 0 && nearDist2 < 0 && nearDist3 < 0 ) ||
  212. ( farDist1 < 0 && farDist2 < 0 && farDist3 < 0 ) ) {
  213. return; // Triangle completely clipped
  214. }
  215. // Check if completely inside (no clipping needed)
  216. if ( nearDist1 >= 0 && nearDist2 >= 0 && nearDist3 >= 0 &&
  217. farDist1 >= 0 && farDist2 >= 0 && farDist3 >= 0 ) {
  218. // No clipping needed - use original path
  219. if ( checkTriangleVisibility( v1, v2, v3 ) === false ) return;
  220. if ( material.side === DoubleSide || checkBackfaceCulling( v1, v2, v3 ) === true ) {
  221. _face = getNextFaceInPool();
  222. _face.id = object.id;
  223. _face.v1.copy( v1 );
  224. _face.v2.copy( v2 );
  225. _face.v3.copy( v3 );
  226. _face.z = ( v1.positionScreen.z + v2.positionScreen.z + v3.positionScreen.z ) / 3;
  227. _face.renderOrder = object.renderOrder;
  228. // face normal
  229. _vector3.subVectors( v3.position, v2.position );
  230. _vector4.subVectors( v1.position, v2.position );
  231. _vector3.cross( _vector4 );
  232. _face.normalModel.copy( _vector3 );
  233. _face.normalModel.applyMatrix3( normalMatrix ).normalize();
  234. for ( let i = 0; i < 3; i ++ ) {
  235. const normal = _face.vertexNormalsModel[ i ];
  236. normal.fromArray( normals, arguments[ i ] * 3 );
  237. normal.applyMatrix3( normalMatrix ).normalize();
  238. const uv = _face.uvs[ i ];
  239. uv.fromArray( uvs, arguments[ i ] * 2 );
  240. }
  241. _face.vertexNormalsLength = 3;
  242. _face.material = material;
  243. if ( material.vertexColors ) {
  244. _face.color.fromArray( colors, a * 3 );
  245. }
  246. _renderData.elements.push( _face );
  247. }
  248. return;
  249. }
  250. // Triangle needs clipping
  251. _clipInputVertices[ 0 ] = _clipPos1;
  252. _clipInputVertices[ 1 ] = _clipPos2;
  253. _clipInputVertices[ 2 ] = _clipPos3;
  254. const clippedCount = clipTriangle( _clipInputVertices );
  255. if ( clippedCount < 3 ) return; // Triangle completely clipped
  256. // Perform perspective divide on clipped vertices and create screen vertices
  257. for ( let i = 0; i < clippedCount; i ++ ) {
  258. const cv = _clipInput[ i ];
  259. // Get or create renderable vertex from pool
  260. let sv = _screenVertexPool[ i ];
  261. if ( ! sv ) {
  262. sv = new RenderableVertex();
  263. _screenVertexPool[ i ] = sv;
  264. }
  265. // Perform perspective divide
  266. const invW = 1 / cv.w;
  267. sv.positionScreen.set( cv.x * invW, cv.y * invW, cv.z * invW, 1 );
  268. // Interpolate world position (simplified - using weighted average based on barycentric-like coords)
  269. // For a proper implementation, we'd need to track interpolation weights
  270. sv.positionWorld.copy( v1.positionWorld );
  271. sv.visible = true;
  272. }
  273. // Triangulate the clipped polygon (simple fan triangulation)
  274. for ( let i = 1; i < clippedCount - 1; i ++ ) {
  275. const tv1 = _screenVertexPool[ 0 ];
  276. const tv2 = _screenVertexPool[ i ];
  277. const tv3 = _screenVertexPool[ i + 1 ];
  278. if ( material.side === DoubleSide || checkBackfaceCulling( tv1, tv2, tv3 ) === true ) {
  279. _face = getNextFaceInPool();
  280. _face.id = object.id;
  281. _face.v1.copy( tv1 );
  282. _face.v2.copy( tv2 );
  283. _face.v3.copy( tv3 );
  284. _face.z = ( tv1.positionScreen.z + tv2.positionScreen.z + tv3.positionScreen.z ) / 3;
  285. _face.renderOrder = object.renderOrder;
  286. // face normal - use original triangle's normal
  287. _vector3.subVectors( v3.position, v2.position );
  288. _vector4.subVectors( v1.position, v2.position );
  289. _vector3.cross( _vector4 );
  290. _face.normalModel.copy( _vector3 );
  291. _face.normalModel.applyMatrix3( normalMatrix ).normalize();
  292. // Use original vertex normals and UVs (simplified - proper impl would interpolate)
  293. for ( let j = 0; j < 3; j ++ ) {
  294. const normal = _face.vertexNormalsModel[ j ];
  295. normal.fromArray( normals, arguments[ j ] * 3 );
  296. normal.applyMatrix3( normalMatrix ).normalize();
  297. const uv = _face.uvs[ j ];
  298. uv.fromArray( uvs, arguments[ j ] * 2 );
  299. }
  300. _face.vertexNormalsLength = 3;
  301. _face.material = material;
  302. if ( material.vertexColors ) {
  303. _face.color.fromArray( colors, a * 3 );
  304. }
  305. _renderData.elements.push( _face );
  306. }
  307. }
  308. }
  309. return {
  310. setObject: setObject,
  311. projectVertex: projectVertex,
  312. checkTriangleVisibility: checkTriangleVisibility,
  313. checkBackfaceCulling: checkBackfaceCulling,
  314. pushVertex: pushVertex,
  315. pushNormal: pushNormal,
  316. pushColor: pushColor,
  317. pushUv: pushUv,
  318. pushLine: pushLine,
  319. pushTriangle: pushTriangle
  320. };
  321. }
  322. const renderList = new RenderList();
  323. function projectObject( object ) {
  324. if ( object.visible === false ) return;
  325. if ( object.isLight ) {
  326. _renderData.lights.push( object );
  327. } else if ( object.isMesh || object.isLine || object.isPoints ) {
  328. if ( object.material.visible === false ) return;
  329. if ( object.frustumCulled === true && _frustum.intersectsObject( object ) === false ) return;
  330. addObject( object );
  331. } else if ( object.isSprite ) {
  332. if ( object.material.visible === false ) return;
  333. if ( object.frustumCulled === true && _frustum.intersectsSprite( object ) === false ) return;
  334. addObject( object );
  335. }
  336. const children = object.children;
  337. for ( let i = 0, l = children.length; i < l; i ++ ) {
  338. projectObject( children[ i ] );
  339. }
  340. }
  341. function addObject( object ) {
  342. _object = getNextObjectInPool();
  343. _object.id = object.id;
  344. _object.object = object;
  345. _vector3.setFromMatrixPosition( object.matrixWorld );
  346. _vector3.applyMatrix4( _viewProjectionMatrix );
  347. _object.z = _vector3.z;
  348. _object.renderOrder = object.renderOrder;
  349. _renderData.objects.push( _object );
  350. }
  351. /**
  352. * Projects the given scene in 3D space into a 2D representation. The result
  353. * is an object with renderable items.
  354. *
  355. * @param {Object3D} scene - A scene or any other type of 3D object.
  356. * @param {Camera} camera - The camera.
  357. * @param {boolean} sortObjects - Whether to sort objects or not.
  358. * @param {boolean} sortElements - Whether to sort elements (faces, lines and sprites) or not.
  359. * @return {{objects:Array<Objects>,lights:Array<Objects>,elements:Array<Objects>}} The projected scene as renderable objects.
  360. */
  361. this.projectScene = function ( scene, camera, sortObjects, sortElements ) {
  362. _faceCount = 0;
  363. _lineCount = 0;
  364. _spriteCount = 0;
  365. _renderData.elements.length = 0;
  366. if ( scene.matrixWorldAutoUpdate === true ) scene.updateMatrixWorld();
  367. if ( camera.parent === null && camera.matrixWorldAutoUpdate === true ) camera.updateMatrixWorld();
  368. _viewMatrix.copy( camera.matrixWorldInverse );
  369. _viewProjectionMatrix.multiplyMatrices( camera.projectionMatrix, _viewMatrix );
  370. _frustum.setFromProjectionMatrix( _viewProjectionMatrix );
  371. //
  372. _objectCount = 0;
  373. _renderData.objects.length = 0;
  374. _renderData.lights.length = 0;
  375. projectObject( scene );
  376. if ( sortObjects === true ) {
  377. painterSortStable( _renderData.objects, 0, _renderData.objects.length );
  378. }
  379. //
  380. const objects = _renderData.objects;
  381. for ( let o = 0, ol = objects.length; o < ol; o ++ ) {
  382. const object = objects[ o ].object;
  383. const geometry = object.geometry;
  384. renderList.setObject( object );
  385. _modelMatrix = object.matrixWorld;
  386. _vertexCount = 0;
  387. if ( object.isMesh ) {
  388. let material = object.material;
  389. const isMultiMaterial = Array.isArray( material );
  390. const attributes = geometry.attributes;
  391. const groups = geometry.groups;
  392. if ( attributes.position === undefined ) continue;
  393. const positions = attributes.position.array;
  394. for ( let i = 0, l = positions.length; i < l; i += 3 ) {
  395. let x = positions[ i ];
  396. let y = positions[ i + 1 ];
  397. let z = positions[ i + 2 ];
  398. const morphTargets = geometry.morphAttributes.position;
  399. if ( morphTargets !== undefined ) {
  400. const morphTargetsRelative = geometry.morphTargetsRelative;
  401. const morphInfluences = object.morphTargetInfluences;
  402. for ( let t = 0, tl = morphTargets.length; t < tl; t ++ ) {
  403. const influence = morphInfluences[ t ];
  404. if ( influence === 0 ) continue;
  405. const target = morphTargets[ t ];
  406. if ( morphTargetsRelative ) {
  407. x += target.getX( i / 3 ) * influence;
  408. y += target.getY( i / 3 ) * influence;
  409. z += target.getZ( i / 3 ) * influence;
  410. } else {
  411. x += ( target.getX( i / 3 ) - positions[ i ] ) * influence;
  412. y += ( target.getY( i / 3 ) - positions[ i + 1 ] ) * influence;
  413. z += ( target.getZ( i / 3 ) - positions[ i + 2 ] ) * influence;
  414. }
  415. }
  416. }
  417. renderList.pushVertex( x, y, z );
  418. }
  419. if ( attributes.normal !== undefined ) {
  420. const normals = attributes.normal.array;
  421. for ( let i = 0, l = normals.length; i < l; i += 3 ) {
  422. renderList.pushNormal( normals[ i ], normals[ i + 1 ], normals[ i + 2 ] );
  423. }
  424. }
  425. if ( attributes.color !== undefined ) {
  426. const colors = attributes.color.array;
  427. for ( let i = 0, l = colors.length; i < l; i += 3 ) {
  428. renderList.pushColor( colors[ i ], colors[ i + 1 ], colors[ i + 2 ] );
  429. }
  430. }
  431. if ( attributes.uv !== undefined ) {
  432. const uvs = attributes.uv.array;
  433. for ( let i = 0, l = uvs.length; i < l; i += 2 ) {
  434. renderList.pushUv( uvs[ i ], uvs[ i + 1 ] );
  435. }
  436. }
  437. if ( geometry.index !== null ) {
  438. const indices = geometry.index.array;
  439. if ( groups.length > 0 ) {
  440. for ( let g = 0; g < groups.length; g ++ ) {
  441. const group = groups[ g ];
  442. material = isMultiMaterial === true
  443. ? object.material[ group.materialIndex ]
  444. : object.material;
  445. if ( material === undefined ) continue;
  446. for ( let i = group.start, l = group.start + group.count; i < l; i += 3 ) {
  447. renderList.pushTriangle( indices[ i ], indices[ i + 1 ], indices[ i + 2 ], material );
  448. }
  449. }
  450. } else {
  451. for ( let i = 0, l = indices.length; i < l; i += 3 ) {
  452. renderList.pushTriangle( indices[ i ], indices[ i + 1 ], indices[ i + 2 ], material );
  453. }
  454. }
  455. } else {
  456. if ( groups.length > 0 ) {
  457. for ( let g = 0; g < groups.length; g ++ ) {
  458. const group = groups[ g ];
  459. material = isMultiMaterial === true
  460. ? object.material[ group.materialIndex ]
  461. : object.material;
  462. if ( material === undefined ) continue;
  463. for ( let i = group.start, l = group.start + group.count; i < l; i += 3 ) {
  464. renderList.pushTriangle( i, i + 1, i + 2, material );
  465. }
  466. }
  467. } else {
  468. for ( let i = 0, l = positions.length / 3; i < l; i += 3 ) {
  469. renderList.pushTriangle( i, i + 1, i + 2, material );
  470. }
  471. }
  472. }
  473. } else if ( object.isLine ) {
  474. _modelViewProjectionMatrix.multiplyMatrices( _viewProjectionMatrix, _modelMatrix );
  475. const attributes = geometry.attributes;
  476. if ( attributes.position !== undefined ) {
  477. const positions = attributes.position.array;
  478. for ( let i = 0, l = positions.length; i < l; i += 3 ) {
  479. renderList.pushVertex( positions[ i ], positions[ i + 1 ], positions[ i + 2 ] );
  480. }
  481. if ( attributes.color !== undefined ) {
  482. const colors = attributes.color.array;
  483. for ( let i = 0, l = colors.length; i < l; i += 3 ) {
  484. renderList.pushColor( colors[ i ], colors[ i + 1 ], colors[ i + 2 ] );
  485. }
  486. }
  487. if ( geometry.index !== null ) {
  488. const indices = geometry.index.array;
  489. for ( let i = 0, l = indices.length; i < l; i += 2 ) {
  490. renderList.pushLine( indices[ i ], indices[ i + 1 ] );
  491. }
  492. } else {
  493. const step = object.isLineSegments ? 2 : 1;
  494. for ( let i = 0, l = ( positions.length / 3 ) - 1; i < l; i += step ) {
  495. renderList.pushLine( i, i + 1 );
  496. }
  497. }
  498. }
  499. } else if ( object.isPoints ) {
  500. _modelViewProjectionMatrix.multiplyMatrices( _viewProjectionMatrix, _modelMatrix );
  501. const attributes = geometry.attributes;
  502. if ( attributes.position !== undefined ) {
  503. const positions = attributes.position.array;
  504. for ( let i = 0, l = positions.length; i < l; i += 3 ) {
  505. _vector4.set( positions[ i ], positions[ i + 1 ], positions[ i + 2 ], 1 );
  506. _vector4.applyMatrix4( _modelViewProjectionMatrix );
  507. pushPoint( _vector4, object, camera );
  508. }
  509. }
  510. } else if ( object.isSprite ) {
  511. object.modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, object.matrixWorld );
  512. _vector4.set( _modelMatrix.elements[ 12 ], _modelMatrix.elements[ 13 ], _modelMatrix.elements[ 14 ], 1 );
  513. _vector4.applyMatrix4( _viewProjectionMatrix );
  514. pushPoint( _vector4, object, camera );
  515. }
  516. }
  517. if ( sortElements === true ) {
  518. painterSortStable( _renderData.elements, 0, _renderData.elements.length );
  519. }
  520. return _renderData;
  521. };
  522. function pushPoint( _vector4, object, camera ) {
  523. const invW = 1 / _vector4.w;
  524. _vector4.z *= invW;
  525. if ( _vector4.z >= - 1 && _vector4.z <= 1 ) {
  526. _sprite = getNextSpriteInPool();
  527. _sprite.id = object.id;
  528. _sprite.x = _vector4.x * invW;
  529. _sprite.y = _vector4.y * invW;
  530. _sprite.z = _vector4.z;
  531. _sprite.renderOrder = object.renderOrder;
  532. _sprite.object = object;
  533. _sprite.rotation = object.rotation;
  534. _sprite.scale.x = object.scale.x * Math.abs( _sprite.x - ( _vector4.x + camera.projectionMatrix.elements[ 0 ] ) / ( _vector4.w + camera.projectionMatrix.elements[ 12 ] ) );
  535. _sprite.scale.y = object.scale.y * Math.abs( _sprite.y - ( _vector4.y + camera.projectionMatrix.elements[ 5 ] ) / ( _vector4.w + camera.projectionMatrix.elements[ 13 ] ) );
  536. _sprite.material = object.material;
  537. _renderData.elements.push( _sprite );
  538. }
  539. }
  540. // Pools
  541. function getNextObjectInPool() {
  542. if ( _objectCount === _objectPoolLength ) {
  543. const object = new RenderableObject();
  544. _objectPool.push( object );
  545. _objectPoolLength ++;
  546. _objectCount ++;
  547. return object;
  548. }
  549. return _objectPool[ _objectCount ++ ];
  550. }
  551. function getNextVertexInPool() {
  552. if ( _vertexCount === _vertexPoolLength ) {
  553. const vertex = new RenderableVertex();
  554. _vertexPool.push( vertex );
  555. _vertexPoolLength ++;
  556. _vertexCount ++;
  557. return vertex;
  558. }
  559. return _vertexPool[ _vertexCount ++ ];
  560. }
  561. function getNextFaceInPool() {
  562. if ( _faceCount === _facePoolLength ) {
  563. const face = new RenderableFace();
  564. _facePool.push( face );
  565. _facePoolLength ++;
  566. _faceCount ++;
  567. return face;
  568. }
  569. return _facePool[ _faceCount ++ ];
  570. }
  571. function getNextLineInPool() {
  572. if ( _lineCount === _linePoolLength ) {
  573. const line = new RenderableLine();
  574. _linePool.push( line );
  575. _linePoolLength ++;
  576. _lineCount ++;
  577. return line;
  578. }
  579. return _linePool[ _lineCount ++ ];
  580. }
  581. function getNextSpriteInPool() {
  582. if ( _spriteCount === _spritePoolLength ) {
  583. const sprite = new RenderableSprite();
  584. _spritePool.push( sprite );
  585. _spritePoolLength ++;
  586. _spriteCount ++;
  587. return sprite;
  588. }
  589. return _spritePool[ _spriteCount ++ ];
  590. }
  591. //
  592. function painterSort( a, b ) {
  593. if ( a.renderOrder !== b.renderOrder ) {
  594. return a.renderOrder - b.renderOrder;
  595. } else if ( a.z !== b.z ) {
  596. return b.z - a.z;
  597. } else if ( a.id !== b.id ) {
  598. return a.id - b.id;
  599. } else {
  600. return 0;
  601. }
  602. }
  603. function painterSortStable( array, start, length ) {
  604. // A stable insertion sort for sorting render items
  605. // This avoids the GC overhead of Array.prototype.sort()
  606. for ( let i = start + 1; i < start + length; i ++ ) {
  607. const item = array[ i ];
  608. let j = i - 1;
  609. while ( j >= start && painterSort( array[ j ], item ) > 0 ) {
  610. array[ j + 1 ] = array[ j ];
  611. j --;
  612. }
  613. array[ j + 1 ] = item;
  614. }
  615. }
  616. // Sutherland-Hodgman triangle clipping in homogeneous clip space
  617. // Returns count of vertices in clipped polygon (0 if completely clipped, 3+ if partially clipped)
  618. // Result vertices are in _clipInput array
  619. function clipTriangle( vertices ) {
  620. // Initialize input with the three input vertices
  621. _clipInput[ 0 ] = vertices[ 0 ];
  622. _clipInput[ 1 ] = vertices[ 1 ];
  623. _clipInput[ 2 ] = vertices[ 2 ];
  624. let inputCount = 3;
  625. let outputCount = 0;
  626. for ( let p = 0; p < _clipPlanes.length; p ++ ) {
  627. const plane = _clipPlanes[ p ];
  628. outputCount = 0;
  629. if ( inputCount === 0 ) break;
  630. for ( let i = 0; i < inputCount; i ++ ) {
  631. const v1 = _clipInput[ i ];
  632. const v2 = _clipInput[ ( i + 1 ) % inputCount ];
  633. const d1 = plane.sign * v1.z + v1.w;
  634. const d2 = plane.sign * v2.z + v2.w;
  635. const v1Inside = d1 >= 0;
  636. const v2Inside = d2 >= 0;
  637. if ( v1Inside && v2Inside ) {
  638. // Both inside - add v1
  639. _clipOutput[ outputCount ++ ] = v1;
  640. } else if ( v1Inside && ! v2Inside ) {
  641. // v1 inside, v2 outside - add v1 and intersection
  642. _clipOutput[ outputCount ++ ] = v1;
  643. const t = d1 / ( d1 - d2 );
  644. let intersection = _clipVertexPool[ outputCount ];
  645. if ( ! intersection ) {
  646. intersection = new Vector4();
  647. _clipVertexPool[ outputCount ] = intersection;
  648. }
  649. intersection.lerpVectors( v1, v2, t );
  650. _clipOutput[ outputCount ++ ] = intersection;
  651. } else if ( ! v1Inside && v2Inside ) {
  652. // v1 outside, v2 inside - add intersection only
  653. const t = d1 / ( d1 - d2 );
  654. let intersection = _clipVertexPool[ outputCount ];
  655. if ( ! intersection ) {
  656. intersection = new Vector4();
  657. _clipVertexPool[ outputCount ] = intersection;
  658. }
  659. intersection.lerpVectors( v1, v2, t );
  660. _clipOutput[ outputCount ++ ] = intersection;
  661. }
  662. // Both outside - add nothing
  663. }
  664. // Swap input/output
  665. const temp = _clipInput;
  666. _clipInput = _clipOutput;
  667. _clipOutput = temp;
  668. inputCount = outputCount;
  669. }
  670. return inputCount;
  671. }
  672. function clipLine( s1, s2 ) {
  673. let alpha1 = 0, alpha2 = 1;
  674. // Calculate the boundary coordinate of each vertex for the near and far clip planes,
  675. // Z = -1 and Z = +1, respectively.
  676. const bc1near = s1.z + s1.w,
  677. bc2near = s2.z + s2.w,
  678. bc1far = - s1.z + s1.w,
  679. bc2far = - s2.z + s2.w;
  680. if ( bc1near >= 0 && bc2near >= 0 && bc1far >= 0 && bc2far >= 0 ) {
  681. // Both vertices lie entirely within all clip planes.
  682. return true;
  683. } else if ( ( bc1near < 0 && bc2near < 0 ) || ( bc1far < 0 && bc2far < 0 ) ) {
  684. // Both vertices lie entirely outside one of the clip planes.
  685. return false;
  686. } else {
  687. // The line segment spans at least one clip plane.
  688. if ( bc1near < 0 ) {
  689. // v1 lies outside the near plane, v2 inside
  690. alpha1 = Math.max( alpha1, bc1near / ( bc1near - bc2near ) );
  691. } else if ( bc2near < 0 ) {
  692. // v2 lies outside the near plane, v1 inside
  693. alpha2 = Math.min( alpha2, bc1near / ( bc1near - bc2near ) );
  694. }
  695. if ( bc1far < 0 ) {
  696. // v1 lies outside the far plane, v2 inside
  697. alpha1 = Math.max( alpha1, bc1far / ( bc1far - bc2far ) );
  698. } else if ( bc2far < 0 ) {
  699. // v2 lies outside the far plane, v2 inside
  700. alpha2 = Math.min( alpha2, bc1far / ( bc1far - bc2far ) );
  701. }
  702. if ( alpha2 < alpha1 ) {
  703. // The line segment spans two boundaries, but is outside both of them.
  704. // (This can't happen when we're only clipping against just near/far but good
  705. // to leave the check here for future usage if other clip planes are added.)
  706. return false;
  707. } else {
  708. // Update the s1 and s2 vertices to match the clipped line segment.
  709. s1.lerp( s2, alpha1 );
  710. s2.lerp( s1, 1 - alpha2 );
  711. return true;
  712. }
  713. }
  714. }
  715. }
  716. }
  717. export { RenderableObject, RenderableFace, RenderableVertex, RenderableLine, RenderableSprite, Projector };
粤ICP备19079148号