UNPKG

angular-3d-viewer

Version:
370 lines (326 loc) 11.2 kB
import { RunTasksBatch } from '../core/taskrunner.js'; import { IsEqual } from '../geometry/geometry.js'; import { CreateObjectUrl, CreateObjectUrlWithMimeType } from '../io/bufferutils.js'; import { MaterialType } from '../model/material.js'; import { MeshInstance, MeshInstanceId } from '../model/meshinstance.js'; import { GetMeshType, MeshType } from '../model/meshutils.js'; import { ConvertColorToThreeColor, GetShadingType, ShadingType } from './threeutils.js'; import * as THREE from 'three'; export class ModelToThreeConversionParams { constructor () { this.forceMediumpForMaterials = false; } } export class ModelToThreeConversionOutput { constructor () { this.defaultMaterial = null; this.objectUrls = []; } } export class ThreeConversionStateHandler { constructor (callbacks) { this.callbacks = callbacks; this.texturesNeeded = 0; this.texturesLoaded = 0; this.threeObject = null; } OnTextureNeeded () { this.texturesNeeded += 1; } OnTextureLoaded () { this.texturesLoaded += 1; this.callbacks.onTextureLoaded (); this.Finish (); } OnModelLoaded (threeObject) { this.threeObject = threeObject; this.Finish (); } Finish () { if (this.threeObject !== null && this.texturesNeeded === this.texturesLoaded) { this.callbacks.onModelLoaded (this.threeObject); } } } export class ThreeNodeTree { constructor (model, threeRootNode) { this.model = model; this.threeNodeItems = []; this.AddNode (model.GetRootNode (), threeRootNode); } AddNode (node, threeNode) { let matrix = node.GetTransformation ().GetMatrix (); let threeMatrix = new THREE.Matrix4 ().fromArray (matrix.Get ()); threeNode.applyMatrix4 (threeMatrix); for (let childNode of node.GetChildNodes ()) { let threeChildNode = new THREE.Object3D (); threeNode.add (threeChildNode); this.AddNode (childNode, threeChildNode); } for (let meshIndex of node.GetMeshIndices ()) { let id = new MeshInstanceId (node.GetId (), meshIndex); let mesh = this.model.GetMesh (meshIndex); this.threeNodeItems.push ({ meshInstance : new MeshInstance (id, node, mesh), threeNode : threeNode }); } } GetNodeItems () { return this.threeNodeItems; } } export function ConvertModelToThreeObject (model, params, output, callbacks) { function CreateThreeMaterial (stateHandler, model, materialIndex, shadingType, params, output) { function SetTextureParameters (texture, threeTexture) { threeTexture.wrapS = THREE.RepeatWrapping; threeTexture.wrapT = THREE.RepeatWrapping; threeTexture.rotation = texture.rotation; threeTexture.offset.x = texture.offset.x; threeTexture.offset.y = texture.offset.y; threeTexture.repeat.x = texture.scale.x; threeTexture.repeat.y = texture.scale.y; } function LoadTexture (stateHandler, threeMaterial, texture, output, onTextureLoaded) { if (texture === null || !texture.IsValid ()) { return; } let loader = new THREE.TextureLoader (); stateHandler.OnTextureNeeded (); let textureObjectUrl = null; if (texture.mimeType !== null) { textureObjectUrl = CreateObjectUrlWithMimeType (texture.buffer, texture.mimeType); } else { textureObjectUrl = CreateObjectUrl (texture.buffer); } output.objectUrls.push (textureObjectUrl); loader.load (textureObjectUrl, (threeTexture) => { SetTextureParameters (texture, threeTexture); threeMaterial.needsUpdate = true; onTextureLoaded (threeTexture); stateHandler.OnTextureLoaded (); }, null, (err) => { stateHandler.OnTextureLoaded (); } ); } let material = model.GetMaterial (materialIndex); let baseColor = ConvertColorToThreeColor (material.color); if (material.vertexColors) { baseColor.setRGB (1.0, 1.0, 1.0); } let materialParams = { color : baseColor, vertexColors : material.vertexColors, opacity : material.opacity, transparent : material.transparent, alphaTest : material.alphaTest, side : THREE.DoubleSide }; if (params.forceMediumpForMaterials) { materialParams.precision = 'mediump'; } let threeMaterial = null; if (shadingType === ShadingType.Phong) { threeMaterial = new THREE.MeshPhongMaterial (materialParams); if (material.type === MaterialType.Phong) { let specularColor = ConvertColorToThreeColor (material.specular); if (IsEqual (material.shininess, 0.0)) { specularColor.setRGB (0.0, 0.0, 0.0); } threeMaterial.specular = specularColor; threeMaterial.shininess = material.shininess * 100.0; LoadTexture (stateHandler, threeMaterial, material.specularMap, output, (threeTexture) => { threeMaterial.specularMap = threeTexture; }); } } else if (shadingType === ShadingType.Physical) { threeMaterial = new THREE.MeshStandardMaterial (materialParams); if (material.type === MaterialType.Physical) { threeMaterial.metalness = material.metalness; threeMaterial.roughness = material.roughness; LoadTexture (stateHandler, threeMaterial, material.metalnessMap, output, (threeTexture) => { threeMaterial.metalness = 1.0; threeMaterial.roughness = 1.0; threeMaterial.metalnessMap = threeTexture; threeMaterial.roughnessMap = threeTexture; }); } } let emissiveColor = ConvertColorToThreeColor (material.emissive); threeMaterial.emissive = emissiveColor; LoadTexture (stateHandler, threeMaterial, material.diffuseMap, output, (threeTexture) => { if (!material.multiplyDiffuseMap) { threeMaterial.color.setRGB (1.0, 1.0, 1.0); } threeMaterial.map = threeTexture; }); LoadTexture (stateHandler, threeMaterial, material.bumpMap, output, (threeTexture) => { threeMaterial.bumpMap = threeTexture; }); LoadTexture (stateHandler, threeMaterial, material.normalMap, output, (threeTexture) => { threeMaterial.normalMap = threeTexture; }); LoadTexture (stateHandler, threeMaterial, material.emissiveMap, output, (threeTexture) => { threeMaterial.emissiveMap = threeTexture; }); if (material.isDefault) { output.defaultMaterial = threeMaterial; } return threeMaterial; } function CreateThreeMesh (meshInstance, modelThreeMaterials) { let mesh = meshInstance.mesh; let triangleCount = mesh.TriangleCount (); let triangleIndices = []; for (let i = 0; i < triangleCount; i++) { triangleIndices.push (i); } triangleIndices.sort ((a, b) => { let aTriangle = mesh.GetTriangle (a); let bTriangle = mesh.GetTriangle (b); return aTriangle.mat - bTriangle.mat; }); let threeGeometry = new THREE.BufferGeometry (); let meshThreeMaterials = []; let meshOriginalMaterials = []; let modelToThreeMaterials = new Map (); let vertices = []; let vertexColors = []; let normals = []; let uvs = []; let groups = []; groups.push ({ start : 0, end : -1 }); let meshHasVertexColors = (mesh.VertexColorCount () > 0); let meshHasUVs = (mesh.TextureUVCount () > 0); for (let i = 0; i < triangleIndices.length; i++) { let triangleIndex = triangleIndices[i]; let triangle = mesh.GetTriangle (triangleIndex); let v0 = mesh.GetVertex (triangle.v0); let v1 = mesh.GetVertex (triangle.v1); let v2 = mesh.GetVertex (triangle.v2); vertices.push (v0.x, v0.y, v0.z, v1.x, v1.y, v1.z, v2.x, v2.y, v2.z); if (triangle.HasVertexColors ()) { let vc0 = ConvertColorToThreeColor (mesh.GetVertexColor (triangle.c0)); let vc1 = ConvertColorToThreeColor (mesh.GetVertexColor (triangle.c1)); let vc2 = ConvertColorToThreeColor (mesh.GetVertexColor (triangle.c2)); vertexColors.push ( vc0.r, vc0.g, vc0.b, vc1.r, vc1.g, vc1.b, vc2.r, vc2.g, vc2.b ); } else if (meshHasVertexColors) { vertexColors.push ( 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 ); } let n0 = mesh.GetNormal (triangle.n0); let n1 = mesh.GetNormal (triangle.n1); let n2 = mesh.GetNormal (triangle.n2); normals.push (n0.x, n0.y, n0.z, n1.x, n1.y, n1.z, n2.x, n2.y, n2.z); if (triangle.HasTextureUVs ()) { let u0 = mesh.GetTextureUV (triangle.u0); let u1 = mesh.GetTextureUV (triangle.u1); let u2 = mesh.GetTextureUV (triangle.u2); uvs.push (u0.x, u0.y, u1.x, u1.y, u2.x, u2.y); } else if (meshHasUVs) { uvs.push (0.0, 0.0, 0.0, 0.0, 0.0, 0.0); } let modelMaterialIndex = triangle.mat; if (!modelToThreeMaterials.has (modelMaterialIndex)) { modelToThreeMaterials.set (modelMaterialIndex, meshThreeMaterials.length); meshThreeMaterials.push (modelThreeMaterials[modelMaterialIndex]); meshOriginalMaterials.push (modelMaterialIndex); if (i > 0) { groups[groups.length - 1].end = i - 1; groups.push ({ start : groups[groups.length - 1].end + 1, end : -1 }); } } } groups[groups.length - 1].end = triangleCount - 1; threeGeometry.setAttribute ('position', new THREE.Float32BufferAttribute (vertices, 3)); if (vertexColors.length !== 0) { threeGeometry.setAttribute ('color', new THREE.Float32BufferAttribute (vertexColors, 3)); } threeGeometry.setAttribute ('normal', new THREE.Float32BufferAttribute (normals, 3)); if (uvs.length !== 0) { threeGeometry.setAttribute ('uv', new THREE.Float32BufferAttribute (uvs, 2)); } for (let i = 0; i < groups.length; i++) { let group = groups[i]; threeGeometry.addGroup (group.start * 3, (group.end - group.start + 1) * 3, i); } let threeMesh = new THREE.Mesh (threeGeometry, meshThreeMaterials); threeMesh.name = mesh.GetName (); threeMesh.userData = { originalMeshInstance : meshInstance, originalMaterials : meshOriginalMaterials, threeMaterials : null }; return threeMesh; } function ConvertMesh (threeObject, meshInstance, modelThreeMaterials) { let type = GetMeshType (meshInstance.mesh); if (type === MeshType.TriangleMesh) { let threeMesh = CreateThreeMesh (meshInstance, modelThreeMaterials); threeObject.add (threeMesh); } } function ConvertNodeHierarchy (threeRootNode, model, modelThreeMaterials, stateHandler) { let nodeTree = new ThreeNodeTree (model, threeRootNode); let threeNodeItems = nodeTree.GetNodeItems (); RunTasksBatch (threeNodeItems.length, 100, { runTask : (firstMeshInstanceIndex, lastMeshInstanceIndex, onReady) => { for (let meshInstanceIndex = firstMeshInstanceIndex; meshInstanceIndex <= lastMeshInstanceIndex; meshInstanceIndex++) { let nodeItem = threeNodeItems[meshInstanceIndex]; ConvertMesh (nodeItem.threeNode, nodeItem.meshInstance, modelThreeMaterials); } onReady (); }, onReady : () => { stateHandler.OnModelLoaded (threeRootNode); } }); } let stateHandler = new ThreeConversionStateHandler (callbacks); let shadingType = GetShadingType (model); let modelThreeMaterials = []; for (let materialIndex = 0; materialIndex < model.MaterialCount (); materialIndex++) { let threeMaterial = CreateThreeMaterial (stateHandler, model, materialIndex, shadingType, params, output); modelThreeMaterials.push (threeMaterial); } let threeObject = new THREE.Object3D (); ConvertNodeHierarchy (threeObject, model, modelThreeMaterials, stateHandler); }