UNPKG

pbrtools

Version:
628 lines (627 loc) 23 kB
"use strict"; const meshGeo_1 = require("./meshGeo"); const buffer_1 = require("./buffer"); const util_1 = require("./util"); const fs = require("fs"); const path = require("path"); class Laya_SubMesh { constructor() { this.attribs = 'POSITION:3,32,0;NORMAL:3,32,12;UV:2,32,24;'; } } class Laya_Mesh { constructor() { this.version = 'LAYASKINANI:01'; this.name = 'MESH'; this.materials = []; //只用字符串表示对应的材质文件或者材质id。有几个就表示有几个submesh this.submeshes = []; } } class _submeshInfo { constructor() { this.iboff = 0; this.ibsize = 0; this.vboff = 0; this.vbsize = 0; this.boneidxoff = 0; this.boneidxsize = 0; } } class Laya_Mesh_W extends buffer_1.BufferWriter { constructor() { super(); this._strmap = ['BLOCK', 'DATA', 'STRINGS']; this.dataOff = 0; this.dataSize = 0; this.strsOff = 0; this.strsNum = 0; // this.bindPoseOff = 0; this.bindPoseSize = 0; this.invGBindePoseOff = 0; this.invGBindePoseSize = 0; this.submeshInfo = []; } /** * 返回写了多大。 */ outobj(obj, members, buff, off) { if (!obj._memtype) { throw '对象中必须有_memtype 描述'; } var mems = members.split(','); var dview = new DataView(buff, off); var moff = 0; mems.forEach((mname) => { var tp = obj._memtype[mname]; if (!tp) throw 'no type info of member ' + mname; var value = obj[mname]; if (value == undefined) throw 'no this member :' + mname; var isarr = value instanceof Array; var len = isarr ? value.length : 0; if (isarr) throw 'outobj not implements array'; switch (tp) { case 'u8': dview.setUint8(moff, value); moff += 1; break; case 'u16': dview.setUint16(moff, value, true); moff += 2; break; case 'u32': dview.setUint32(moff, value, true); moff += 4; break; case 'f32': dview.setFloat32(moff, value, true); moff += 4; break; } }); return moff; } outStrings(strs) { } saveAsLm(file) { this.submeshInfo = []; this.mesh.submeshes.forEach((v, i) => { this.submeshInfo[i] = new _submeshInfo(); }); //先空跑一下,组装字符表,统计大小 this.nowrite = true; this._saveAsLm(); var sz = this._writePos; this.buff = new ArrayBuffer(sz); this.datav = new DataView(this.buff); //真正写buffer this.nowrite = false; this._writePos = 0; this._saveAsLm(); fs.writeFileSync(file, new Buffer(new Uint8Array(this.buff))); } _saveAsLm() { this.wstr(this.mesh.version) .wu16(0) .wu16(5 + this.mesh.materials.length + this.mesh.submeshes.length); //chunk count //0 //data chunk //'DATA' this.wstrid('DATA') //data chunk id .wu32(this.dataOff) .wu32(this.dataSize); //var dataC={id:1,dataoff:0,datasize:0,_memtype:{id:'u16',dataoff:'u32',datasize:'u32'}}; //this.outobj(dataC,'id,dataoff,datasize',this.buff,this._writePos); //1 //strings chunk //'STRINGS' this.wstrid('STRINGS') //strings chunk id .wu16(this.strsOff) .wu16(this.strsNum); //把所有的string以wstr的方式写到对应区域 //2 //material chunk //'MATERIAL' this.mesh.materials.forEach((mtlname, i) => { this.wstrid('MATERIAL'); //id this.wu16(i); //放到_materials数组的位置 this.wstrid('SIMPLE') //shader name /没有用 .wstrid(mtlname); //url }); //其他的material //this.wu16(3);//相同块的id相同 //3 //mesh chunk //'MESH' this.wstrid('MESH') //id .wstrid(this.mesh.name) //name .wu32(this.bindPoseOff) //bindpose start .wu32(this.bindPoseSize) //bindpose size .wu32(this.invGBindePoseOff) //invGBindPose start .wu32(this.invGBindePoseSize); //invGBindPose size //写bindpose和invgbindpos到相应的地方 //4 //sub mesh //"SUBMESH" var materialid = 0; this.mesh.submeshes.forEach((sm, i) => { var sminfo = this.submeshInfo[i]; this.wstrid('SUBMESH') .wstrid('SUBMESH') .wu8(materialid); this.wstrid(sm.attribs) .wu32(sminfo.iboff) //ibofs 都是相对于data的 .wu32(sminfo.ibsize) //ibsize .wu32(0) //vbIndicesofs 不知道干什么的 .wu32(0) //vbIndicessize 不知道干什么的 .wu32(sminfo.vboff) //vbofs .wu32(sminfo.vbsize) //vbsize .wu32(sminfo.boneidxoff) //bonedicofs .wu32(sminfo.boneidxsize); //bonedicsize }); //写vb,ib,bonedic:int8[] //5 //DATAAREA this.wstrid('DATAAREA'); //=6 this.dataOff = this._writePos; this.strsOff = this._writePos - this.dataOff; this.strsNum = this._strmap.length; //strings this._strmap.forEach((v) => { this.wstr(v); }); //pose this.bindPoseOff = this._writePos - this.dataOff; this.bindPoseSize = this.mesh._bindPoses.byteLength; this.wab(this.mesh._bindPoses.buffer, this.mesh._bindPoses.byteLength); //invpose this.invGBindePoseOff = this._writePos - this.dataOff; this.invGBindePoseSize = this.mesh._inverseBindPoses.byteLength; this.wab(this.mesh._inverseBindPoses.buffer, this.mesh._inverseBindPoses.byteLength); this.mesh.submeshes.forEach((sm, i) => { var sminfo = this.submeshInfo[i]; //vb sminfo.vboff = this._writePos - this.dataOff; sminfo.vbsize = sm._vertexBuffer.byteLength; this.wab(sm._vertexBuffer.buffer, sm._vertexBuffer.byteLength); //ib sminfo.iboff = this._writePos - this.dataOff; sminfo.ibsize = sm._indexBuffer.byteLength; this.wab(sm._indexBuffer.buffer, sm._indexBuffer.byteLength); //bone index sminfo.boneidxoff = this._writePos - this.dataOff; sminfo.boneidxsize = sm._boneindicesBuffer.byteLength; this.wab(sm._boneindicesBuffer.buffer, sm._boneindicesBuffer.byteLength); }); } wstrid(str) { var i = this._strmap.indexOf(str); if (i == -1) { i = this._strmap.length; this._strmap.push(str); } this.wu16(i); return this; } } exports.Laya_Mesh_W = Laya_Mesh_W; /** * 问题:现在的格式无法略过不认识的块。 */ class loader_lh extends buffer_1.BufferReader { constructor() { super(); this._strings = ['BLOCK', 'DATA', 'STRINGS']; //初始值。read_STRINGS后会被替换 this._funcs = [null, this.read_DATA.bind(this), this.read_STRINGS.bind(this)]; this.blockCount = 0; this.dataoffset = 0; this.datasize = 0; this._attrReg = new RegExp("(\\w+)|([:,;])", "g"); this._cursubmeshid = 0; } load(str, mesh) { this.mesh = mesh; } parse(data) { this.init(data); this.mesh.version = this.readString(); this.read_BLOCK(); for (var i = 0; i < this.blockCount; i++) { var idx = this.readU16(); var blcokname = this._strings[idx]; var func = this['read_' + blcokname]; if (func) { func.call(this); } else { console.error('no function to read chunk ' + blcokname); } if (idx == 2) { var lmaturls = this._strings.filter((v) => { return v.indexOf('.lmat') > 0; }); } } } read_BLOCK() { this.readpos += 2; this.blockCount = this.readU16(); } read_DATA() { this.dataoffset = this.readU32(); this.datasize = this.readU32(); } read_STRINGS() { var ret = []; var offset = this.readU16(); var size = this.readU16(); var oldpos = this.readpos; //strings的绝对偏移。注意是相对于data块的 this.readpos = this.dataoffset + offset; for (var i = 0; i < size; i++) { var ss = this.readString(); ret.push(ss); } this._strings = ret; this.readpos = oldpos; } read_MATERIAL() { var index = this.readU16(); var stridx = this.readU16(); var urlidx = this.readU16(); var shadername = this._strings[stridx]; var url = this._strings[urlidx]; // mesh if (index < 0 || index > 255) throw 'material index error:' + index; this.mesh.materials[index] = url; } read_MESH() { var name = this.getStr(this.readU16()); switch (this.mesh.version) { case 'LAYASKINANI:01': var bindPoseStart = this.readU32(); var binPoseLength = this.readU32(); var ttt = this.buff.slice(bindPoseStart + this.dataoffset); //由于起始位置不是4的倍数,只能重新复制一个 var bindPoseDatas = new Float32Array(ttt, 0, binPoseLength); var invBindPoseStart = this.readU32(); var invBindPoseLength = this.readU32(); ttt = this.buff.slice(invBindPoseStart + this.dataoffset); var invBindPoseDatas = new Float32Array(ttt, 0, invBindPoseLength); //mesh this.mesh._bindPoses = bindPoseDatas; this.mesh._inverseBindPoses = invBindPoseDatas; break; default: throw ('wrong version :' + this.mesh.version); } } read_SUBMESH() { var className = this.getStr(this.readU16()); var material = this.readU8(); //这个没有用 var bufferAttribute = this.getStr(this.readU16()); this._shaderAttributes = bufferAttribute.match(this._attrReg); var ibofs = this.readU32(); var ibsize = this.readU32(); var vbIndicesofs = this.readU32(); //Not use? 骨骼索引? var vbIndicessize = this.readU32(); //not use? var vbofs = this.readU32(); var vbsize = this.readU32(); var boneDicofs = this.readU32(); var boneDicsize = this.readU32(); //vertex decl //this._getVertexDeclaration(); var vbStart = vbofs + this.dataoffset; var vbBuff = new Float32Array(this.buff.slice(vbStart, vbStart + vbsize)); //TODO ib 可能不从0开始 //现在是相同格式的合并,但是这样可能会导致错误。 var ibStart = ibofs + this.dataoffset; var ibBuff = new Uint16Array(this.buff.slice(ibStart, ibStart + ibsize)); //bone index var bidBuff = new Uint8Array(this.buff.slice(boneDicofs + this.dataoffset, boneDicofs + this.dataoffset + boneDicsize)); //mesh var sm = this.mesh.submeshes[this._cursubmeshid++] = new Laya_SubMesh(); sm.attribs = bufferAttribute; sm._vertexBuffer = vbBuff; sm._indexBuffer = ibBuff; sm._boneindicesBuffer = bidBuff; } read_DATAAREA() { } getStr(id) { return this._strings[id]; } _getVertexDeclaration() { var position, normal, color, texcoord0, texcoord1, tangent, blendWeight, blendIndex; for (var i = 0; i < this._shaderAttributes.length; i += 8) { switch (this._shaderAttributes[i]) { case "POSITION": position = true; break; case "NORMAL": normal = true; break; case "COLOR": color = true; break; case "UV": texcoord0 = true; break; case "UV1": texcoord1 = true; break; case "BLENDWEIGHT": blendWeight = true; break; case "BLENDINDICES": blendIndex = true; break; case "TANGENT": tangent = true; break; } } //TODO this._shaderAttributes; } } exports.loader_lh = loader_lh; var srcPath = 'E:/layaair/layaair/publish/LayaAirPublish/samples/as/3d/bin/h5/threeDimen/models/test/'; class VertexAttrib { constructor() { this.name = ''; this.size = 0; this.type = 0; this.offset = 0; } } class VertexDeclare { constructor() { this.attribs = []; } getTypeLen(t) { return 4; } getStride() { if (this.attribs.length <= 0) return 0; var lastattr = this.attribs[this.attribs.length - 1]; return lastattr.offset + this.getTypeLen(lastattr.type) * lastattr.size; } getOff(name) { var att = this.attribs.find((v, i) => { return v.name == name; }); return att.offset; } } function parseAttrib(str) { //0 name, 1 size, 2 stide, 3 offset var R = new VertexDeclare(); var ret = R.attribs; var atts = str.split(';'); if (atts.length <= 0) throw 'parse att string error' + str; atts.forEach((v, i) => { if (v.length <= 0) return; var s1 = v.split(':'); if (s1.length < 2) throw 'parse att err1'; var ca = new VertexAttrib(); ca.name = s1[0]; var s2 = s1[1].split(','); if (s2.length < 3) throw 'parse att err2'; ca.size = parseInt(s2[0]); ca.type = parseInt(s2[1]); ca.offset = parseInt(s2[2]); ret.push(ca); }); return R; } function handlelm(mesh, modelcnfg, swapyz) { if (mesh.version == 'LAYAMODEL:02') { return false; } mesh.version = 'LAYAMODEL:02'; mesh.materials.forEach((mtl, i) => { if (modelcnfg && modelcnfg[mesh.materials[i]]) { mesh.materials[i] = modelcnfg[mesh.materials[i]]; } else { mesh.materials[i] = mesh.materials[i].replace('.lmat', '.lpbr'); } console.log(mesh.materials[i]); }); mesh.submeshes.forEach((sm, i) => { var atts = parseAttrib(sm.attribs); console.log(atts); var oldstride = atts.getStride(); var oldfstride = oldstride / 4; if (sm.attribs == 'POSITION:3,32,0;NORMAL:3,32,12;UV:2,32,24;') sm.attribs = 'POSITION:3,56,0;NORMAL:3,56,12;TANGENT:3,56,24;BINORMAL:3,56,36;UV:2,56,48;'; if (sm.attribs == 'POSITION:3,64,0;NORMAL:3,64,12;UV:2,64,24;BLENDWEIGHT:4,64,32;BLENDINDICES:4,64,48;') sm.attribs = 'POSITION:3,88,0;NORMAL:3,88,12;TANGENT:3,88,24;BINORMAL:3,88,36;UV:2,88,48;BLENDWEIGHT:4,88,56;BLENDINDICES:4,88,72;'; //sm.attribs='POSITION:3,32,0;NORMAL:3,32,12;UV:2,32,24;'; var newatts = parseAttrib(sm.attribs); var newstride = newatts.getStride(); var newfstride = newstride / 4; var oldvb = sm._vertexBuffer; var vertnum = sm._vertexBuffer.byteLength / oldstride; var mg = new meshGeo_1.MeshGeo(oldvb, sm._indexBuffer, oldfstride, atts.getOff('UV') / 4, ''); //BBX var minx = 1e6, miny = 1e6, minz = 1e6, maxx = -1e6, maxy = -1e6, maxz = -1e6; for (var 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; } console.log('submesh size:[', minx, miny, minz, maxx, maxy, maxz, ']'); var coldi = 0; //缩放 if (modelcnfg && modelcnfg['scale'] != undefined) { var scale = modelcnfg['scale'] * 1.0; for (var 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 (var 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 var tb = mg.calcTangent(); var vb = sm._vertexBuffer = new Float32Array(vertnum * newfstride); var cnewi = 0; for (var 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++]; } } }); return true; } 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_lh(); cc.mesh = new Laya_Mesh(); var data = fs.readFileSync(file); try { console.log('parse ' + file); cc.parse(data.buffer); } catch (e) { console.log(e); return; } var rfile = path.basename(file); var filepath = path.dirname(file); //材质相关 cc.mesh.materials.forEach((v, i) => { var lmat = filepath + v; var lpbr = filepath + v.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.mesh, modelCnfgObj[rfile], true)) { var save = new Laya_Mesh_W(); save.mesh = cc.mesh; save.saveAsLm(file + '.lm'); } else { } } }, false); } //dowork();