UNPKG

pbrtools

Version:
614 lines (613 loc) 22.5 kB
"use strict"; const buffer_1 = require("./buffer"); const fs = require("fs"); const path = require("path"); const util_1 = require("./util"); const meshGeo_1 = require("./meshGeo"); //Version 2 class data_block2 { constructor(off, len) { this.offofData = 0; //文档说是相对内容区,实际是相对于整个文件 this.length = 0; this.offofData = off; this.length = len; } } class Mtl_block2 { constructor() { this.clsName = ''; this.shaderName = ''; this.mtlPath = ''; } } class BindBoneInfo2 { constructor() { this._bones = []; } } class MeshInfo2 { constructor() { this._name = ''; this._vbInfos = []; } } class SubmeshInfo2 { constructor() { this.vbIdx = 0; //short this.vbstart = 0; //int32 this.vblen = 0; //int32 this.ibstart = 0; //int32; this.iblen = 0; //int32 this.drawCnt = 0; //short this.subs = []; } } class VertexAttrib { constructor(attrib) { this.attribs = attrib.split(/[,;:]/); } getVertexSize() { let sz = 0; this.attribs.forEach((v) => { if (VertexAttrib.compSz[v]) { sz += VertexAttrib.compSz[v].sz; } else { throw 'unknown vertex component :' + v; } }); return sz; } hasAttrib(attr) { return this.attribs.indexOf(attr) >= 0; } getOff(attr) { let off = 0; for (let i = 0; i < this.attribs.length; i++) { let cattr = this.attribs[i]; if (cattr == attr) { return off; } off += VertexAttrib.compSz[cattr].sz; } return -1; } } VertexAttrib.compSz = { POSITION: { sz: 12, type: 'float', num: 3 }, NORMAL: { sz: 12, type: 'float', num: 3 }, COLOR: { sz: 16, type: 'float', num: 4 }, UV: { sz: 8, type: 'float', num: 2 }, UV1: { sz: 8, type: 'float', num: 2 }, BLENDWEIGHT: { sz: 16, type: 'float', num: 4 }, BLENDINDICES: { sz: 16, type: 'int32', num: 4 }, TANGENT: { sz: 12, type: 'float', num: 3 }, BINORMAL: { sz: 12, type: 'float', num: 3 } }; class loader_lh2 extends buffer_1.BufferReader { constructor() { super(); this._dataoff = 0; //数据区偏移 this._strings = []; this._datablocks = []; this._mtls = []; this._meshInfo = new MeshInfo2(); this._bindInfo = new BindBoneInfo2(); this._subMeshes = []; this._attrReg = new RegExp("(\\w+)|([:,;])", "g"); this._cursubmeshid = 0; } load(str) { var data = fs.readFileSync(str); this.parse(data.buffer); } parse(data) { this.init(data); let version = this.readString(); //区块信息 this._dataoff = this.readU32(); let len = this.readU32(); let cnt = this.readU16(); for (let i = 0; i < cnt; i++) { this._datablocks.push(new data_block2(this.readU32(), this.readU32())); } //字符串 let strOff = this.readU32(); let strCnt = this.readU16(); this.seek(this._dataoff + strOff); for (let i = 0; i < strCnt; i++) { this._strings.push(this.readString()); } //内容区 this._datablocks.forEach((v, i) => { this.seek(v.offofData); let strid = this.readU16(); var str = this._strings[strid]; console.log('--- ' + str); var func = this['read_' + str]; if (func) { func.call(this); } else { console.log('unknown block ' + str); } }); console.log('ok'); } read_MATERIAL() { let mtlinfo = new Mtl_block2(); mtlinfo.clsName = this._strings[this.readU16()]; mtlinfo.shaderName = this._strings[this.readU16()]; mtlinfo.mtlPath = this._strings[this.readU16()]; this._mtls.push(mtlinfo); } read_MESH() { this._meshInfo._name = this._strings[this.readU16()]; //VB let vbCnt = this.readU16(); for (let i = 0; i < vbCnt; i++) { let vbstart = this.readU32(); let vblen = this.readU32(); let attrib = this._strings[this.readU16()]; let cvbinfo = { _vb: null, _attrib: '' }; let vb = null; if ((this._dataoff + vbstart) % 4 != 0) { //没有按照4对齐 let vbdt = new Uint8Array(this.buff, this._dataoff + vbstart, vblen); vb = new Float32Array(vblen / 4); (new Uint8Array(vb.buffer)).set(vbdt); } else { vb = new Float32Array(this.buff, this._dataoff + vbstart, vblen / 4); } this._meshInfo._vbInfos.push({ _vb: vb, _attrib: attrib }); } //IB let ibstart = this.readU32(); let iblen = this.readU32(); if ((this._dataoff + ibstart) % 2 != 0) { let ibdt = new Uint8Array(this.buff, this._dataoff + ibstart, iblen); this._meshInfo._ib = new Uint16Array(iblen / 2); (new Uint8Array(this._meshInfo._ib.buffer)).set(ibdt); } else { this._meshInfo._ib = new Uint16Array(this.buff, this._dataoff + ibstart, iblen / 2); } //bones let boneCnt = this.readU16(); for (let i = 0; i < boneCnt; i++) { let bonename = this._strings[this.readU16()]; this._bindInfo._bones.push(bonename); } let bindPoseStart = this.readU32(); let bindPoseLen = this.readU32(); let bpdt = new Uint8Array(this.buff, this._dataoff + bindPoseStart, bindPoseLen); this._bindInfo._bonePose = new Float32Array(bindPoseLen / 4); (new Uint8Array(this._bindInfo._bonePose.buffer)).set(bpdt); let inverseGlobalBindPosesStart = this.readU32(); let inverseGlobalBindPosesLength = this.readU32(); let igPose = new Uint8Array(this.buff, this._dataoff + inverseGlobalBindPosesStart, inverseGlobalBindPosesLength); this._bindInfo._invgBonePose = new Float32Array(inverseGlobalBindPosesLength / 4); (new Uint8Array(this._bindInfo._invgBonePose.buffer)).set(igPose); } read_SUBMESH() { let sub = new SubmeshInfo2(); sub.vbIdx = this.readU16(); sub.vbstart = this.readU32(); sub.vblen = this.readU32(); sub.ibstart = this.readU32(); sub.iblen = this.readU32(); sub.drawCnt = this.readU16(); for (let i = 0; i < sub.drawCnt; i++) { let subsub = { subibstart: this.readU32(), subiblen: this.readU32(), bonedic: new Uint8Array(this.buff, this._dataoff + this.readU32(), this.readU32()) }; sub.subs.push(subsub); } this._subMeshes.push(sub); } getStr(id) { return this._strings[id]; } getOrAddString(str) { let r = this._strings.indexOf(str); if (r < 0) { r = this._strings.length; this._strings.push(str); } return r; } save(file) { //先清空字符串 this._strings = []; this._mtls.length = 0; let blockcnt = this._mtls.length + 1 + this._subMeshes.length; this._datablocks.length = 0; //计算头部分的大小 let verstr = 'LAYAMODEL:03'; let headlen = verstr.length + 2; headlen += (4 + 4 + 2 + blockcnt * 8 + 4 + 2); this._dataoff = headlen; let headbuff = new buffer_1.BufferWriter(headlen); //头先不写,最后在写 let datapos = 0; //只是数据区偏移,不是整体 //写材质区 let mtlblockw = new buffer_1.BufferWriter(this._mtls.length * 8); let mtlnameid = this.getOrAddString('MATERIAL'); this._mtls.forEach((v) => { let cblk = new data_block2(datapos + this._dataoff, 8); this._datablocks.push(cblk); mtlblockw.wu16(mtlnameid); mtlblockw.wu16(this.getOrAddString(v.clsName)); mtlblockw.wu16(this.getOrAddString(v.shaderName)); mtlblockw.wu16(this.getOrAddString(v.mtlPath)); datapos += 8; }); //写mesh let meshsz = 2 + 2 + 2 + this._meshInfo._vbInfos.length * (4 + 4 + 2) + 4 + 4 + 2 + 2 * this._bindInfo._bones.length + 4 + 4 + 4 + 4; //len let meshblockw = new buffer_1.BufferWriter(meshsz); this._datablocks.push(new data_block2(datapos + this._dataoff, meshsz)); datapos += meshsz; meshblockw.wu16(this.getOrAddString('MESH')) .wu16(this.getOrAddString(this._meshInfo._name)) .wu16(this._meshInfo._vbInfos.length); let vbsz = 0; this._meshInfo._vbInfos.forEach((v) => { meshblockw.wu32(datapos) .wu32(v._vb.byteLength) .wu16(this.getOrAddString(v._attrib)); datapos += v._vb.byteLength; vbsz += v._vb.byteLength; }); //ib meshblockw.wu32(datapos); //IB实际位置 meshblockw.wu32(this._meshInfo._ib.byteLength); datapos += this._meshInfo._ib.byteLength; //bone meshblockw.wu16(this._bindInfo._bones.length); this._bindInfo._bones.forEach((v) => { meshblockw.wu16(this.getOrAddString(v)); }); //bindpose meshblockw.wu32(datapos); meshblockw.wu32(this._bindInfo._bonePose.byteLength); datapos += this._bindInfo._bonePose.byteLength; //invg meshblockw.wu32(datapos); meshblockw.wu32(this._bindInfo._invgBonePose.byteLength); datapos += this._bindInfo._invgBonePose.byteLength; //mesh对应的数据 let meshdatasz = vbsz + this._meshInfo._ib.byteLength + this._bindInfo._bonePose.byteLength + this._bindInfo._invgBonePose.byteLength; let meshdatablockw = new buffer_1.BufferWriter(meshdatasz); this._meshInfo._vbInfos.forEach((v) => { meshdatablockw.wab(v._vb.buffer, v._vb.byteLength, v._vb.byteOffset); }); let _ib = this._meshInfo._ib; meshdatablockw.wab(_ib.buffer, _ib.byteLength, _ib.byteOffset); meshdatablockw.wab(this._bindInfo._bonePose.buffer); meshdatablockw.wab(this._bindInfo._invgBonePose.buffer); //submesh let submeshsz = 0; this._subMeshes.forEach((v) => { let cursz = 2 + 2 + 4 + 4 + 4 + 4 + 2 + v.drawCnt * 16; submeshsz += cursz; this._datablocks.push(new data_block2(datapos + this._dataoff, cursz)); datapos += cursz; //完成后指向了submesh的最后部分 }); let submeshblockw = new buffer_1.BufferWriter(submeshsz); let bonedicsz = 0; this._subMeshes.forEach((v) => { submeshblockw.wu16(this.getOrAddString('SUBMESH')) .wu16(v.vbIdx) .wu32(v.vbstart) .wu32(v.vblen) .wu32(v.ibstart) .wu32(v.iblen) .wu16(v.drawCnt); for (let i = 0; i < v.drawCnt; i++) { let dicsz = v.subs[i].bonedic.byteLength; bonedicsz += dicsz; submeshblockw.wu32(v.subs[i].subibstart) .wu32(v.subs[i].subiblen) .wu32(datapos) .wu32(dicsz); datapos += dicsz; } }); //bonedic let bonedicw = new buffer_1.BufferWriter(bonedicsz); this._subMeshes.forEach((v) => { v.subs.forEach((sv) => { bonedicw.wab(sv.bonedic.buffer, sv.bonedic.byteLength, sv.bonedic.byteOffset); }); }); let strpos = datapos; //为了方便,字符串最后再写 let strsw = new buffer_1.BufferWriter(0); strsw.nowrite = true; //先计算大小 this._strings.forEach((str) => { strsw.wstr(str); }); //然后真写 let strbuffsz = strsw._writePos; strsw = new buffer_1.BufferWriter(strbuffsz); this._strings.forEach((str) => { strsw.wstr(str); }); //整合 let alldatalen = meshblockw.buff.byteLength + meshdatablockw.buff.byteLength + submeshblockw.buff.byteLength + bonedicw.buff.byteLength + strsw.buff.byteLength; headbuff.wstr(verstr); headbuff.wu32(headlen) .wu32(alldatalen) //link data lenth .wu16(blockcnt); this._datablocks.forEach((v) => { headbuff.wu32(v.offofData) .wu32(v.length); }); headbuff.wu32(strpos); //stroff headbuff.wu16(this._strings.length); //strcnt let out = new buffer_1.BufferWriter(headbuff.buff.byteLength + alldatalen); out.wab(headbuff.buff); //out.wab(mtlblockw.buff); out.wab(meshblockw.buff); out.wab(meshdatablockw.buff); out.wab(submeshblockw.buff); out.wab(bonedicw.buff); out.wab(strsw.buff); fs.writeFileSync(file, new Buffer(new Uint8Array(out.buff))); } } //TEST /* var cc = new loader_lh2(); cc.load('E:/layaair/layaair/publish/LayaAirPublish/samples/res/threeDimen/skinModel/dude/dude-him.lm'); try{ cc.save('d:/temp/bb.lm'); }catch(e){ console.log(e); } */ //TEST function handlelm(lm, modelcnfg, swapyz) { lm._mtls.forEach((mtl, i) => { if (modelcnfg && modelcnfg[mtl.mtlPath]) { lm._mtls[i].mtlPath = modelcnfg[mtl.mtlPath]; } else { lm._mtls[i].mtlPath = lm._mtls[i].mtlPath.replace('.lmat', '.lpbr'); } console.log(lm._mtls[i].mtlPath); }); lm._meshInfo._vbInfos.forEach((v) => { let attrib = new VertexAttrib(v._attrib); let vertstride = attrib.getVertexSize(); let oldfstride = vertstride / 4; let vertnum = v._vb.byteLength / vertstride; let mg = new meshGeo_1.MeshGeo(v._vb, lm._meshInfo._ib, vertstride / 4, attrib.getOff('UV') / 4, ''); //BBX var minx = 1e6, miny = 1e6, minz = 1e6, maxx = -1e6, maxy = -1e6, maxz = -1e6; for (let i = 0; i < vertnum; i++) { var stp = i * oldfstride; var posx = v._vb[stp++]; var posy = v._vb[stp++]; var posz = v._vb[stp++]; if (posx < minx) minx = posx; if (posx > maxx) maxx = posx; if (posy < miny) miny = posy; if (posy > maxy) maxy = posy; if (posz < minz) minz = posz; if (posz > maxz) maxz = posz; stp = i * oldfstride; } console.log('submesh size:[', minx, miny, minz, maxx, maxy, maxz, ']'); let oldvb = v._vb; var coldi = 0; //缩放 if (modelcnfg && modelcnfg['scale'] != undefined) { var scale = modelcnfg['scale'] * 1.0; for (let i = 0; i < vertnum; i++) { var stp = i * oldfstride; var posx = oldvb[stp++]; var posy = oldvb[stp++]; var posz = oldvb[stp++]; if (posx < minx) minx = posx; if (posx > maxx) maxx = posx; if (posy < miny) miny = posy; if (posy > maxy) maxy = posy; if (posz < minz) minz = posz; if (posz > maxz) maxz = posz; stp = i * oldfstride; oldvb[stp++] = posx * scale; oldvb[stp++] = posy * scale; oldvb[stp++] = posz * scale; } } //TODO 是否交换yz if (swapyz) { console.log('ATTENTION! swap y z now! '); let coldi = 0; for (let i = 0; i < vertnum; i++) { var stp = i * oldfstride; var posx = oldvb[stp++]; var posy = oldvb[stp++]; var posz = oldvb[stp++]; var normx = oldvb[stp++]; var normy = oldvb[stp++]; var normz = oldvb[stp++]; stp = i * oldfstride; oldvb[stp++] = posx; oldvb[stp++] = posz; oldvb[stp++] = -posy; oldvb[stp++] = normx; oldvb[stp++] = normz; oldvb[stp++] = -normy; } } //计算tangent //目前只处理两个都没有的 let docalctan = true; if (v._attrib === 'POSITION,NORMAL,UV') { console.log('111', v._attrib); let newattrib = 'POSITION,NORMAL,TANGENT,BINORMAL,UV'; v._attrib = newattrib; } else if (v._attrib == 'POSITION,NORMAL,UV,BLENDWEIGHT,BLENDINDICES') { console.log('222', v._attrib); let newattrib = 'POSITION,NORMAL,TANGENT,BINORMAL,UV,BLENDWEIGHT,BLENDINDICES'; v._attrib = newattrib; } else if (v._attrib == 'POSITION,NORMAL,UV,UV1,BLENDWEIGHT,BLENDINDICES') { console.log('112', v._attrib); let newattrib = 'POSITION,NORMAL,TANGENT,BINORMAL,UV,UV1,BLENDWEIGHT,BLENDINDICES'; v._attrib = newattrib; } else { docalctan = false; console.log('error:vetext has tangent. do nothing'); } if (docalctan) { let newfstride = oldfstride + 6; var tb = mg.calcTangent(); var vb = v._vb = new Float32Array(vertnum * newfstride); var cnewi = 0; for (let i = 0; i < vertnum; i++) { vb[cnewi++] = oldvb[coldi++]; vb[cnewi++] = oldvb[coldi++]; vb[cnewi++] = oldvb[coldi++]; //pos vb[cnewi++] = oldvb[coldi++]; vb[cnewi++] = oldvb[coldi++]; vb[cnewi++] = oldvb[coldi++]; //normal vb[cnewi++] = tb.tan[i * 3]; vb[cnewi++] = tb.tan[i * 3 + 1]; vb[cnewi++] = tb.tan[i * 3 + 2]; //tan vb[cnewi++] = tb.binor[i * 3]; vb[cnewi++] = tb.binor[i * 3 + 1]; vb[cnewi++] = tb.binor[i * 3 + 2]; //bin //剩下的 for (var li = 0; li < (oldfstride - 6); li++) { vb[cnewi++] = oldvb[coldi++]; } } console.log('calctangent ok'); } }); return true; } var srcPath = 'E:/layaair/layaair/publish/LayaAirPublish/samples/res/threeDimen/skinModel/dude'; function dowork() { var modelCnfgObj = {}; try { var modelcnfg = fs.readFileSync(srcPath + '/config.json', 'utf8'); if (modelcnfg) { modelCnfgObj = JSON.parse(modelcnfg); } } catch (e) { console.log('no config file:config.json'); } util_1.allfiles(srcPath, (file) => { if (path.extname(file).toLowerCase() == '.lm') { if (path.extname(file.substr(0, file.length - 3)).toLowerCase() == '.lm') { return; } var cc = new loader_lh2(); try { cc.load(file); } catch (e) { console.log(e); return; } var rfile = path.basename(file); var filepath = path.dirname(file) + '/'; //材质相关 cc._mtls.forEach((v, i) => { var lmat = filepath + v.mtlPath; var lpbr = filepath + v.mtlPath.replace('.lmat', '.lpbr'); var colorfile = ''; var normalfile = ''; var meshname = ''; if (!fs.existsSync(lpbr)) { if (fs.existsSync(lmat)) { try { var lmatobj = JSON.parse(fs.readFileSync(lmat, 'utf8')); try { colorfile = lmatobj['customProps']['diffuseTexture']['texture2D']; } catch (e) { } try { normalfile = lmatobj['customProps']['normalTexture']['texture2D']; } catch (e) { } try { meshname = lmatobj['props']['name']; } catch (e) { } } catch (e) { } } var pbrfilec = `{ "version":"LAYAMATERIAL:01", "type": "PBRMaterial", "props": { "name": "${meshname}", "renderMode": 1, "has_tangent":true, "textures":[ {"name":"diffuseTexture","path":"${colorfile}"}, {"name":"normalTexture","path":"${normalfile}"} ] } }`; fs.writeFileSync(lpbr, pbrfilec, 'utf8'); } }); //修改mesh if (handlelm(cc, modelCnfgObj[rfile], false)) { cc.save(file + '.lm'); } else { } } }, false); } dowork();