Projector.js 26 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154
  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. // Derive near/far clip distances from NDC z and stored clip-space w
  200. // (projectVertex already computed positionScreen = clipPos / w, with w preserved)
  201. const w1 = v1.positionScreen.w;
  202. const w2 = v2.positionScreen.w;
  203. const w3 = v3.positionScreen.w;
  204. const nearDist1 = w1 * ( v1.positionScreen.z + 1 );
  205. const nearDist2 = w2 * ( v2.positionScreen.z + 1 );
  206. const nearDist3 = w3 * ( v3.positionScreen.z + 1 );
  207. const farDist1 = w1 * ( 1 - v1.positionScreen.z );
  208. const farDist2 = w2 * ( 1 - v2.positionScreen.z );
  209. const farDist3 = w3 * ( 1 - v3.positionScreen.z );
  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 - reconstruct clip-space positions from NDC + w
  251. _clipPos1.set( v1.positionScreen.x * w1, v1.positionScreen.y * w1, v1.positionScreen.z * w1, w1 );
  252. _clipPos2.set( v2.positionScreen.x * w2, v2.positionScreen.y * w2, v2.positionScreen.z * w2, w2 );
  253. _clipPos3.set( v3.positionScreen.x * w3, v3.positionScreen.y * w3, v3.positionScreen.z * w3, w3 );
  254. _clipInputVertices[ 0 ] = _clipPos1;
  255. _clipInputVertices[ 1 ] = _clipPos2;
  256. _clipInputVertices[ 2 ] = _clipPos3;
  257. const clippedCount = clipTriangle( _clipInputVertices );
  258. if ( clippedCount < 3 ) return; // Triangle completely clipped
  259. // Perform perspective divide on clipped vertices and create screen vertices
  260. for ( let i = 0; i < clippedCount; i ++ ) {
  261. const cv = _clipInput[ i ];
  262. // Get or create renderable vertex from pool
  263. let sv = _screenVertexPool[ i ];
  264. if ( ! sv ) {
  265. sv = new RenderableVertex();
  266. _screenVertexPool[ i ] = sv;
  267. }
  268. // Perform perspective divide
  269. const invW = 1 / cv.w;
  270. sv.positionScreen.set( cv.x * invW, cv.y * invW, cv.z * invW, 1 );
  271. // Interpolate world position (simplified - using weighted average based on barycentric-like coords)
  272. // For a proper implementation, we'd need to track interpolation weights
  273. sv.positionWorld.copy( v1.positionWorld );
  274. sv.visible = true;
  275. }
  276. // Triangulate the clipped polygon (simple fan triangulation)
  277. for ( let i = 1; i < clippedCount - 1; i ++ ) {
  278. const tv1 = _screenVertexPool[ 0 ];
  279. const tv2 = _screenVertexPool[ i ];
  280. const tv3 = _screenVertexPool[ i + 1 ];
  281. if ( material.side === DoubleSide || checkBackfaceCulling( tv1, tv2, tv3 ) === true ) {
  282. _face = getNextFaceInPool();
  283. _face.id = object.id;
  284. _face.v1.copy( tv1 );
  285. _face.v2.copy( tv2 );
  286. _face.v3.copy( tv3 );
  287. _face.z = ( tv1.positionScreen.z + tv2.positionScreen.z + tv3.positionScreen.z ) / 3;
  288. _face.renderOrder = object.renderOrder;
  289. // face normal - use original triangle's normal
  290. _vector3.subVectors( v3.position, v2.position );
  291. _vector4.subVectors( v1.position, v2.position );
  292. _vector3.cross( _vector4 );
  293. _face.normalModel.copy( _vector3 );
  294. _face.normalModel.applyMatrix3( normalMatrix ).normalize();
  295. // Use original vertex normals and UVs (simplified - proper impl would interpolate)
  296. for ( let j = 0; j < 3; j ++ ) {
  297. const normal = _face.vertexNormalsModel[ j ];
  298. normal.fromArray( normals, arguments[ j ] * 3 );
  299. normal.applyMatrix3( normalMatrix ).normalize();
  300. const uv = _face.uvs[ j ];
  301. uv.fromArray( uvs, arguments[ j ] * 2 );
  302. }
  303. _face.vertexNormalsLength = 3;
  304. _face.material = material;
  305. if ( material.vertexColors ) {
  306. _face.color.fromArray( colors, a * 3 );
  307. }
  308. _renderData.elements.push( _face );
  309. }
  310. }
  311. }
  312. return {
  313. setObject: setObject,
  314. projectVertex: projectVertex,
  315. checkTriangleVisibility: checkTriangleVisibility,
  316. checkBackfaceCulling: checkBackfaceCulling,
  317. pushVertex: pushVertex,
  318. pushNormal: pushNormal,
  319. pushColor: pushColor,
  320. pushUv: pushUv,
  321. pushLine: pushLine,
  322. pushTriangle: pushTriangle
  323. };
  324. }
  325. const renderList = new RenderList();
  326. function projectObject( object ) {
  327. if ( object.visible === false ) return;
  328. if ( object.isLight ) {
  329. _renderData.lights.push( object );
  330. } else if ( object.isMesh || object.isLine || object.isPoints ) {
  331. if ( object.material.visible === false ) return;
  332. if ( object.frustumCulled === true && _frustum.intersectsObject( object ) === false ) return;
  333. addObject( object );
  334. } else if ( object.isSprite ) {
  335. if ( object.material.visible === false ) return;
  336. if ( object.frustumCulled === true && _frustum.intersectsSprite( object ) === false ) return;
  337. addObject( object );
  338. }
  339. const children = object.children;
  340. for ( let i = 0, l = children.length; i < l; i ++ ) {
  341. projectObject( children[ i ] );
  342. }
  343. }
  344. function addObject( object ) {
  345. _object = getNextObjectInPool();
  346. _object.id = object.id;
  347. _object.object = object;
  348. _vector3.setFromMatrixPosition( object.matrixWorld );
  349. _vector3.applyMatrix4( _viewProjectionMatrix );
  350. _object.z = _vector3.z;
  351. _object.renderOrder = object.renderOrder;
  352. _renderData.objects.push( _object );
  353. }
  354. /**
  355. * Projects the given scene in 3D space into a 2D representation. The result
  356. * is an object with renderable items.
  357. *
  358. * @param {Object3D} scene - A scene or any other type of 3D object.
  359. * @param {Camera} camera - The camera.
  360. * @param {boolean} sortObjects - Whether to sort objects or not.
  361. * @param {boolean} sortElements - Whether to sort elements (faces, lines and sprites) or not.
  362. * @return {{objects:Array<Objects>,lights:Array<Objects>,elements:Array<Objects>}} The projected scene as renderable objects.
  363. */
  364. this.projectScene = function ( scene, camera, sortObjects, sortElements ) {
  365. _faceCount = 0;
  366. _lineCount = 0;
  367. _spriteCount = 0;
  368. _renderData.elements.length = 0;
  369. if ( scene.matrixWorldAutoUpdate === true ) scene.updateMatrixWorld();
  370. if ( camera.parent === null && camera.matrixWorldAutoUpdate === true ) camera.updateMatrixWorld();
  371. _viewMatrix.copy( camera.matrixWorldInverse );
  372. _viewProjectionMatrix.multiplyMatrices( camera.projectionMatrix, _viewMatrix );
  373. _frustum.setFromProjectionMatrix( _viewProjectionMatrix );
  374. //
  375. _objectCount = 0;
  376. _renderData.objects.length = 0;
  377. _renderData.lights.length = 0;
  378. projectObject( scene );
  379. if ( sortObjects === true ) {
  380. _renderData.objects.sort( painterSort );
  381. }
  382. //
  383. const objects = _renderData.objects;
  384. for ( let o = 0, ol = objects.length; o < ol; o ++ ) {
  385. const object = objects[ o ].object;
  386. const geometry = object.geometry;
  387. renderList.setObject( object );
  388. _modelMatrix = object.matrixWorld;
  389. _vertexCount = 0;
  390. if ( object.isMesh ) {
  391. let material = object.material;
  392. const isMultiMaterial = Array.isArray( material );
  393. const attributes = geometry.attributes;
  394. const groups = geometry.groups;
  395. if ( attributes.position === undefined ) continue;
  396. const positions = attributes.position.array;
  397. for ( let i = 0, l = positions.length; i < l; i += 3 ) {
  398. let x = positions[ i ];
  399. let y = positions[ i + 1 ];
  400. let z = positions[ i + 2 ];
  401. const morphTargets = geometry.morphAttributes.position;
  402. if ( morphTargets !== undefined ) {
  403. const morphTargetsRelative = geometry.morphTargetsRelative;
  404. const morphInfluences = object.morphTargetInfluences;
  405. for ( let t = 0, tl = morphTargets.length; t < tl; t ++ ) {
  406. const influence = morphInfluences[ t ];
  407. if ( influence === 0 ) continue;
  408. const target = morphTargets[ t ];
  409. if ( morphTargetsRelative ) {
  410. x += target.getX( i / 3 ) * influence;
  411. y += target.getY( i / 3 ) * influence;
  412. z += target.getZ( i / 3 ) * influence;
  413. } else {
  414. x += ( target.getX( i / 3 ) - positions[ i ] ) * influence;
  415. y += ( target.getY( i / 3 ) - positions[ i + 1 ] ) * influence;
  416. z += ( target.getZ( i / 3 ) - positions[ i + 2 ] ) * influence;
  417. }
  418. }
  419. }
  420. renderList.pushVertex( x, y, z );
  421. }
  422. if ( attributes.normal !== undefined ) {
  423. const normals = attributes.normal.array;
  424. for ( let i = 0, l = normals.length; i < l; i += 3 ) {
  425. renderList.pushNormal( normals[ i ], normals[ i + 1 ], normals[ i + 2 ] );
  426. }
  427. }
  428. if ( attributes.color !== undefined ) {
  429. const colors = attributes.color.array;
  430. for ( let i = 0, l = colors.length; i < l; i += 3 ) {
  431. renderList.pushColor( colors[ i ], colors[ i + 1 ], colors[ i + 2 ] );
  432. }
  433. }
  434. if ( attributes.uv !== undefined ) {
  435. const uvs = attributes.uv.array;
  436. for ( let i = 0, l = uvs.length; i < l; i += 2 ) {
  437. renderList.pushUv( uvs[ i ], uvs[ i + 1 ] );
  438. }
  439. }
  440. if ( geometry.index !== null ) {
  441. const indices = geometry.index.array;
  442. if ( groups.length > 0 ) {
  443. for ( let g = 0; g < groups.length; g ++ ) {
  444. const group = groups[ g ];
  445. material = isMultiMaterial === true
  446. ? object.material[ group.materialIndex ]
  447. : object.material;
  448. if ( material === undefined ) continue;
  449. for ( let i = group.start, l = group.start + group.count; i < l; i += 3 ) {
  450. renderList.pushTriangle( indices[ i ], indices[ i + 1 ], indices[ i + 2 ], material );
  451. }
  452. }
  453. } else {
  454. for ( let i = 0, l = indices.length; i < l; i += 3 ) {
  455. renderList.pushTriangle( indices[ i ], indices[ i + 1 ], indices[ i + 2 ], material );
  456. }
  457. }
  458. } else {
  459. if ( groups.length > 0 ) {
  460. for ( let g = 0; g < groups.length; g ++ ) {
  461. const group = groups[ g ];
  462. material = isMultiMaterial === true
  463. ? object.material[ group.materialIndex ]
  464. : object.material;
  465. if ( material === undefined ) continue;
  466. for ( let i = group.start, l = group.start + group.count; i < l; i += 3 ) {
  467. renderList.pushTriangle( i, i + 1, i + 2, material );
  468. }
  469. }
  470. } else {
  471. for ( let i = 0, l = positions.length / 3; i < l; i += 3 ) {
  472. renderList.pushTriangle( i, i + 1, i + 2, material );
  473. }
  474. }
  475. }
  476. } else if ( object.isLine ) {
  477. _modelViewProjectionMatrix.multiplyMatrices( _viewProjectionMatrix, _modelMatrix );
  478. const attributes = geometry.attributes;
  479. if ( attributes.position !== undefined ) {
  480. const positions = attributes.position.array;
  481. for ( let i = 0, l = positions.length; i < l; i += 3 ) {
  482. renderList.pushVertex( positions[ i ], positions[ i + 1 ], positions[ i + 2 ] );
  483. }
  484. if ( attributes.color !== undefined ) {
  485. const colors = attributes.color.array;
  486. for ( let i = 0, l = colors.length; i < l; i += 3 ) {
  487. renderList.pushColor( colors[ i ], colors[ i + 1 ], colors[ i + 2 ] );
  488. }
  489. }
  490. if ( geometry.index !== null ) {
  491. const indices = geometry.index.array;
  492. for ( let i = 0, l = indices.length; i < l; i += 2 ) {
  493. renderList.pushLine( indices[ i ], indices[ i + 1 ] );
  494. }
  495. } else {
  496. const step = object.isLineSegments ? 2 : 1;
  497. for ( let i = 0, l = ( positions.length / 3 ) - 1; i < l; i += step ) {
  498. renderList.pushLine( i, i + 1 );
  499. }
  500. }
  501. }
  502. } else if ( object.isPoints ) {
  503. _modelViewProjectionMatrix.multiplyMatrices( _viewProjectionMatrix, _modelMatrix );
  504. const attributes = geometry.attributes;
  505. if ( attributes.position !== undefined ) {
  506. const positions = attributes.position.array;
  507. for ( let i = 0, l = positions.length; i < l; i += 3 ) {
  508. _vector4.set( positions[ i ], positions[ i + 1 ], positions[ i + 2 ], 1 );
  509. _vector4.applyMatrix4( _modelViewProjectionMatrix );
  510. pushPoint( _vector4, object, camera );
  511. }
  512. }
  513. } else if ( object.isSprite ) {
  514. object.modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, object.matrixWorld );
  515. _vector4.set( _modelMatrix.elements[ 12 ], _modelMatrix.elements[ 13 ], _modelMatrix.elements[ 14 ], 1 );
  516. _vector4.applyMatrix4( _viewProjectionMatrix );
  517. pushPoint( _vector4, object, camera );
  518. }
  519. }
  520. if ( sortElements === true ) {
  521. _renderData.elements.sort( painterSort );
  522. }
  523. return _renderData;
  524. };
  525. function pushPoint( _vector4, object, camera ) {
  526. const invW = 1 / _vector4.w;
  527. _vector4.z *= invW;
  528. if ( _vector4.z >= - 1 && _vector4.z <= 1 ) {
  529. _sprite = getNextSpriteInPool();
  530. _sprite.id = object.id;
  531. _sprite.x = _vector4.x * invW;
  532. _sprite.y = _vector4.y * invW;
  533. _sprite.z = _vector4.z;
  534. _sprite.renderOrder = object.renderOrder;
  535. _sprite.object = object;
  536. _sprite.rotation = object.rotation;
  537. _sprite.scale.x = object.scale.x * Math.abs( _sprite.x - ( _vector4.x + camera.projectionMatrix.elements[ 0 ] ) / ( _vector4.w + camera.projectionMatrix.elements[ 12 ] ) );
  538. _sprite.scale.y = object.scale.y * Math.abs( _sprite.y - ( _vector4.y + camera.projectionMatrix.elements[ 5 ] ) / ( _vector4.w + camera.projectionMatrix.elements[ 13 ] ) );
  539. _sprite.material = object.material;
  540. _renderData.elements.push( _sprite );
  541. }
  542. }
  543. // Pools
  544. function getNextObjectInPool() {
  545. if ( _objectCount === _objectPoolLength ) {
  546. const object = new RenderableObject();
  547. _objectPool.push( object );
  548. _objectPoolLength ++;
  549. _objectCount ++;
  550. return object;
  551. }
  552. return _objectPool[ _objectCount ++ ];
  553. }
  554. function getNextVertexInPool() {
  555. if ( _vertexCount === _vertexPoolLength ) {
  556. const vertex = new RenderableVertex();
  557. _vertexPool.push( vertex );
  558. _vertexPoolLength ++;
  559. _vertexCount ++;
  560. return vertex;
  561. }
  562. return _vertexPool[ _vertexCount ++ ];
  563. }
  564. function getNextFaceInPool() {
  565. if ( _faceCount === _facePoolLength ) {
  566. const face = new RenderableFace();
  567. _facePool.push( face );
  568. _facePoolLength ++;
  569. _faceCount ++;
  570. return face;
  571. }
  572. return _facePool[ _faceCount ++ ];
  573. }
  574. function getNextLineInPool() {
  575. if ( _lineCount === _linePoolLength ) {
  576. const line = new RenderableLine();
  577. _linePool.push( line );
  578. _linePoolLength ++;
  579. _lineCount ++;
  580. return line;
  581. }
  582. return _linePool[ _lineCount ++ ];
  583. }
  584. function getNextSpriteInPool() {
  585. if ( _spriteCount === _spritePoolLength ) {
  586. const sprite = new RenderableSprite();
  587. _spritePool.push( sprite );
  588. _spritePoolLength ++;
  589. _spriteCount ++;
  590. return sprite;
  591. }
  592. return _spritePool[ _spriteCount ++ ];
  593. }
  594. //
  595. function painterSort( a, b ) {
  596. if ( a.renderOrder !== b.renderOrder ) {
  597. return a.renderOrder - b.renderOrder;
  598. } else if ( a.z !== b.z ) {
  599. return b.z - a.z;
  600. } else if ( a.id !== b.id ) {
  601. return a.id - b.id;
  602. } else {
  603. return 0;
  604. }
  605. }
  606. // Sutherland-Hodgman triangle clipping in homogeneous clip space
  607. // Returns count of vertices in clipped polygon (0 if completely clipped, 3+ if partially clipped)
  608. // Result vertices are in _clipInput array
  609. function clipTriangle( vertices ) {
  610. // Initialize input with the three input vertices
  611. _clipInput[ 0 ] = vertices[ 0 ];
  612. _clipInput[ 1 ] = vertices[ 1 ];
  613. _clipInput[ 2 ] = vertices[ 2 ];
  614. let inputCount = 3;
  615. let outputCount = 0;
  616. for ( let p = 0; p < _clipPlanes.length; p ++ ) {
  617. const plane = _clipPlanes[ p ];
  618. outputCount = 0;
  619. if ( inputCount === 0 ) break;
  620. for ( let i = 0; i < inputCount; i ++ ) {
  621. const v1 = _clipInput[ i ];
  622. const v2 = _clipInput[ ( i + 1 ) % inputCount ];
  623. const d1 = plane.sign * v1.z + v1.w;
  624. const d2 = plane.sign * v2.z + v2.w;
  625. const v1Inside = d1 >= 0;
  626. const v2Inside = d2 >= 0;
  627. if ( v1Inside && v2Inside ) {
  628. // Both inside - add v1
  629. _clipOutput[ outputCount ++ ] = v1;
  630. } else if ( v1Inside && ! v2Inside ) {
  631. // v1 inside, v2 outside - add v1 and intersection
  632. _clipOutput[ outputCount ++ ] = v1;
  633. const t = d1 / ( d1 - d2 );
  634. let intersection = _clipVertexPool[ outputCount ];
  635. if ( ! intersection ) {
  636. intersection = new Vector4();
  637. _clipVertexPool[ outputCount ] = intersection;
  638. }
  639. intersection.lerpVectors( v1, v2, t );
  640. _clipOutput[ outputCount ++ ] = intersection;
  641. } else if ( ! v1Inside && v2Inside ) {
  642. // v1 outside, v2 inside - add intersection only
  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. }
  652. // Both outside - add nothing
  653. }
  654. // Swap input/output
  655. const temp = _clipInput;
  656. _clipInput = _clipOutput;
  657. _clipOutput = temp;
  658. inputCount = outputCount;
  659. }
  660. return inputCount;
  661. }
  662. function clipLine( s1, s2 ) {
  663. let alpha1 = 0, alpha2 = 1;
  664. // Calculate the boundary coordinate of each vertex for the near and far clip planes,
  665. // Z = -1 and Z = +1, respectively.
  666. const bc1near = s1.z + s1.w,
  667. bc2near = s2.z + s2.w,
  668. bc1far = - s1.z + s1.w,
  669. bc2far = - s2.z + s2.w;
  670. if ( bc1near >= 0 && bc2near >= 0 && bc1far >= 0 && bc2far >= 0 ) {
  671. // Both vertices lie entirely within all clip planes.
  672. return true;
  673. } else if ( ( bc1near < 0 && bc2near < 0 ) || ( bc1far < 0 && bc2far < 0 ) ) {
  674. // Both vertices lie entirely outside one of the clip planes.
  675. return false;
  676. } else {
  677. // The line segment spans at least one clip plane.
  678. if ( bc1near < 0 ) {
  679. // v1 lies outside the near plane, v2 inside
  680. alpha1 = Math.max( alpha1, bc1near / ( bc1near - bc2near ) );
  681. } else if ( bc2near < 0 ) {
  682. // v2 lies outside the near plane, v1 inside
  683. alpha2 = Math.min( alpha2, bc1near / ( bc1near - bc2near ) );
  684. }
  685. if ( bc1far < 0 ) {
  686. // v1 lies outside the far plane, v2 inside
  687. alpha1 = Math.max( alpha1, bc1far / ( bc1far - bc2far ) );
  688. } else if ( bc2far < 0 ) {
  689. // v2 lies outside the far plane, v2 inside
  690. alpha2 = Math.min( alpha2, bc1far / ( bc1far - bc2far ) );
  691. }
  692. if ( alpha2 < alpha1 ) {
  693. // The line segment spans two boundaries, but is outside both of them.
  694. // (This can't happen when we're only clipping against just near/far but good
  695. // to leave the check here for future usage if other clip planes are added.)
  696. return false;
  697. } else {
  698. // Update the s1 and s2 vertices to match the clipped line segment.
  699. s1.lerp( s2, alpha1 );
  700. s2.lerp( s1, 1 - alpha2 );
  701. return true;
  702. }
  703. }
  704. }
  705. }
  706. }
  707. export { RenderableObject, RenderableFace, RenderableVertex, RenderableLine, RenderableSprite, Projector };
粤ICP备19079148号