123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202 |
- import {
- BufferAttribute,
- BufferGeometry,
- Group,
- LineSegments,
- Matrix3,
- Mesh
- } from 'three';
- import { mergeBufferGeometries } from './BufferGeometryUtils.js';
- class LDrawUtils {
- static mergeObject( object ) {
- // Merges geometries in object by materials and returns new object. Use on not indexed geometries.
- // The object buffers reference the old object ones.
- // Special treatment is done to the conditional lines generated by LDrawLoader.
- function extractGroup( geometry, group, elementSize, isConditionalLine ) {
- // Extracts a group from a geometry as a new geometry (with attribute buffers referencing original buffers)
- const newGeometry = new BufferGeometry();
- const originalPositions = geometry.getAttribute( 'position' ).array;
- const originalNormals = elementSize === 3 ? geometry.getAttribute( 'normal' ).array : null;
- const numVertsGroup = Math.min( group.count, Math.floor( originalPositions.length / 3 ) - group.start );
- const vertStart = group.start * 3;
- const vertEnd = ( group.start + numVertsGroup ) * 3;
- const positions = originalPositions.subarray( vertStart, vertEnd );
- const normals = originalNormals !== null ? originalNormals.subarray( vertStart, vertEnd ) : null;
- newGeometry.setAttribute( 'position', new BufferAttribute( positions, 3 ) );
- if ( normals !== null ) newGeometry.setAttribute( 'normal', new BufferAttribute( normals, 3 ) );
- if ( isConditionalLine ) {
- const controlArray0 = geometry.getAttribute( 'control0' ).array.subarray( vertStart, vertEnd );
- const controlArray1 = geometry.getAttribute( 'control1' ).array.subarray( vertStart, vertEnd );
- const directionArray = geometry.getAttribute( 'direction' ).array.subarray( vertStart, vertEnd );
- newGeometry.setAttribute( 'control0', new BufferAttribute( controlArray0, 3, false ) );
- newGeometry.setAttribute( 'control1', new BufferAttribute( controlArray1, 3, false ) );
- newGeometry.setAttribute( 'direction', new BufferAttribute( directionArray, 3, false ) );
- }
- return newGeometry;
- }
- function addGeometry( mat, geometry, geometries ) {
- const geoms = geometries[ mat.uuid ];
- if ( ! geoms ) {
- geometries[ mat.uuid ] = {
- mat: mat,
- arr: [ geometry ]
- };
- } else {
- geoms.arr.push( geometry );
- }
- }
- function permuteAttribute( attribute, elemSize ) {
- // Permutes first two vertices of each attribute element
- if ( ! attribute ) return;
- const verts = attribute.array;
- const numVerts = Math.floor( verts.length / 3 );
- let offset = 0;
- for ( let i = 0; i < numVerts; i ++ ) {
- const x = verts[ offset ];
- const y = verts[ offset + 1 ];
- const z = verts[ offset + 2 ];
- verts[ offset ] = verts[ offset + 3 ];
- verts[ offset + 1 ] = verts[ offset + 4 ];
- verts[ offset + 2 ] = verts[ offset + 5 ];
- verts[ offset + 3 ] = x;
- verts[ offset + 4 ] = y;
- verts[ offset + 5 ] = z;
- offset += elemSize * 3;
- }
- }
- // Traverse the object hierarchy collecting geometries and transforming them to world space
- const meshGeometries = {};
- const linesGeometries = {};
- const condLinesGeometries = {};
- object.updateMatrixWorld( true );
- const normalMatrix = new Matrix3();
- object.traverse( c => {
- if ( c.isMesh | c.isLineSegments ) {
- const elemSize = c.isMesh ? 3 : 2;
- const geometry = c.geometry.clone();
- const matrixIsInverted = c.matrixWorld.determinant() < 0;
- if ( matrixIsInverted ) {
- permuteAttribute( geometry.attributes.position, elemSize );
- permuteAttribute( geometry.attributes.normal, elemSize );
- }
- geometry.applyMatrix4( c.matrixWorld );
- if ( c.isConditionalLine ) {
- geometry.attributes.control0.applyMatrix4( c.matrixWorld );
- geometry.attributes.control1.applyMatrix4( c.matrixWorld );
- normalMatrix.getNormalMatrix( c.matrixWorld );
- geometry.attributes.direction.applyNormalMatrix( normalMatrix );
- }
- const geometries = c.isMesh ? meshGeometries : ( c.isConditionalLine ? condLinesGeometries : linesGeometries );
- if ( Array.isArray( c.material ) ) {
- for ( const groupIndex in geometry.groups ) {
- const group = geometry.groups[ groupIndex ];
- const mat = c.material[ group.materialIndex ];
- const newGeometry = extractGroup( geometry, group, elemSize, c.isConditionalLine );
- addGeometry( mat, newGeometry, geometries );
- }
- } else {
- addGeometry( c.material, geometry, geometries );
- }
- }
- } );
- // Create object with merged geometries
- const mergedObject = new Group();
- const meshMaterialsIds = Object.keys( meshGeometries );
- for ( const meshMaterialsId of meshMaterialsIds ) {
- const meshGeometry = meshGeometries[ meshMaterialsId ];
- const mergedGeometry = mergeBufferGeometries( meshGeometry.arr );
- mergedObject.add( new Mesh( mergedGeometry, meshGeometry.mat ) );
- }
- const linesMaterialsIds = Object.keys( linesGeometries );
- for ( const linesMaterialsId of linesMaterialsIds ) {
- const lineGeometry = linesGeometries[ linesMaterialsId ];
- const mergedGeometry = mergeBufferGeometries( lineGeometry.arr );
- mergedObject.add( new LineSegments( mergedGeometry, lineGeometry.mat ) );
- }
- const condLinesMaterialsIds = Object.keys( condLinesGeometries );
- for ( const condLinesMaterialsId of condLinesMaterialsIds ) {
- const condLineGeometry = condLinesGeometries[ condLinesMaterialsId ];
- const mergedGeometry = mergeBufferGeometries( condLineGeometry.arr );
- const condLines = new LineSegments( mergedGeometry, condLineGeometry.mat );
- condLines.isConditionalLine = true;
- mergedObject.add( condLines );
- }
- mergedObject.userData.constructionStep = 0;
- mergedObject.userData.numConstructionSteps = 1;
- return mergedObject;
- }
- }
- export { LDrawUtils };
|