UNPKG

pbrtools

Version:
621 lines (570 loc) 21.5 kB
import { MeshGeo } from './meshGeo'; import { BufferReader,BufferWriter } from './buffer'; import {allfiles } from './util' import * as fs from 'fs'; import * as path from 'path'; class Laya_SubMesh{ attribs='POSITION:3,32,0;NORMAL:3,32,12;UV:2,32,24;' _vertexBuffer:Float32Array; _indexBuffer:Uint16Array; _boneindicesBuffer:Uint8Array;//uint8 } class Laya_Mesh{ version='LAYASKINANI:01'; name='MESH'; materials:string[]=[]; //只用字符串表示对应的材质文件或者材质id。有几个就表示有几个submesh _bindPoses:Float32Array;//Matrix44[] _inverseBindPoses:Float32Array;//Matrix44[] submeshes:Laya_SubMesh[]=[]; } class _submeshInfo{ iboff=0; ibsize=0; vboff=0; vbsize=0; boneidxoff=0; boneidxsize=0; } export class Laya_Mesh_W extends BufferWriter{ mesh:Laya_Mesh; _strmap:string[]=['BLOCK', 'DATA', 'STRINGS']; dataOff=0; dataSize=0; strsOff=0; strsNum=0; // bindPoseOff=0; bindPoseSize=0; invGBindePoseOff=0; invGBindePoseSize=0; submeshInfo:_submeshInfo[]=[]; constructor(){ super(); } /** * 返回写了多大。 */ outobj(obj,members:string,buff:ArrayBuffer,off:number):number{ 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:string[]){ } saveAsLm(file:string){ 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:string):Laya_Mesh_W{//先转成id var i = this._strmap.indexOf(str); if(i==-1){ i=this._strmap.length; this._strmap.push(str); } this.wu16(i); return this; } } /** * 问题:现在的格式无法略过不认识的块。 */ export class loader_lh extends BufferReader{ _strings = ['BLOCK', 'DATA', 'STRINGS']; //初始值。read_STRINGS后会被替换 _funcs = [null,this.read_DATA.bind(this),this.read_STRINGS.bind(this)]; blockCount=0; dataoffset=0; datasize=0; _attrReg = new RegExp("(\\w+)|([:,;])", "g"); _shaderAttributes:string[]; mesh:Laya_Mesh; _cursubmeshid=0; constructor(){ super(); } load(str:string,mesh:Laya_Mesh){ this.mesh=mesh; } parse(data:ArrayBuffer):any{ 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){//strings 特殊 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); //break; } } 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:number):string{ return this._strings[id]; } _getVertexDeclaration(){ var position:boolean, normal:boolean, color:boolean, texcoord0:boolean, texcoord1:boolean, tangent:boolean, blendWeight:boolean, blendIndex:boolean; 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; } } var srcPath = 'E:/layaair/layaair/publish/LayaAirPublish/samples/as/3d/bin/h5/threeDimen/models/test/'; class VertexAttrib{ name=''; size=0; type=0; offset=0; } class VertexDeclare{ attribs:VertexAttrib[]=[]; getTypeLen(t:number):number{ 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:string):number{ var att = this.attribs.find((v,i)=>{return v.name==name}); return att.offset; } } function parseAttrib(str:string):VertexDeclare{ //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:Laya_Mesh,modelcnfg:Object,swapyz:boolean):boolean{ 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(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'); } 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')) as Object; 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();