168 lines
4.5 KiB
JavaScript
168 lines
4.5 KiB
JavaScript
import { Mesh } from './'
|
|
import { MeshInstance, MultiMeshInstance } from '../components'
|
|
import { Material } from './material'
|
|
|
|
import { mat4 } from 'gl-matrix'
|
|
|
|
import Resource from '../resource'
|
|
|
|
let meshCache = {}
|
|
|
|
// Parse an assimp2json formatted mesh file
|
|
// Supports multiple geometries
|
|
async function assimp2json (gl, file, dat, pos) {
|
|
let cleaned = []
|
|
let materials = []
|
|
for (let mi in dat.meshes) {
|
|
let mesh = dat.meshes[mi]
|
|
let material
|
|
|
|
if (mesh.materialindex != null && dat.materials && dat.materials.length) {
|
|
// Ensure we don't re-create materials with the same index
|
|
if (materials[mesh.materialindex]) {
|
|
material = materials[mesh.materialindex]
|
|
} else {
|
|
// Load a new material
|
|
material = new Material()
|
|
let matdata = dat.materials[mesh.materialindex].properties
|
|
|
|
// Parse material information
|
|
for (let pi in matdata) {
|
|
let property = matdata[pi]
|
|
if (!property || !property.key) continue
|
|
if (property.key === '?mat.name') material.name = property.value
|
|
else if (property.key.indexOf('$clr.') === 0) {
|
|
let dproperty = property.key.substr(5)
|
|
switch (dproperty) {
|
|
case 'specular':
|
|
case 'diffuse':
|
|
case 'shininess':
|
|
case 'ambient':
|
|
case 'reflective':
|
|
material[dproperty] = property.value
|
|
break
|
|
}
|
|
} else if (property.key.indexOf('$tex.file') === 0) {
|
|
if (!material.textures) {
|
|
material.textures = []
|
|
}
|
|
|
|
material.textures.push(property.value)
|
|
}
|
|
}
|
|
|
|
materials[mesh.materialindex] = material
|
|
}
|
|
}
|
|
|
|
cleaned.push({
|
|
vertices: mesh.vertices,
|
|
indices: [].concat.apply([], mesh.faces),
|
|
uv: mesh.texturecoords ? mesh.texturecoords[0] : null,
|
|
normals: mesh.normals ? mesh.normals : null,
|
|
material
|
|
})
|
|
}
|
|
|
|
// Load everything
|
|
let loadComplete = []
|
|
for (let i in cleaned) {
|
|
let meshdata = cleaned[i]
|
|
let mesh = Mesh.construct(gl, meshdata.vertices, meshdata.indices,
|
|
meshdata.uv, meshdata.normals)
|
|
|
|
// Initialize the material's texture if present
|
|
if (meshdata.material) {
|
|
mesh.material = meshdata.material
|
|
|
|
// Ensure all textures get loaded before finishing
|
|
if (meshdata.material.textures) {
|
|
await meshdata.material.loadTextures(gl)
|
|
}
|
|
}
|
|
|
|
loadComplete.push(mesh)
|
|
}
|
|
|
|
let finished = []
|
|
function setChildren (parent, chMeshes, last) {
|
|
if (!chMeshes.meshes) {
|
|
if (chMeshes.children) {
|
|
for (let j in chMeshes.children) {
|
|
setChildren(null, chMeshes.children[j], chMeshes)
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
// Transpose the mesh's transform
|
|
let transform = chMeshes.transformation
|
|
|
|
let meshIndex = chMeshes.meshes[0]
|
|
let mesh = loadComplete[meshIndex]
|
|
let meshInstance = new MeshInstance(mesh, parent == null ? pos : null)
|
|
|
|
meshInstance.mesh = mesh
|
|
|
|
if (chMeshes.children) {
|
|
for (let i in chMeshes.children) {
|
|
if (!chMeshes.children[i].meshes) continue
|
|
setChildren(meshInstance, chMeshes.children[i], chMeshes)
|
|
}
|
|
}
|
|
|
|
meshInstance.name = chMeshes.name
|
|
|
|
if (parent == null) {
|
|
// Multiply the last meshless node's transform in order to preserve it
|
|
|
|
finished.push(meshInstance)
|
|
} else {
|
|
parent.children.push(meshInstance)
|
|
meshInstance.parent = parent
|
|
}
|
|
|
|
meshInstance.setTransformation(transform)
|
|
}
|
|
|
|
setChildren(null, dat.rootnode)
|
|
|
|
if (!finished.length) throw new Error('Failed to build mesh.')
|
|
|
|
// Cache the mesh
|
|
meshCache[file] = finished
|
|
|
|
let returnType
|
|
if (finished.length > 1) {
|
|
returnType = new MultiMeshInstance(finished)
|
|
} else {
|
|
returnType = finished[0]
|
|
}
|
|
|
|
return returnType
|
|
}
|
|
|
|
// Parse a collada mesh
|
|
async function collada (gl, file, dat, pos) {
|
|
// TODO...
|
|
}
|
|
|
|
async function loadMesh (gl, file, pos) {
|
|
file = '/assets/models/' + file + '.json'
|
|
|
|
// Ensure each mesh file is loaded only once
|
|
if (meshCache[file]) return meshCache[file].length > 1 ? new MultiMeshInstance(meshCache[file], pos) : meshCache[file][0]
|
|
|
|
let dat = await Resource.GET({ type: 'json', url: file })
|
|
|
|
// Recognize a assimp2json file format
|
|
if (dat['__metadata__'] && dat['__metadata__'].format === 'assimp2json') {
|
|
if (!dat.meshes) throw new Error('No geometries found in file ' + file)
|
|
return assimp2json(gl, file, dat, pos)
|
|
}
|
|
|
|
throw new Error('Unsupported mesh format.')
|
|
}
|
|
|
|
export default loadMesh
|