UNPKG

playcanvas

Version:

PlayCanvas WebGL game engine

322 lines (319 loc) 13.1 kB
import { Mat4 } from '../../core/math/mat4.js'; import { Vec3 } from '../../core/math/vec3.js'; import { BoundingBox } from '../../core/shape/bounding-box.js'; import { TYPE_FLOAT32, TYPE_UINT32, TYPE_INT32, TYPE_UINT16, TYPE_INT16, TYPE_UINT8, TYPE_INT8, SEMANTIC_TEXCOORD7, SEMANTIC_TEXCOORD6, SEMANTIC_TEXCOORD5, SEMANTIC_TEXCOORD4, SEMANTIC_TEXCOORD3, SEMANTIC_TEXCOORD2, SEMANTIC_TEXCOORD1, SEMANTIC_TEXCOORD0, SEMANTIC_COLOR, SEMANTIC_BLENDINDICES, SEMANTIC_BLENDWEIGHT, SEMANTIC_TANGENT, SEMANTIC_NORMAL, SEMANTIC_POSITION, INDEXFORMAT_UINT32, INDEXFORMAT_UINT16, PRIMITIVE_TRIFAN, PRIMITIVE_TRISTRIP, PRIMITIVE_TRIANGLES, PRIMITIVE_LINESTRIP, PRIMITIVE_LINELOOP, PRIMITIVE_LINES, PRIMITIVE_POINTS } from '../../platform/graphics/constants.js'; import { IndexBuffer } from '../../platform/graphics/index-buffer.js'; import { VertexBuffer } from '../../platform/graphics/vertex-buffer.js'; import { VertexFormat } from '../../platform/graphics/vertex-format.js'; import { VertexIterator } from '../../platform/graphics/vertex-iterator.js'; import { GraphNode } from '../../scene/graph-node.js'; import { Mesh } from '../../scene/mesh.js'; import { MeshInstance } from '../../scene/mesh-instance.js'; import { Model } from '../../scene/model.js'; import { Morph } from '../../scene/morph.js'; import { MorphInstance } from '../../scene/morph-instance.js'; import { MorphTarget } from '../../scene/morph-target.js'; import { Skin } from '../../scene/skin.js'; import { SkinInstance } from '../../scene/skin-instance.js'; const JSON_PRIMITIVE_TYPE = { 'points': PRIMITIVE_POINTS, 'lines': PRIMITIVE_LINES, 'lineloop': PRIMITIVE_LINELOOP, 'linestrip': PRIMITIVE_LINESTRIP, 'triangles': PRIMITIVE_TRIANGLES, 'trianglestrip': PRIMITIVE_TRISTRIP, 'trianglefan': PRIMITIVE_TRIFAN }; const JSON_VERTEX_ELEMENT_TYPE = { 'int8': TYPE_INT8, 'uint8': TYPE_UINT8, 'int16': TYPE_INT16, 'uint16': TYPE_UINT16, 'int32': TYPE_INT32, 'uint32': TYPE_UINT32, 'float32': TYPE_FLOAT32 }; class JsonModelParser { constructor(modelHandler){ this._device = modelHandler.device; this._defaultMaterial = modelHandler.defaultMaterial; } parse(data, callback) { const modelData = data.model; if (!modelData) { callback(null, null); return; } if (modelData.version <= 1) { callback('JsonModelParser#parse: Trying to parse unsupported model format.'); return; } const nodes = this._parseNodes(data); const skins = this._parseSkins(data, nodes); const vertexBuffers = this._parseVertexBuffers(data); const indices = this._parseIndexBuffers(data, vertexBuffers); const morphs = this._parseMorphs(data, nodes, vertexBuffers); const meshes = this._parseMeshes(data, skins.skins, morphs.morphs, vertexBuffers, indices.buffer, indices.data); const meshInstances = this._parseMeshInstances(data, nodes, meshes, skins.skins, skins.instances, morphs.morphs, morphs.instances); const model = new Model(); model.graph = nodes[0]; model.meshInstances = meshInstances; model.skinInstances = skins.instances; model.morphInstances = morphs.instances; model.getGraph().syncHierarchy(); callback(null, model); } _parseNodes(data) { const modelData = data.model; const nodes = []; let i; for(i = 0; i < modelData.nodes.length; i++){ const nodeData = modelData.nodes[i]; const node = new GraphNode(nodeData.name); node.setLocalPosition(nodeData.position[0], nodeData.position[1], nodeData.position[2]); node.setLocalEulerAngles(nodeData.rotation[0], nodeData.rotation[1], nodeData.rotation[2]); node.setLocalScale(nodeData.scale[0], nodeData.scale[1], nodeData.scale[2]); node.scaleCompensation = !!nodeData.scaleCompensation; nodes.push(node); } for(i = 1; i < modelData.parents.length; i++){ nodes[modelData.parents[i]].addChild(nodes[i]); } return nodes; } _parseSkins(data, nodes) { const modelData = data.model; const skins = []; const skinInstances = []; let i, j; for(i = 0; i < modelData.skins.length; i++){ const skinData = modelData.skins[i]; const inverseBindMatrices = []; for(j = 0; j < skinData.inverseBindMatrices.length; j++){ const ibm = skinData.inverseBindMatrices[j]; inverseBindMatrices[j] = new Mat4().set(ibm); } const skin = new Skin(this._device, inverseBindMatrices, skinData.boneNames); skins.push(skin); const skinInstance = new SkinInstance(skin); const bones = []; for(j = 0; j < skin.boneNames.length; j++){ const boneName = skin.boneNames[j]; const bone = nodes[0].findByName(boneName); bones.push(bone); } skinInstance.bones = bones; skinInstances.push(skinInstance); } return { skins: skins, instances: skinInstances }; } _getMorphVertexCount(modelData, morphIndex, vertexBuffers) { for(let i = 0; i < modelData.meshes.length; i++){ const meshData = modelData.meshes[i]; if (meshData.morph === morphIndex) { const vertexBuffer = vertexBuffers[meshData.vertices]; return vertexBuffer.numVertices; } } return undefined; } _parseMorphs(data, nodes, vertexBuffers) { const modelData = data.model; const morphs = []; const morphInstances = []; let i, j, vertexCount; let targets, morphTarget, morphTargetArray; if (modelData.morphs) { const sparseToFull = function(data, indices, totalCount) { const full = new Float32Array(totalCount * 3); for(let s = 0; s < indices.length; s++){ const dstIndex = indices[s] * 3; full[dstIndex] = data[s * 3]; full[dstIndex + 1] = data[s * 3 + 1]; full[dstIndex + 2] = data[s * 3 + 2]; } return full; }; for(i = 0; i < modelData.morphs.length; i++){ targets = modelData.morphs[i].targets; morphTargetArray = []; vertexCount = this._getMorphVertexCount(modelData, i, vertexBuffers); for(j = 0; j < targets.length; j++){ const targetAabb = targets[j].aabb; const min = targetAabb.min; const max = targetAabb.max; const aabb = new BoundingBox(new Vec3((max[0] + min[0]) * 0.5, (max[1] + min[1]) * 0.5, (max[2] + min[2]) * 0.5), new Vec3((max[0] - min[0]) * 0.5, (max[1] - min[1]) * 0.5, (max[2] - min[2]) * 0.5)); const indices = targets[j].indices; let deltaPositions = targets[j].deltaPositions; let deltaNormals = targets[j].deltaNormals; if (indices) { deltaPositions = sparseToFull(deltaPositions, indices, vertexCount); deltaNormals = sparseToFull(deltaNormals, indices, vertexCount); } morphTarget = new MorphTarget({ deltaPositions: deltaPositions, deltaNormals: deltaNormals, name: targets[j].name, aabb: aabb }); morphTargetArray.push(morphTarget); } const morph = new Morph(morphTargetArray, this._device); morphs.push(morph); const morphInstance = new MorphInstance(morph); morphInstances.push(morphInstance); } } return { morphs: morphs, instances: morphInstances }; } _parseVertexBuffers(data) { const modelData = data.model; const vertexBuffers = []; const attributeMap = { position: SEMANTIC_POSITION, normal: SEMANTIC_NORMAL, tangent: SEMANTIC_TANGENT, blendWeight: SEMANTIC_BLENDWEIGHT, blendIndices: SEMANTIC_BLENDINDICES, color: SEMANTIC_COLOR, texCoord0: SEMANTIC_TEXCOORD0, texCoord1: SEMANTIC_TEXCOORD1, texCoord2: SEMANTIC_TEXCOORD2, texCoord3: SEMANTIC_TEXCOORD3, texCoord4: SEMANTIC_TEXCOORD4, texCoord5: SEMANTIC_TEXCOORD5, texCoord6: SEMANTIC_TEXCOORD6, texCoord7: SEMANTIC_TEXCOORD7 }; for(let i = 0; i < modelData.vertices.length; i++){ const vertexData = modelData.vertices[i]; const formatDesc = []; for(const attributeName in vertexData){ const attribute = vertexData[attributeName]; formatDesc.push({ semantic: attributeMap[attributeName], components: attribute.components, type: JSON_VERTEX_ELEMENT_TYPE[attribute.type], normalize: attributeMap[attributeName] === SEMANTIC_COLOR }); } const vertexFormat = new VertexFormat(this._device, formatDesc); const numVertices = vertexData.position.data.length / vertexData.position.components; const vertexBuffer = new VertexBuffer(this._device, vertexFormat, numVertices); const iterator = new VertexIterator(vertexBuffer); for(let j = 0; j < numVertices; j++){ for(const attributeName in vertexData){ const attribute = vertexData[attributeName]; switch(attribute.components){ case 1: iterator.element[attributeMap[attributeName]].set(attribute.data[j]); break; case 2: iterator.element[attributeMap[attributeName]].set(attribute.data[j * 2], 1.0 - attribute.data[j * 2 + 1]); break; case 3: iterator.element[attributeMap[attributeName]].set(attribute.data[j * 3], attribute.data[j * 3 + 1], attribute.data[j * 3 + 2]); break; case 4: iterator.element[attributeMap[attributeName]].set(attribute.data[j * 4], attribute.data[j * 4 + 1], attribute.data[j * 4 + 2], attribute.data[j * 4 + 3]); break; } } iterator.next(); } iterator.end(); vertexBuffers.push(vertexBuffer); } return vertexBuffers; } _parseIndexBuffers(data, vertexBuffers) { const modelData = data.model; let indexBuffer = null; let indexData = null; let i; let numIndices = 0; for(i = 0; i < modelData.meshes.length; i++){ const meshData = modelData.meshes[i]; if (meshData.indices !== undefined) { numIndices += meshData.indices.length; } } let maxVerts = 0; for(i = 0; i < vertexBuffers.length; i++){ maxVerts = Math.max(maxVerts, vertexBuffers[i].numVertices); } if (numIndices > 0) { if (maxVerts > 0xFFFF) { indexBuffer = new IndexBuffer(this._device, INDEXFORMAT_UINT32, numIndices); indexData = new Uint32Array(indexBuffer.lock()); } else { indexBuffer = new IndexBuffer(this._device, INDEXFORMAT_UINT16, numIndices); indexData = new Uint16Array(indexBuffer.lock()); } } return { buffer: indexBuffer, data: indexData }; } _parseMeshes(data, skins, morphs, vertexBuffers, indexBuffer, indexData) { const modelData = data.model; const meshes = []; let indexBase = 0; for(let i = 0; i < modelData.meshes.length; i++){ const meshData = modelData.meshes[i]; const meshAabb = meshData.aabb; const min = meshAabb.min; const max = meshAabb.max; const aabb = new BoundingBox(new Vec3((max[0] + min[0]) * 0.5, (max[1] + min[1]) * 0.5, (max[2] + min[2]) * 0.5), new Vec3((max[0] - min[0]) * 0.5, (max[1] - min[1]) * 0.5, (max[2] - min[2]) * 0.5)); const indexed = meshData.indices !== undefined; const mesh = new Mesh(this._device); mesh.vertexBuffer = vertexBuffers[meshData.vertices]; mesh.indexBuffer[0] = indexed ? indexBuffer : null; mesh.primitive[0].type = JSON_PRIMITIVE_TYPE[meshData.type]; mesh.primitive[0].base = indexed ? meshData.base + indexBase : meshData.base; mesh.primitive[0].count = meshData.count; mesh.primitive[0].indexed = indexed; mesh.skin = meshData.skin !== undefined ? skins[meshData.skin] : null; mesh.morph = meshData.morph !== undefined ? morphs[meshData.morph] : null; mesh.aabb = aabb; if (indexed) { indexData.set(meshData.indices, indexBase); indexBase += meshData.indices.length; } meshes.push(mesh); } if (indexBuffer !== null) { indexBuffer.unlock(); } return meshes; } _parseMeshInstances(data, nodes, meshes, skins, skinInstances, morphs, morphInstances) { const modelData = data.model; const meshInstances = []; let i; for(i = 0; i < modelData.meshInstances.length; i++){ const meshInstanceData = modelData.meshInstances[i]; const node = nodes[meshInstanceData.node]; const mesh = meshes[meshInstanceData.mesh]; const meshInstance = new MeshInstance(mesh, this._defaultMaterial, node); if (mesh.skin) { const skinIndex = skins.indexOf(mesh.skin); meshInstance.skinInstance = skinInstances[skinIndex]; } if (mesh.morph) { const morphIndex = morphs.indexOf(mesh.morph); meshInstance.morphInstance = morphInstances[morphIndex]; } meshInstances.push(meshInstance); } return meshInstances; } } export { JsonModelParser };