LDrawUtils.js 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. ( function () {
  2. class LDrawUtils {
  3. static mergeObject( object ) {
  4. // Merges geometries in object by materials and returns new object. Use on not indexed geometries.
  5. // The object buffers reference the old object ones.
  6. // Special treatment is done to the conditional lines generated by LDrawLoader.
  7. function extractGroup( geometry, group, elementSize, isConditionalLine ) {
  8. // Extracts a group from a geometry as a new geometry (with attribute buffers referencing original buffers)
  9. const newGeometry = new THREE.BufferGeometry();
  10. const originalPositions = geometry.getAttribute( 'position' ).array;
  11. const originalNormals = elementSize === 3 ? geometry.getAttribute( 'normal' ).array : null;
  12. const numVertsGroup = Math.min( group.count, Math.floor( originalPositions.length / 3 ) - group.start );
  13. const vertStart = group.start * 3;
  14. const vertEnd = ( group.start + numVertsGroup ) * 3;
  15. const positions = originalPositions.subarray( vertStart, vertEnd );
  16. const normals = originalNormals !== null ? originalNormals.subarray( vertStart, vertEnd ) : null;
  17. newGeometry.setAttribute( 'position', new THREE.BufferAttribute( positions, 3 ) );
  18. if ( normals !== null ) newGeometry.setAttribute( 'normal', new THREE.BufferAttribute( normals, 3 ) );
  19. if ( isConditionalLine ) {
  20. const controlArray0 = geometry.getAttribute( 'control0' ).array.subarray( vertStart, vertEnd );
  21. const controlArray1 = geometry.getAttribute( 'control1' ).array.subarray( vertStart, vertEnd );
  22. const directionArray = geometry.getAttribute( 'direction' ).array.subarray( vertStart, vertEnd );
  23. newGeometry.setAttribute( 'control0', new THREE.BufferAttribute( controlArray0, 3, false ) );
  24. newGeometry.setAttribute( 'control1', new THREE.BufferAttribute( controlArray1, 3, false ) );
  25. newGeometry.setAttribute( 'direction', new THREE.BufferAttribute( directionArray, 3, false ) );
  26. }
  27. return newGeometry;
  28. }
  29. function addGeometry( mat, geometry, geometries ) {
  30. const geoms = geometries[ mat.uuid ];
  31. if ( ! geoms ) {
  32. geometries[ mat.uuid ] = {
  33. mat: mat,
  34. arr: [ geometry ]
  35. };
  36. } else {
  37. geoms.arr.push( geometry );
  38. }
  39. }
  40. function permuteAttribute( attribute, elemSize ) {
  41. // Permutes first two vertices of each attribute element
  42. if ( ! attribute ) return;
  43. const verts = attribute.array;
  44. const numVerts = Math.floor( verts.length / 3 );
  45. let offset = 0;
  46. for ( let i = 0; i < numVerts; i ++ ) {
  47. const x = verts[ offset ];
  48. const y = verts[ offset + 1 ];
  49. const z = verts[ offset + 2 ];
  50. verts[ offset ] = verts[ offset + 3 ];
  51. verts[ offset + 1 ] = verts[ offset + 4 ];
  52. verts[ offset + 2 ] = verts[ offset + 5 ];
  53. verts[ offset + 3 ] = x;
  54. verts[ offset + 4 ] = y;
  55. verts[ offset + 5 ] = z;
  56. offset += elemSize * 3;
  57. }
  58. }
  59. // Traverse the object hierarchy collecting geometries and transforming them to world space
  60. const meshGeometries = {};
  61. const linesGeometries = {};
  62. const condLinesGeometries = {};
  63. object.updateMatrixWorld( true );
  64. const normalMatrix = new THREE.Matrix3();
  65. object.traverse( c => {
  66. if ( c.isMesh | c.isLineSegments ) {
  67. const elemSize = c.isMesh ? 3 : 2;
  68. const geometry = c.geometry.clone();
  69. const matrixIsInverted = c.matrixWorld.determinant() < 0;
  70. if ( matrixIsInverted ) {
  71. permuteAttribute( geometry.attributes.position, elemSize );
  72. permuteAttribute( geometry.attributes.normal, elemSize );
  73. }
  74. geometry.applyMatrix4( c.matrixWorld );
  75. if ( c.isConditionalLine ) {
  76. geometry.attributes.control0.applyMatrix4( c.matrixWorld );
  77. geometry.attributes.control1.applyMatrix4( c.matrixWorld );
  78. normalMatrix.getNormalMatrix( c.matrixWorld );
  79. geometry.attributes.direction.applyNormalMatrix( normalMatrix );
  80. }
  81. const geometries = c.isMesh ? meshGeometries : c.isConditionalLine ? condLinesGeometries : linesGeometries;
  82. if ( Array.isArray( c.material ) ) {
  83. for ( const groupIndex in geometry.groups ) {
  84. const group = geometry.groups[ groupIndex ];
  85. const mat = c.material[ group.materialIndex ];
  86. const newGeometry = extractGroup( geometry, group, elemSize, c.isConditionalLine );
  87. addGeometry( mat, newGeometry, geometries );
  88. }
  89. } else {
  90. addGeometry( c.material, geometry, geometries );
  91. }
  92. }
  93. } );
  94. // Create object with merged geometries
  95. const mergedObject = new THREE.Group();
  96. const meshMaterialsIds = Object.keys( meshGeometries );
  97. for ( const meshMaterialsId of meshMaterialsIds ) {
  98. const meshGeometry = meshGeometries[ meshMaterialsId ];
  99. const mergedGeometry = THREE.mergeBufferGeometries( meshGeometry.arr );
  100. mergedObject.add( new THREE.Mesh( mergedGeometry, meshGeometry.mat ) );
  101. }
  102. const linesMaterialsIds = Object.keys( linesGeometries );
  103. for ( const linesMaterialsId of linesMaterialsIds ) {
  104. const lineGeometry = linesGeometries[ linesMaterialsId ];
  105. const mergedGeometry = THREE.mergeBufferGeometries( lineGeometry.arr );
  106. mergedObject.add( new THREE.LineSegments( mergedGeometry, lineGeometry.mat ) );
  107. }
  108. const condLinesMaterialsIds = Object.keys( condLinesGeometries );
  109. for ( const condLinesMaterialsId of condLinesMaterialsIds ) {
  110. const condLineGeometry = condLinesGeometries[ condLinesMaterialsId ];
  111. const mergedGeometry = THREE.mergeBufferGeometries( condLineGeometry.arr );
  112. const condLines = new THREE.LineSegments( mergedGeometry, condLineGeometry.mat );
  113. condLines.isConditionalLine = true;
  114. mergedObject.add( condLines );
  115. }
  116. mergedObject.userData.constructionStep = 0;
  117. mergedObject.userData.numConstructionSteps = 1;
  118. return mergedObject;
  119. }
  120. }
  121. THREE.LDrawUtils = {};
  122. THREE.LDrawUtils.LDrawUtils = LDrawUtils;
  123. } )();