import { Color, Matrix3, Vector2, Vector3 } from '../lib/three.module.js' class OBJExporter { parse(object) { let output = '' let indexVertex = 0 let indexVertexUvs = 0 let indexNormals = 0 const vertex = new Vector3() const color = new Color() const normal = new Vector3() const uv = new Vector2() const face = [] function parseMesh(mesh) { let nbVertex = 0 let nbNormals = 0 let nbVertexUvs = 0 const geometry = mesh.geometry const normalMatrixWorld = new Matrix3() if (geometry.isBufferGeometry !== true) { throw new Error('THREE.OBJExporter: Geometry is not of type THREE.BufferGeometry.') } // shortcuts const vertices = geometry.getAttribute('position') const normals = geometry.getAttribute('normal') const uvs = geometry.getAttribute('uv') const indices = geometry.getIndex() // name of the mesh object output += 'o ' + mesh.name + '\n' // name of the mesh material if (mesh.material && mesh.material.name) { output += 'usemtl ' + mesh.material.name + '\n' } // vertices if (vertices !== undefined) { for (let i = 0, l = vertices.count; i < l; i++, nbVertex++) { vertex.x = vertices.getX(i) vertex.y = vertices.getY(i) vertex.z = vertices.getZ(i) // transform the vertex to world space vertex.applyMatrix4(mesh.matrixWorld) // transform the vertex to export format output += 'v ' + vertex.x + ' ' + vertex.y + ' ' + vertex.z + '\n' } } // uvs if (uvs !== undefined) { for (let i = 0, l = uvs.count; i < l; i++, nbVertexUvs++) { uv.x = uvs.getX(i) uv.y = uvs.getY(i) // transform the uv to export format output += 'vt ' + uv.x + ' ' + uv.y + '\n' } } // normals if (normals !== undefined) { normalMatrixWorld.getNormalMatrix(mesh.matrixWorld) for (let i = 0, l = normals.count; i < l; i++, nbNormals++) { normal.x = normals.getX(i) normal.y = normals.getY(i) normal.z = normals.getZ(i) // transform the normal to world space normal.applyMatrix3(normalMatrixWorld).normalize() // transform the normal to export format output += 'vn ' + normal.x + ' ' + normal.y + ' ' + normal.z + '\n' } } // faces if (indices !== null) { for (let i = 0, l = indices.count; i < l; i += 3) { for (let m = 0; m < 3; m++) { const j = indices.getX(i + m) + 1 face[m] = indexVertex + j + (normals || uvs ? '/' + (uvs ? indexVertexUvs + j : '') + (normals ? '/' + (indexNormals + j) : '') : '') } // transform the face to export format output += 'f ' + face.join(' ') + '\n' } } else { for (let i = 0, l = vertices.count; i < l; i += 3) { for (let m = 0; m < 3; m++) { const j = i + m + 1 face[m] = indexVertex + j + (normals || uvs ? '/' + (uvs ? indexVertexUvs + j : '') + (normals ? '/' + (indexNormals + j) : '') : '') } // transform the face to export format output += 'f ' + face.join(' ') + '\n' } } // update index indexVertex += nbVertex indexVertexUvs += nbVertexUvs indexNormals += nbNormals } function parseLine(line) { let nbVertex = 0 const geometry = line.geometry const type = line.type if (geometry.isBufferGeometry !== true) { throw new Error('THREE.OBJExporter: Geometry is not of type THREE.BufferGeometry.') } // shortcuts const vertices = geometry.getAttribute('position') // name of the line object output += 'o ' + line.name + '\n' if (vertices !== undefined) { for (let i = 0, l = vertices.count; i < l; i++, nbVertex++) { vertex.x = vertices.getX(i) vertex.y = vertices.getY(i) vertex.z = vertices.getZ(i) // transform the vertex to world space vertex.applyMatrix4(line.matrixWorld) // transform the vertex to export format output += 'v ' + vertex.x + ' ' + vertex.y + ' ' + vertex.z + '\n' } } if (type === 'Line') { output += 'l ' for (let j = 1, l = vertices.count; j <= l; j++) { output += indexVertex + j + ' ' } output += '\n' } if (type === 'LineSegments') { for (let j = 1, k = j + 1, l = vertices.count; j < l; j += 2, k = j + 1) { output += 'l ' + (indexVertex + j) + ' ' + (indexVertex + k) + '\n' } } // update index indexVertex += nbVertex } function parsePoints(points) { let nbVertex = 0 const geometry = points.geometry if (geometry.isBufferGeometry !== true) { throw new Error('THREE.OBJExporter: Geometry is not of type THREE.BufferGeometry.') } const vertices = geometry.getAttribute('position') const colors = geometry.getAttribute('color') output += 'o ' + points.name + '\n' if (vertices !== undefined) { for (let i = 0, l = vertices.count; i < l; i++, nbVertex++) { vertex.fromBufferAttribute(vertices, i) vertex.applyMatrix4(points.matrixWorld) output += 'v ' + vertex.x + ' ' + vertex.y + ' ' + vertex.z if (colors !== undefined) { color.fromBufferAttribute(colors, i) output += ' ' + color.r + ' ' + color.g + ' ' + color.b } output += '\n' } } output += 'p ' for (let j = 1, l = vertices.count; j <= l; j++) { output += indexVertex + j + ' ' } output += '\n' // update index indexVertex += nbVertex } object.traverse(function(child) { if (child.visible) { if (child.isMesh === true) { parseMesh(child) } if (child.isLine === true) { parseLine(child) } if (child.isPoints === true) { parsePoints(child) } } }) return output } } export { OBJExporter }