Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #Version 1.2.1
- from inc_noesis import *
- import os
- ## credits :-
- # akderebur and I for researching the format and writing the script
- # rfuzzo for the CP77Tools
- # SurprisedMango, Divined, Yoratoni, ZeRoY, Banishingshift for some help for the tests
- ## WIP cr2w metadata parsing
- bHighestLODOnly = True #if put to True, the low poly meshes won't be loaded
- bLoadRigFile = True #if put to True, enables selecting a paired rig file with the skeleton hierarchy info
- bLoadSeveralRigFiles = False #if put to True, same as above but with several rig files and without validity check
- bParentToRootIfNoParent = False #if put to True, unparented bones will be parented to Root
- matList = []
- texList = []
- txList = []
- miList = []
- mtList = []
- importList = []
- red_info = {}
- red_idx = {}
- cr2w_list = {}
- cr2w_external_list = []
- cr2w_external_data = []
- cr2w_textures = []
- bTextureFileType = "dds"
- cp77_path = "C:\\dev\\cyberpunk\\out\\"
- def registerNoesisTypes():
- handle = noesis.register("CP2077",".mesh;.cookedapp")
- noesis.setHandlerTypeCheck(handle, CheckType)
- noesis.setHandlerLoadModel(handle, LoadModel)
- return 1
- def CheckType(data):
- bs = NoeBitStream(data)
- if len(data) < 16:
- print("Invalid file, too small")
- return 0
- return 1
- def peek(bs,offset,dataType):
- curr_ptr = bs.tell()
- bs.seek(offset)
- if dataType == "UShort":
- val = bs.readUShort()
- bs.seek(curr_ptr)
- return val
- else:
- bs.seek(curr_ptr)
- class CR2WData:
- def __init__(self,srcFile,exports,impList,extList):
- self.name = srcFile
- self.cr2w = []
- self.variables = []
- self.Imports = impList
- self.External = extList
- class CR2WFile:
- def __init__(self, bs,srcFile):
- self.rootPtr = bs.tell()
- self.redfile = srcFile #os.path.basename(rapi.getInputName())
- cr2w_list[self.redfile] = {}
- #cr2w_external_list[self.redfile] = {}
- #print("[CR2W]::"+srcFile)
- cr2w_chunks = findAllFlags(bs, b'\x43\x52\x32\x57', bs.getSize())
- if len(cr2w_chunks) < 1:
- return 1
- idx = 0
- for i in cr2w_chunks:
- bs.seek(i)
- cr2wf = CR2WFile2(bs,srcFile)
- cr2w_list[self.redfile][str(idx)] = []
- #cr2w_external_list[self.redfile][str(idx)] = []
- if len(cr2wf.External) > 0:
- for exfile in cr2wf.External:
- if exfile not in cr2w_external_list:
- cr2w_external_list.append(exfile)
- if len(cr2wf.Imports) > 0:
- for impfile in cr2wf.Imports:
- if impfile not in cr2w_external_list:
- cr2w_external_list.append(impfile)
- if len(cr2wf.ExportInfo) > 0:
- for exp in cr2wf.ExportInfo:
- cr2w_list[self.redfile][str(idx)].append(exp.variables)
- idx += 1
- class CR2WFile2:
- def __init__(self, bs,srcFile):
- #relative offset in current CR2W chunk
- self.redfile = srcFile
- self.rptr = bs.tell()
- self.ptr = bs.tell()
- self.header = CR2WFileHeader(bs)
- self.toc = []
- for i in range(10):
- self.toc.append(CR2WTable(bs,self.rptr))
- self.m_strings = []
- self.str_offsets = {}
- self.Names = []
- self.NHash = {}
- self.Imports = []
- self.ExportInfo = []
- self.Buffers = []
- self.External = []
- self.StringDictionary = {}
- self.str_offsets = {}
- self.str_idx = []
- self.m_strings = {}
- self.import_names = []
- #print("fileheader=====")
- #print(vars(self.header))
- self.readStrings(bs)
- self.readNames(bs)
- self.readImports(bs)
- self.readProps(bs)
- self.readExports(bs)
- self.readBufferInfo(bs)
- self.readBufferData()
- #Table[0]
- def readStrings(self,bs):
- curr_ptr = bs.tell()
- sPtr,size,crc = self.toc[0].info()
- bs.seek(sPtr)
- idx = 0
- self.m_strings = []
- while(bs.tell() < (size + sPtr)):
- str_offset = bs.tell() - sPtr
- self.m_strings.append(bs.readString())
- self.str_offsets[str_offset] = idx
- idx+=1
- return 1
- def strOffset(self,soffset):
- sidx = self.str_offsets[soffset]
- return self.m_strings[sidx]
- #Table[1]
- def readNames(self,bs):
- curr_ptr = bs.tell()
- nPtr,nlen,crc = self.toc[1].info()
- bs.seek(nPtr)
- for idx in range(nlen):
- nval = bs.readUInt()
- nhash = bs.readUInt()
- self.Names.append(self.strOffset(nval))
- #self.nHash.append(nhash)
- #Table[2]
- def readImports(self, bs):
- iPtr,ilen,crc = self.toc[2].info()
- bs.seek(iPtr)
- self.Imports = []
- #self.ImportsInfo = []
- for i in range(ilen):
- impObj = {}
- depotPath = bs.readUInt()
- className = bs.readUShort()
- flags = bs.readUShort()
- self.Imports.append(self.strOffset(depotPath))
- return 1
- #Table[3]
- def readProps(self, bs):
- pPtr,plen,crc = self.toc[3].info()
- bs.seek(pPtr)
- self.Properties = []
- for i in range(plen):
- pObj = {}
- clsName = bs.readUShort()
- pObj["className"] = clsName
- pObj["classFlags"] = bs.readUShort()
- pObj["propertyName"] = bs.readUShort()
- pObj["propertyFlags"] = bs.readUShort()
- pObj["hash"] = bs.readUInt64()
- pObj["value"] = ''
- pObj["REDType"] = self.Names[clsName]
- pObj["REDName"] = self.Names[pObj["propertyName"]]
- self.Properties.append(pObj)
- return 1
- def readREDInfo(self,bs):
- curr_ptr = bs.tell()
- rnidx = bs.readUShort()
- rtidx = bs.readUShort()
- if rnidx >= len(self.Names):
- #print("error "+self.redfile)
- #print("@"+ str(bs.tell()))
- val = "skip"
- return [str(rnidx),str(rtidx),val]
- rName = self.Names[rnidx]
- if rtidx >= len(self.Names):
- #print("error "+self.redfile)
- #print("@"+ str(bs.tell()))
- val = "skip"
- return [rName,str(rtidx),val]
- rType = self.Names[rtidx]
- val = {}
- if rType == "CName":
- ntype = bs.readUInt()
- rval = self.Names[bs.readUShort()]
- return [rName,rType,rval]
- elif rType == "ECookingPlatform":
- dlen = bs.readUInt()
- rVal = self.Names[bs.readUShort()]
- return [rName,rType,rVal]
- elif rType == "array:CName":
- cNameArr = [];
- arrSize = bs.readUInt()
- arrLen = bs.readUInt()
- for i in range(arrLen):
- cNameArr.append(self.Names[bs.readUShort()])
- bs.readUShort()
- return [rName,rType,cNameArr]
- elif rType == "array:EMaterialVertexFactory":
- rArr = [];
- dataPtr = bs.tell()
- dataLen = bs.readUInt()
- arrLen = bs.readUInt()
- for i in range(arrLen):
- rArr.append(self.Names[bs.readUShort()])
- checkPtr = dataPtr + dataLen
- if bs.tell() < checkPtr:
- bs.seek(checkPtr)
- return [rName,rType,rArr]
- elif rType == "Color":
- #dlen = bs.readUInt()
- bs.readUByte()
- col = {}
- xn,xt,xv = self.readREDInfo(bs)
- col[xn] = xv
- xn2,xt2,xv2 = self.readREDInfo(bs)
- col[xn2] = xv2
- xn3,xt3,xv3 = self.readREDInfo(bs)
- col[xn3] = xv3
- xn4,xt4,xv4 = self.readREDInfo(bs)
- col[xn4] = xv4
- bs.readUShort()
- return [rName,rType,col]
- elif rType == "Vector4":
- bs.readUByte()
- vec = {}
- xn,xt,xv = self.readREDInfo(bs)
- vec[xn] = xv
- yn,yt,yv = self.readREDInfo(bs)
- vec[yn] = yv
- zn,zt,zv = self.readREDInfo(bs)
- vec[zn] = zv
- wn,wt,wv = self.readREDInfo(bs)
- vec[wn] = wv
- return [rName,rType,vec]
- elif rType == "Uint8":
- dlen = bs.readUInt()
- rval = bs.readUByte()
- return [rName,rType,rval]
- elif rType == "[3]Uint32":
- dlen = bs.readUInt()
- inum = bs.readUInt()
- rval = []
- for i in range(inum):
- rval.append(bs.readUInt())
- return [rName,rType,rval]
- elif rType == "Float":
- flPtr = bs.tell()
- fflag = bs.readUByte()
- bs.seek(flPtr)
- if fflag == 8:
- dlen = bs.readUInt()
- rval = bs.readFloat()
- else:
- rval = bs.readFloat()
- return [rName,rType,rval]
- elif rType == "Bool":
- dlen = bs.readUInt()
- rval = bs.readUByte()
- return [rName,rType,rval]
- elif rType == "rRef:IMaterial":
- dlen = bs.readUInt()
- bmid = bs.readUShort() - 1
- if len(self.Imports) > bmid:
- rval = self.Imports[bmid]
- self.External.append(rval)
- else:
- rval = bmid + 1
- return [rName,rType,rval]
- elif rType == "rRef:ITexture":
- texFile = bs.readUShort()
- rval = self.Imports[texFile-1]
- self.External.append(rval)
- return [rName,rType,rval]
- elif rType == "rRef:CHairProfile":
- texFile = bs.readUShort()
- rval = self.Imports[texFile-1]
- self.External.append(rval)
- return [rName,rType,rval]
- elif rType == "rRef:Multilayer_Mask":
- texFile = bs.readUShort()
- rval = self.Imports[texFile-1]
- self.External.append(rval)
- return [rName,rType,rval]
- elif rType == "rRef:Multilayer_Setup":
- texFile = bs.readUShort()
- rval = self.Imports[texFile-1]
- self.External.append(rval)
- return [rName,rType,rval]
- else:
- #skip = bs.tell()
- #val = bs.readUInt()
- #skip = skip + val
- #if val > 4:
- # bs.seek(skip)
- val = "skip"
- return [rName,rType,val]
- def readExportInfo(self,bs,offset,size,redType):
- curr_ptr = bs.tell()
- xPtr = offset
- endPtr = xPtr + size
- bs.seek(xPtr + 1)
- cVars = {}
- if redType == "meshMeshAppearance":
- xn,xt,xv = self.readREDInfo(bs)
- if xt == "array:CName":
- cVars[xn] = xv
- if bs.tell() < (endPtr - 4):
- xn2,xt2,xv2 = self.readREDInfo(bs)
- cVars[xn2] = xv2
- elif xt == "CName":
- cVars[xn] = xv
- if bs.tell() < (endPtr - 4):
- xn2,xt2,xv2 = self.readREDInfo(bs)
- cVars[xn2] = xv2
- else:
- cVars[xn] = xv
- elif redType == "CMaterialInstance":
- mi = {}
- peekVal = peek(bs,bs.tell(),"UShort")
- while peekVal > 0:
- xn,xt,xv = self.readREDInfo(bs)
- mi[xn] = xv
- peekVal = peek(bs,bs.tell(),"UShort")
- #print(mi)
- skip = bs.readUShort()
- num_entries = bs.readUInt()
- for i in range(num_entries):
- dataPtr = bs.tell()
- dataLen = bs.readUInt()
- xn,xt,xv = self.readREDInfo(bs)
- mi[xn] = xv
- #print(str(i)+":: "+xn+" :: "+xt+" @ "+str(dataPtr)+" -> "+str(dataLen))
- checkPtr = dataPtr + dataLen
- if bs.tell() < checkPtr:
- bs.seek(checkPtr)
- if dataLen > size:
- cVars[redType] = mi
- return cVars
- if xv == "skip" and dataLen > 8:
- bs.seek(dataPtr + dataLen)
- cVars[redType] = mi
- else:
- xn,xt,xv = self.readREDInfo(bs)
- cVars[xn+"_"+xt] = xv
- bs.seek(curr_ptr);
- return cVars
- #Table[4]
- def readExports(self, bs):
- curr_ptr = bs.tell()
- xPtr,xlen,crc = self.toc[4].info()
- bs.seek(xPtr)
- self.Chunks = []
- self.ExportInfo = []
- if xlen < 1:
- return 1
- for i in range(xlen):
- ex = CR2WExport(bs,i)
- ex.REDType = self.Names[ex.className]
- ex.dataOffset += self.rptr
- self.ExportInfo.append(ex)
- for x in range(xlen):
- exp = self.ExportInfo[x]
- #print("[EXPORT]::"+exp.REDType)
- exData = self.readExportInfo(bs,exp.dataOffset,exp.dataSize,exp.REDType)
- #print(exData)
- self.ExportInfo[x].variables = exData
- return 1
- #Table[5]
- def readBufferInfo(self, bs):
- curr_ptr = bs.tell()
- bPtr,blen,crc = self.toc[5].info()
- bs.seek(bPtr)
- self.Buffers = []
- if blen < 1:
- return 1
- for i in range(blen):
- bf = CR2WBuffer(bs)
- self.Buffers.append(bf)
- def readBufferData(self):
- if len(self.Buffers) < 1:
- return 1
- fd = os.path.dirname(rapi.getInputName())
- fn = os.path.basename(rapi.getInputName())
- bpath = fd + "\\" + fn + ".0.buffer"
- if (rapi.checkFileExists(bpath)):
- bs2 = NoeBitStream(rapi.loadIntoByteArray(bpath))
- bs2.seek(0x6,1)
- if bs2.readUShort()==0x7FFF:
- return 1
- else:
- bs2.seek(0)
- cr2wtmp2 = CR2WFile(bs2,bpath)
- return 1
- class CR2WTable:
- def __init__(self, bs,rel):
- self.offset = bs.readUInt() + rel
- self.len = bs.readUInt()
- self.crc32 = bs.readUInt()
- def info(self):
- return [self.offset,self.len,self.crc32]
- class CR2WFileHeader:
- def __init__(self, bs):
- self.magic = bs.readUInt()
- self.version = bs.readUInt()
- self.flags = bs.readUInt()
- self.timeStamp = bs.readUInt64()
- self.buildVersion = bs.readUInt()
- self.fileSize = bs.readUInt()
- self.bufferSize = bs.readUInt()
- self.crc32 = bs.readUInt()
- self.numChunks = bs.readUInt()
- class CR2WExport:
- def __init__(self, bs,chunkIndex):
- self.REDType = ""
- self.REDName = ""# redtyoe + str(ChunkIndex)
- self.className = bs.readUShort()
- self.objectFlags= bs.readUShort()
- self.parentID = bs.readUInt()
- self.dataSize = bs.readUInt()
- self.dataOffset = bs.readUInt()
- self.template = bs.readUInt()
- self.crc32 = bs.readUInt()
- self.ChunkIndex = chunkIndex + 1
- self.variables = {}
- self.data = []
- #self.data"] = self.readExportInfo(bs,exObj["dataOffset"],exObj["dataSize"],exObj["REDType"])
- class CR2WBuffer:
- def __init__(self, bs):
- self.flags = bs.readUInt()
- self.index= bs.readUInt()
- self.offset = bs.readUInt()
- self.diskSize = bs.readUInt()
- self.memSize = bs.readUInt()
- self.crc32 = bs.readUInt()
- self.data = []
- self.data = []
- def CR2WDump():
- for extfile in cr2w_external_list:
- ipath = cp77_path + extfile
- iext = os.path.splitext(ipath)[1]
- iname = os.path.splitext(ipath)[0]
- if iext == ".xbm":
- cr2w_textures.append(extfile)
- if (rapi.checkFileExists(ipath)):
- txList.append(ipath)
- else: #if iext == ".mi":
- cr2w_external_data.append(extfile)
- if (rapi.checkFileExists(ipath)):
- bs = NoeBitStream(rapi.loadIntoByteArray(ipath))
- cr2w_data = CR2WFile(bs,ipath)
- #else:
- # cr2w_external_data.append(extfile)
- def LoadMats(mesh_name):
- #thisName = rapi.getLocalFileName(rapi.getInputName())
- fd = os.path.dirname(rapi.getInputName())
- fn = os.path.basename(rapi.getInputName())
- tp = fd + "\\textures\\" + fn.split(".")[0] + "_"
- diffuseTex = tp + "d01." + bTextureFileType
- normalTex = tp + "n01." + bTextureFileType
- material = NoeMaterial(mesh_name, "")
- if (rapi.checkFileExists(diffuseTex)):
- material.setTexture(diffuseTex)
- if (rapi.checkFileExists(normalTex)):
- material.setNormalTexture(normalTex)
- rapi.rpgSetMaterial(mesh_name)
- matList.append(material)
- return 1
- def buildFlagFromNames(names, nameToIndex, padding):
- flag = bytes()
- for name in names:
- flag += (nameToIndex[name]).to_bytes(2,byteorder='little')
- for i in range(padding):
- flag += b'\x00'
- return flag
- def findAllFlags(bs, flag, maxOffset):
- a=True
- found = []
- endPtr = maxOffset - (len(flag)+1)
- while a and bs.tell() < endPtr:
- checkPoint = bs.tell()
- temp = bs.readBytes(len(flag))
- if temp == flag:
- found.append(checkPoint)
- #bs.seek(checkPoint)
- #a = False
- #return True
- else :
- bs.seek(checkPoint+1)
- return found
- def findFlag(bs, flag, maxOffset):
- a=True
- while a and bs.tell() < maxOffset -1:
- checkPoint = bs.tell()
- temp = bs.readBytes(len(flag))
- if temp == flag:
- bs.seek(checkPoint)
- a = False
- return True
- else :
- bs.seek(checkPoint+1)
- return False
- def ParseChunkHeader(bs):
- optr = bs.tell()
- magic = bs.readUInt()
- #print("--MAGIC--"+str(magic))
- version = bs.readUInt()
- bs.readBytes(16)
- dataOffset = bs.readUInt() + optr
- maxOffset = dataOffset
- bs.readBytes(12)
- stringSectionOffset = bs.readUInt() + optr
- stringSectionSize = bs.readUInt()
- bs.readBytes(4)
- stringSectionEndOffset = bs.readUInt() + optr
- hashTableEntryCount = bs.readUInt()
- bs.readBytes(4)
- #hashTableEndOffset = bs.readUInt()
- importSectionOffset = bs.readUInt() + optr
- importCount = bs.readUInt()
- #bs.readBytes(4)
- #unkStruct1SectionEndOffset = bs.readUInt()
- #unkStruct2Count = bs.readUInt()
- #bs.readBytes(4)
- #unkStruct2SectionEndOffset = bs.readUInt()
- #unkStruct3Count = bs.readUInt()
- #bs.readBytes(4)
- #unkStruct3SectionEndOffset = bs.readUInt()
- #unkStruct4Count = bs.readUInt()
- #bs.readBytes(4)
- #unkStruct4SectionEndOffset = bs.readUInt()
- #unkStruct5Count = bs.readUInt()
- #bs.readBytes(4)
- bs.seek(stringSectionOffset)
- #grab names
- indexToName = []
- strOffset = {}
- implist = []
- nameToIndex = {}
- index = 0
- #print("----strings @ "+str(stringSectionOffset))
- while(bs.tell() < stringSectionEndOffset):
- strIdx = bs.tell() - stringSectionOffset
- name = bs.readString()
- #print(name)
- indexToName.append(name)
- nameToIndex[name] = index
- strOffset[strIdx] = name
- index+=1
- bs.seek(importSectionOffset)
- for i in range(importCount):
- imp = strOffset[bs.readUInt()]
- if imp not in importList:
- importList.append(imp)
- implist.append(imp)
- bs.readUInt()
- bs.seek(optr)
- #print("----imports----")
- #print(implist)
- #print(strOffset)
- #print(indexToName)
- #bs.seek(unkStruct4SectionEndOffset + unkStruct5Count * 16)
- #indexToName, nameToIndex, maxOffset = ParseHeader(bs)
- return [strOffset, implist, indexToName, nameToIndex, maxOffset]
- def ParseHeader(bs):
- magic = bs.readUInt()
- version = bs.readUInt()
- bs.readBytes(16)
- dataOffset = bs.readUInt()
- maxOffset = dataOffset
- bs.readBytes(12)
- stringSectionOffset = bs.readUInt()
- stringSectionSize = bs.readUInt()
- bs.readBytes(4)
- stringSectionEndOffset = bs.readUInt()
- hashTableEntryCount = bs.readUInt()
- bs.readBytes(4)
- #hashTableEndOffset = bs.readUInt()
- importSectionOffset = bs.readUInt()
- importCount = bs.readUInt()
- #unkStruct1Count = bs.readUInt()
- bs.readBytes(4)
- unkStruct1SectionEndOffset = bs.readUInt()
- unkStruct2Count = bs.readUInt()
- bs.readBytes(4)
- unkStruct2SectionEndOffset = bs.readUInt()
- unkStruct3Count = bs.readUInt()
- bs.readBytes(4)
- unkStruct3SectionEndOffset = bs.readUInt()
- unkStruct4Count = bs.readUInt()
- bs.readBytes(4)
- unkStruct4SectionEndOffset = bs.readUInt()
- unkStruct5Count = bs.readUInt()
- bs.readBytes(4)
- bs.seek(stringSectionOffset)
- #grab names
- indexToName = []
- nameToIndex = {}
- strOffset = {}
- index = 0
- while(bs.tell() < stringSectionEndOffset):
- strIdx = bs.tell() - stringSectionOffset
- name = bs.readString()
- indexToName.append(name)
- nameToIndex[name] = index
- strOffset[strIdx] = name
- index+=1
- bs.seek(importSectionOffset)
- for i in range(importCount):
- importList.append(strOffset[bs.readUInt()])
- bs.readUInt()
- print("===root imports===")
- print(importList)
- bs.seek(unkStruct4SectionEndOffset + unkStruct5Count * 16)
- return [indexToName, nameToIndex, maxOffset]
- def LoadRig(br, meshBones, bindMatrices):
- indexToName, nameToIndex, maxOffset = ParseHeader(br)
- # Read bone names
- bNameFlag = buildFlagFromNames(["boneNames","array:CName"],nameToIndex,0)
- rigBones = []
- if findFlag(br, bNameFlag, maxOffset):
- br.seek(0x8,1)
- boneC = br.readInt()
- for i in range(boneC):
- rigBones.append(indexToName[br.readUShort()])
- bfail = False
- #print(rigBones)
- #print("----")
- # Verify rig
- for boneN in meshBones:
- if boneN not in rigBones:
- print(boneN)
- bfail=True
- if bfail:
- return [[], []]
- # Parenting info
- findFlag(br, b'\xFF\xFF\x00\x00', br.getSize())
- parIds = []
- for b in range(boneC):
- parIds.append(br.readShort())
- bones = []
- for b in range(boneC):
- boneName = rigBones[b]
- bonePos = NoeVec3.fromBytes(br.readBytes(12))
- bonePos = NoeVec3([bonePos.vec3[0], bonePos.vec3[1], bonePos.vec3[2]])
- br.readFloat()
- boneRot = NoeQuat.fromBytes(br.readBytes(16)).transpose()
- boneScl = NoeQuat.fromBytes(br.readBytes(16))
- if boneName in meshBones: # Set bind matrix
- bInd = meshBones.index(boneName)
- matrix = NoeMat44(bindMatrices[bInd]).toMat43().inverse()
- else:
- matrix = boneRot.toMat43()
- matrix[3] = bonePos
- if b:
- matrix*=bones[parIds[b]].getMatrix()
- bones.append(NoeBone(b, rigBones[b], matrix, None, parIds[b]))
- # Return bones and name list (for easy remap)
- return [bones, rigBones]
- def LoadRig2(br, meshBones, boneNameToParentName):
- stringIndexToName, nameToStringIndex, maxOffset = ParseHeader(br)
- checkPoint = br.tell()
- # Read bone names
- bNameFlag = buildFlagFromNames(["boneNames","array:CName"],nameToStringIndex,0)
- boneNamesToIndex = {}
- IndexToRigBoneName = []
- names = []
- if findFlag(br, bNameFlag, maxOffset):
- br.seek(0x8,1)
- boneC = br.readInt()
- for i in range(boneC):
- index = br.readUShort()
- IndexToRigBoneName.append(stringIndexToName[index])
- boneNamesToIndex[stringIndexToName[index]] = i
- names.append(stringIndexToName[index])
- # Parenting info
- findFlag(br, b'\xFF\xFF\x00\x00', br.getSize())
- parIds = []
- for b in range(boneC):
- parIds.append(br.readShort())
- for name in names:
- b = boneNamesToIndex[name]
- if name not in boneNameToParentName:
- if parIds[b] >=0 and IndexToRigBoneName[parIds[b]] in meshBones:
- boneNameToParentName[name] = IndexToRigBoneName[parIds[b]]
- else:
- if bParentToRootIfNoParent:
- boneNameToParentName[name] = "Root"
- else:
- boneNameToParentName[name] = ""
- missingParent = []
- for name in meshBones:
- if name not in boneNameToParentName:
- missingParent.append(name)
- if missingParent:
- print("Bones without parenting info left :")
- for name in missingParent:
- print(name)
- return True
- else:
- print("All bones have parenting info, proceeding")
- return False
- def LoadModel(data, mdlList):
- ctx = rapi.rpgCreateContext()
- bs = NoeBitStream(data)
- indexToName, nameToIndex, maxOffset = ParseHeader(bs)
- checkPoint = bs.tell()
- bs.seek(0)
- cr2wtmp = CR2WFile(bs,os.path.basename(rapi.getInputName()))
- bs.seek(checkPoint)
- noesis.logPopup()
- #Quantization info
- quantScaleFlag = buildFlagFromNames(["quantizationScale","Vector4"],nameToIndex,0)
- if not findFlag(bs, quantScaleFlag, maxOffset):
- print("No quantization scale found")
- return 0
- bs.seek(0x9,1)
- qScale = []
- for i in range(4):
- bs.seek(0x8,1)
- qScale.append(bs.readFloat())
- bs.seek(checkPoint)
- quantOffFlag = buildFlagFromNames(["quantizationOffset","Vector4"],nameToIndex,0)
- if not findFlag(bs, quantOffFlag, maxOffset):
- print("No quantization offset found")
- return 0
- bs.seek(0x9,1)
- qOff = []
- for i in range(4):
- bs.seek(0x8,1)
- qOff.append(bs.readFloat())
- bs.seek(checkPoint)
- qMat = NoeMat43()
- for i in range(3):
- qMat[i][i] = qScale[i]
- qMat[3] = NoeVec3(qOff[:-1])
- #get BBox info, not necessary though since scaling is done by the quantizationScale
- boxFlag = buildFlagFromNames(["boundingBox","Box"],nameToIndex,0)
- if not findFlag(bs, boxFlag, maxOffset):
- print("No BBox")
- return 0
- bs.seek(0x12,1)
- minBBox = []
- for i in range(4):
- bs.seek(0x8,1)
- minBBox.append(bs.readFloat())
- bs.seek(0xB,1)
- maxBBox = []
- for i in range(4):
- bs.seek(0x8,1)
- maxBBox.append(bs.readFloat())
- checkPoint = bs.tell()
- #get different vert/idx counts
- vCounts = []
- idxCounts = []
- vertDefs = []
- posFlag = buildFlagFromNames(["numVertices","Uint16"],nameToIndex,0)
- while findFlag(bs, posFlag, maxOffset):
- bs.seek(8,1)
- vCounts.append(bs.readUShort())
- bs.seek(8,1)
- idxCounts.append(bs.readUInt())
- submeshCount = len(idxCounts)
- bs.seek(checkPoint)
- #Grab bones if relevant
- boneNames=["Root"]
- glBoneNames=[]
- boneNameToParentName={}
- boneNameToParentName["Root"] = ""
- bones = []
- bMap = []
- boneLoadLoop = True if bLoadRigFile else False
- bSkeletonBuilt = False
- if "boneRigMatrices" in nameToIndex:
- bs.seek(checkPoint)
- boneFlags = buildFlagFromNames(["boneRigMatrices","array:Matrix"],nameToIndex,0)
- if findFlag(bs, boneFlags, maxOffset):
- bs.seek(0x4,1)
- sectionSize = bs.readUInt()
- boneCount = int(sectionSize/239) #Can be grabbed at the beginning too
- for i in range(boneCount):
- boneNames.append(indexToName[nameToIndex["boneNames"] + i + 1])
- lst = [[NoeVec4([1,0,0,0]), NoeVec4([0,1,0,0]), NoeVec4([0,0,1,0]), NoeVec4([0,0,0,1])]] #Root placeholder
- for i in range(boneCount):
- bs.seek(3,1)
- v = []
- for j in range(4):
- bs.seek(11,1)
- for k in range(4):
- bs.seek(8,1)
- v.append(bs.readFloat())
- lst.append([NoeVec4(v[4*u:4*u+4]) for u in range(4)])
- while boneLoadLoop:
- rigData = rapi.loadPairedFileOptional("rig file", ".rig")
- if rigData is not None:
- if bLoadSeveralRigFiles:
- br = NoeBitStream(rigData)
- if not LoadRig2(br, boneNames, boneNameToParentName):
- boneLoadLoop = False
- else:
- br = NoeBitStream(rigData)
- bones, glBoneNames = LoadRig(br, boneNames, lst)
- if bones:
- boneLoadLoop = False
- bSkeletonBuilt = True
- else:
- print("Invalid rig file, choose another one")
- else:
- boneLoadLoop = False
- if not bSkeletonBuilt:
- for i,l in enumerate(lst):
- matrix = NoeMat44(l).toMat43().inverse()
- if boneNames[i] in boneNameToParentName:
- bone = NoeBone(i, boneNames[i], matrix, boneNameToParentName[boneNames[i]], 0)
- else:
- if i != 0 and bParentToRootIfNoParent:
- bone = NoeBone(i, boneNames[i], matrix, None, 0)
- else:
- bone = NoeBone(i, boneNames[i], matrix, None, -1)
- bones.append(bone)
- bMap.append(i+1)
- else:
- for i in range(len(boneNames)):
- bMap.append(glBoneNames.index(boneNames[i]))
- bs.seek(checkPoint)
- # Index offsets for multiple meshes
- indOffs = []
- indOffs.append(0)
- if "teOffset" in nameToIndex:
- indOffFlag = buildFlagFromNames(["teOffset","Uint32"],nameToIndex,0)
- while findFlag(bs, indOffFlag, maxOffset):
- bs.seek(8, 1)
- indOffs.append(bs.readUInt())
- bs.seek(checkPoint)
- # Vertex Component Definitions
- vDefFlag = buildFlagFromNames(["vertexLayout","GpuWrapApiVertexLayoutDesc"],nameToIndex,0)
- uvSeen = 0
- while findFlag(bs, vDefFlag, maxOffset):
- bs.seek(17, 1)
- compC = bs.readInt()
- bs.seek(1, 1)
- vertDef = []
- for c in range(compC):
- bs.seek(8, 1)
- compTInd = bs.readUShort()
- bs.seek(8, 1)
- compNInd = bs.readUShort()
- if compTInd >= len(indexToName) or compNInd >= len(indexToName):
- break
- compType = indexToName[compTInd]
- compName = indexToName[compNInd]
- if compName == "PS_TexCoord":
- uvSeen = 1
- if uvSeen == 1:
- uvSeen = 0
- vertDef.append([compName, compType])
- # Skip stream usage/type/index for now
- testName = indexToName[bs.readUShort()]
- if testName == "streamIndex" or testName == "usageIndex":
- bs.seek(7, 1)
- testName = indexToName[bs.readUShort()]
- if testName == "streamIndex":
- bs.seek(7, 1)
- testName = indexToName[bs.readUShort()]
- if testName == "streamType":
- bs.seek(11, 1)
- else:
- bs.seek(1, 1)
- elif testName == "streamType":
- bs.seek(11, 1)
- else:
- bs.seek(1, 1)
- else:
- bs.seek(1, 1)
- if compName == "PS_DestructionIndices":
- bs.seek(22, 1)
- vertDefs.append(vertDef)
- bs.seek(checkPoint)
- # Offsets for where vertex components start
- # Some components are grouped and only 1 offset for them
- cmpOffFlag = buildFlagFromNames(["byteOffsets","static:5,Uint32"],nameToIndex,0)
- # Vertex component offsets
- vCompOffs = []
- while findFlag(bs, cmpOffFlag, maxOffset):
- bs.seek(8, 1)
- offC = bs.readInt()
- offs = []
- for a in range(offC):
- offs.append(bs.readInt())
- vCompOffs.append(offs)
- bs.seek(checkPoint)
- #Get index section offset, not necessary but better to have in case something wrong happens with align and stuff
- indexOfsFlag = buildFlagFromNames(["indexBufferOffset","Uint32"],nameToIndex,0)
- if not findFlag(bs, indexOfsFlag, maxOffset):
- print("couldn't find index offset")
- return 0
- bs.seek(8,1)
- idxOffset = bs.readUInt()
- bs.seek(checkPoint)
- #Grab LOD info
- lodInfo = []
- if bHighestLODOnly:
- LODInfoFlag = buildFlagFromNames(["lodMask","Uint8"],nameToIndex,0)
- while findFlag(bs, LODInfoFlag, maxOffset):
- bs.seek(8, 1)
- lodInfo.append(bs.readUByte())
- #Grab correct paired buffer file
- bBufferDetected = False
- thisName = rapi.getLocalFileName(rapi.getInputName())
- dir = os.path.dirname(rapi.getInputName())
- for root, dirs, files in os.walk(dir):
- for fileName in files:
- lowerName = fileName.lower()
- if lowerName.endswith(".buffer") and lowerName.split(".")[0] == "".join(thisName.split(".")[:-1]):
- bufferPath = os.path.join(root, fileName)
- if (rapi.checkFileExists(bufferPath)):
- bs2 = NoeBitStream(rapi.loadIntoByteArray(bufferPath))
- bs2.seek(0x6,1)
- if bs2.readUShort()==0x7FFF:
- bs = NoeBitStream(rapi.loadIntoByteArray(bufferPath))
- print("Detected : " + lowerName)
- bBufferDetected = True
- break
- #else:
- #bs2.seek(0)
- #cr2wtmp2 = CR2WFile(bs2)
- if not bBufferDetected:
- print("Couldn't find a suitable buffer file")
- return 0
- #$## parse imports/external list ??
- #CR2WDump()
- #print(" ")
- #print("==========cr2w_external_list=============")
- #print(cr2w_external_data)
- #print("--------------------")
- #print(" ")
- #print("==========cr2w_textures=============")
- #print(cr2w_textures)
- #print(" ")
- #print("==========cr2w_list=============")
- #!!! need to split string by 0x4000,
- #print(cr2w_list)
- #print("--------------------")
- #Parsing semantics
- posBuffers = []
- posBuffers2 = []
- uvBuffers = []
- normBuffers = []
- posBStrides = []
- vDefInd = 0
- for i,vc in enumerate(vCounts):
- if i >= len(indOffs):#breaks, avoid issue with shared buffs used on a few meshes. May need to use streamIndex info for these
- break
- if bHighestLODOnly and i < len(lodInfo):
- if lodInfo[i] > 1:
- break
- vertDef = vertDefs[vDefInd]
- vCompOff = vCompOffs[vDefInd]
- vDefInd = vDefInd + 1
- skinBICount = 0
- skinBWCount = 0
- posBStride = 8
- for comp in vertDef:
- if comp[0] == "PS_SkinIndices":
- skinBICount +=1
- posBStride+=4
- elif comp[0] == "PS_SkinWeights":
- skinBWCount +=1
- posBStride+=4
- elif comp[0] == "PS_ExtraData":
- posBStride+=8
- # Normally need to make a reader that can read any data type
- # But I will just assume each component will use the same type every time
- uvAdded = 0
- if(vCompOff[0] < bs.getSize()):
- bs.seek(vCompOff[0])
- else:
- print("Error, wrong buffer file used, try to rename the mesh and the .buffer")
- return 0
- for comp in vertDef:
- #positions
- start = bs.tell()
- if comp[0] == "PS_Position":
- buffer = bs.readBytes(vc*(posBStride))
- rapi.rpgBindPositionBuffer(buffer,noesis.RPGEODATA_SHORT, posBStride)
- if(skinBICount)>0: #assuming that BI count always equal as BW count, so assuming no weird stuff
- rapi.rpgBindBoneIndexBufferOfs(buffer, noesis.RPGEODATA_UBYTE, posBStride, 8, 4 * skinBICount)
- rapi.rpgBindBoneWeightBufferOfs(buffer, noesis.RPGEODATA_UBYTE, posBStride, 8 + 4*skinBICount, 4 * skinBWCount)
- elif comp[0] == "PS_TexCoord": # Can be multiple
- if uvAdded == 0: #prevent UV2 layer messing up
- if(vCompOff[1] < bs.getSize()):
- bs.seek(vCompOff[1])
- else:
- print("Error, wrong buffer file used, try to rename the mesh and the .buffer")
- return 0
- rapi.rpgBindUV1Buffer(bs.readBytes(vc*4), noesis.RPGEODATA_HALFFLOAT, 0x4)
- elif uvAdded == 1:
- if(vCompOff[3] < bs.getSize()):
- bs.seek(vCompOff[3])
- else:
- print("Error, wrong buffer file used, try to rename the mesh and the .buffer")
- return 0
- rapi.rpgBindUV2BufferOfs(bs.readBytes(vc*8), noesis.RPGEODATA_HALFFLOAT, 0x8, 0x4)
- uvAdded = uvAdded + 1
- elif comp[0] == "PS_Normal": #more research needed
- if(vCompOff[2] < bs.getSize()):
- bs.seek(vCompOff[2])
- else:
- print("Error, wrong buffer file used, try to rename the mesh and the .buffer")
- return 0
- buff = rapi.decodeNormals32(bs.readBytes(vc*8),8, 10, 10, 10,0)
- rapi.rpgBindNormalBuffer(buff, noesis.RPGEODATA_FLOAT, 0xC)
- #elif comp[0] == "PS_Tangent": #seem interleaved with normals
- # normBuffers.append(bs.readBytes(vc*4)) #PT_Dec4
- # elif comp[0] == "PS_Color":
- # bs.seek(vCompOff[3])
- # bs.seek(vc * 4,1) #PT_Color
- else:
- continue
- #grab indices, commit, clear buffers
- bs.seek(idxOffset + indOffs[i])
- rapi.rpgSetName("submesh"+str(i))
- rapi.rpgSetTransform(qMat)
- if bMap:
- rapi.rpgSetBoneMap(bMap)
- # Hacky Method of Loading Tex
- LoadMats("submesh"+str(i))
- rapi.rpgCommitTriangles(bs.readBytes(idxCounts[i]*2), noesis.RPGEODATA_USHORT, idxCounts[i], noesis.RPGEO_TRIANGLE)
- rapi.rpgClearBufferBinds()
- rapi.rpgSetOption(noesis.RPGOPT_TRIWINDBACKWARD,1)
- try:
- mdl = rapi.rpgConstructModel()
- except:
- mdl = NoeModel()
- if(bones):
- mdl.setBones(bones)
- mdl.setModelMaterials(NoeModelMaterials(texList, matList))
- mdlList.append(mdl)
- return 1
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement