UNPKG

mdx-m3-viewer

Version:

A browser WebGL model viewer. Mainly focused on models of the games Warcraft 3 and Starcraft 2.

271 lines (219 loc) 7.95 kB
import Parser from '../../../parsers/m3/model'; import M3ParserModel from '../../../parsers/m3/modelheader'; import M3ParserDivision from '../../../parsers/m3/division'; import Model from '../../model'; import M3StandardMaterial from './standardmaterial'; import M3Bone from './bone'; import M3Sequence from './sequence'; import M3Sts from './sts'; import M3Stc from './stc'; import M3Stg from './stg'; import M3AttachmentPoint from './attachment'; import M3Camera from './camera'; import M3Region from './region'; import M3ModelInstance from './modelinstance'; import M3Batch from './batch'; /** * An M3 model. */ export default class M3Model extends Model { name: string = ''; batches: M3Batch[] = []; materials: any[][] = [[], []]; // 2D array for the possibility of adding more material types in the future materialMaps: any[] = []; bones: M3Bone[] = []; boneLookup: Uint16Array | null = null; sequences: M3Sequence[] = []; sts: M3Sts[] = []; stc: M3Stc[] = []; stg: M3Stg[] = []; attachments: M3AttachmentPoint[] = []; cameras: M3Camera[] = []; regions: M3Region[] = []; initialReference: Float32Array[] = []; elementBuffer: WebGLBuffer | null = null; arrayBuffer: WebGLBuffer | null = null; vertexSize: number = 0; uvSetCount: number = 0; createInstance(): M3ModelInstance { return new M3ModelInstance(this); } load(bufferOrParser: ArrayBuffer | Parser) { let parser; if (bufferOrParser instanceof Parser) { parser = bufferOrParser; } else { parser = new Parser(bufferOrParser); } let model = <M3ParserModel>parser.model; let div = model.divisions.get(); this.name = model.modelName.getAll().join(''); this.setupGeometry(model, div); let materialMaps = <any[]>model.materialReferences.getAll(); this.materialMaps = materialMaps; // Create concrete material objects for standard materials for (let material of model.materials[0].getAll()) { this.materials[1].push(new M3StandardMaterial(this, material)); } // Create concrete batch objects for (let batch of div.batches.getAll()) { let regionId = batch.regionIndex; let materialMap = materialMaps[batch.materialReferenceIndex]; if (materialMap.materialType === 1) { this.batches.push(new M3Batch(this.regions[regionId], this.materials[1][materialMap.materialIndex])) } } /* var batchGroups = [[], [], [], [], [], []]; for (i = 0, l = batches.length; i < l; i++) { var blendMode = batches[i].material.blendMode; batchGroups[blendMode].push(batches[i]); } function sortByPriority(a, b) { var a = a.material.priority; var b = b.material.priority; if (a < b) { return 1; } else if (a == b) { return 0; } else { return -1; } } for (i = 0; i < 6; i++) { batchGroups[i].sort(sortByPriority); } */ /* // In the EggPortrait model the batches seem to be sorted by blend mode. Is this true for every model? this.batches.sort(function (a, b) { var ba = a.material.blendMode; var bb = b.material.blendMode; if (ba < bb) { return -1; } else if (ba == bb) { return 0; } else { return 1; } }); */ // this.batches = batchGroups[0].concat(batchGroups[1]).concat(batchGroups[2]).concat(batchGroups[3]).concat(batchGroups[4]).concat(batchGroups[5]); this.initialReference = <Float32Array[]>model.absoluteInverseBoneRestPositions.getAll(); for (let bone of model.bones.getAll()) { this.bones.push(new M3Bone(bone)); } this.boneLookup = <Uint16Array>model.boneLookup.getAll(); for (let sequence of model.sequences.getAll()) { this.sequences.push(new M3Sequence(sequence)); } for (let sts of model.sts.getAll()) { this.sts.push(new M3Sts(sts)); } for (let stc of model.stc.getAll()) { this.stc.push(new M3Stc(stc)); } for (let stg of model.stg.getAll()) { this.stg.push(new M3Stg(stg, this.sts, this.stc)); } this.addGlobalAnims(); /* if (parser.fuzzyHitTestObjects.length > 0) { for (i = 0, l = parser.fuzzyHitTestObjects.length; i < l; i++) { this.boundingShapes[i] = new M3BoundingShape(parser.fuzzyHitTestObjects[i], parser.bones, gl); } } */ /* if (parser.particleEmitters.length > 0) { this.particleEmitters = []; for (i = 0, l = parser.particleEmitters.length; i < l; i++) { this.particleEmitters[i] = new M3ParticleEmitter(parser.particleEmitters[i], this); } } */ for (let attachment of model.attachmentPoints.getAll()) { this.attachments.push(new M3AttachmentPoint(attachment)); } for (let camera of model.cameras.getAll()) { this.cameras.push(new M3Camera(camera)); } } setupGeometry(parser: M3ParserModel, div: M3ParserDivision) { let gl = this.viewer.gl; let uvSetCount = 1; let vertexFlags = parser.vertexFlags; if (vertexFlags & 0x40000) { uvSetCount = 2; } else if (vertexFlags & 0x80000) { uvSetCount = 3; } else if (vertexFlags & 0x100000) { uvSetCount = 4; } let regions = div.regions.getAll(); let totalElements = 0; let offsets = []; for (let i = 0, l = regions.length; i < l; i++) { offsets[i] = totalElements; totalElements += regions[i].triangleIndicesCount; } let elementArray = new Uint16Array(totalElements); let triangles = <Uint16Array>div.triangles.getAll(); for (let i = 0, l = regions.length; i < l; i++) { this.regions.push(new M3Region(this, regions[i], triangles, elementArray, offsets[i])); } let vertices = <Uint8Array>parser.vertices.getAll(); this.elementBuffer = gl.createBuffer(); gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.elementBuffer); gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, elementArray, gl.STATIC_DRAW); let arrayBuffer = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, arrayBuffer); gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW); this.arrayBuffer = arrayBuffer; this.vertexSize = (7 + uvSetCount) * 4; this.uvSetCount = uvSetCount; } mapMaterial(index: number) { let materialMap = this.materialMaps[index]; return this.materials[materialMap.materialType][materialMap.materialIndex]; } addGlobalAnims() { /* var i, l; var glbirth, glstand, gldeath; var stgs = this.stg; var stg, name; for (i = 0, l = stgs.length; i < l; i++) { stg = stgs[i]; name = stg.name.toLowerCase(); // Because obviously there will be a wrong case in some model... if (name === 'glbirth') { glbirth = stg; } else if (name === 'glstand') { glstand = stg; } else if (name === 'gldeath') { gldeath = stg; } } for (i = 0, l = stgs.length; i < l; i++) { stg = stgs[i]; name = stg.name.toLowerCase(); // Because obviously there will be a wrong case in some model... if (name !== 'glbirth' && name !== 'glstand' && name !== 'gldeath') { if (name.indexOf('birth') !== -1 && glbirth) { stg.stcIndices = stg.stcIndices.concat(glbirth.stcIndices); } else if (name.indexOf('death') !== -1 && gldeath) { stg.stcIndices = stg.stcIndices.concat(gldeath.stcIndices); } else if (glstand) { stg.stcIndices = stg.stcIndices.concat(glstand.stcIndices); } } } */ } // getValue(animRef: M3ParserAnimationReference, sequence: number, frame: number) { // if (sequence !== -1) { // return this.stg[sequence].getValue(animRef, frame); // } else { // return animRef.initValue; // } // } }