from inc_noesis import * import noesis import rapi from struct import * import re import codecs def registerNoesisTypes(): '''Register the plugin. Just change the Game name and extension.''' handle = noesis.register("Metasequoia", ".mqo") noesis.setHandlerTypeCheck(handle, noepyCheckType) noesis.setHandlerLoadModel(handle, noepyLoadModel) return 1 def noepyCheckType(data): '''Verify that the format is supported by this plugin. Default yes''' return 1 def noepyLoadModel(data, mdlList): '''Build the model, set materials, bones, and animations. You do not need all of them as long as they are empty lists (they are by default)''' ctx = rapi.rpgCreateContext() inputName = rapi.getInputName() with codecs.open(inputName, 'rb', encoding="Shift-JIS") as file: parser = SanaeParser(file) parser.parse_file() #parser = SanaeParser(data) #parser.parse_file() mdl = rapi.rpgConstructModel() mdl.setModelMaterials(NoeModelMaterials(parser.texList, parser.matList)) mdl.setBones(parser.boneList) mdl.setAnims(parser.animList) mdlList.append(mdl) return 1 class SanaeParser(object): def __init__(self, data): '''Initialize some data. Refer to Sanae.py to see what is already initialized''' #self.inFile = NoeBitStream(data) self.inFile = data self.animList = [] self.texList = [] self.matList = [] self.boneList = [] rapi.rpgSetOption(noesis.RPGOPT_TRIWINDBACKWARD, 1) def split_field(self, field): '''Given a field of the form "name(values)", return a list of the form ["name", "values"]''' match = re.match(r'(\w[\w\d_]*)\((.*)\)$', field) return list(match.groups()) if match else [field] def tokenize(self, line): return re.split(r'\s+(?=[^()]*(?:\(|$))', line) def skip_lines(self, string): '''Skip lines until it starts with string''' line = self.inFile.readline() while not line.strip().startswith(string): line = self.inFile.readline() return line def build_mesh_tris(self, vertList, triBuffs): #iterate over all of the idxBuffs based on matNum for matNum in triBuffs: numIdx = 0 idxBuff = bytes() vertBuff = bytes() idxList = triBuffs[matNum]["idxList"] uvBuff = triBuffs[matNum]["uvBuff"] for index in idxList: coords = vertList[index] vertBuff += struct.pack("3f", *(map(float, coords))) idxBuff += struct.pack("L", numIdx) numIdx += 1 matName = self.matList[matNum].name rapi.rpgSetMaterial(matName) rapi.rpgBindPositionBuffer(vertBuff, noesis.RPGEODATA_FLOAT, 12) if uvBuff: rapi.rpgBindUV1Buffer(uvBuff, noesis.RPGEODATA_FLOAT, 8) rapi.rpgCommitTriangles(idxBuff, noesis.RPGEODATA_UINT, numIdx, noesis.RPGEO_TRIANGLE, 1) def build_mesh_quads(self, vertList, quadBuffs): for matNum in quadBuffs: numIdx = 0 idxBuff = bytes() vertBuff = bytes() idxList = quadBuffs[matNum]["idxList"] uvBuff = quadBuffs[matNum]["uvBuff"] for index in idxList: coords = vertList[index] vertBuff += struct.pack("3f", *(map(float, coords))) idxBuff += struct.pack("L", numIdx) numIdx += 1 matName = self.matList[matNum].name rapi.rpgSetMaterial(matName) rapi.rpgBindPositionBuffer(vertBuff, noesis.RPGEODATA_FLOAT, 12) if uvBuff: rapi.rpgBindUV1Buffer(uvBuff, noesis.RPGEODATA_FLOAT, 8) rapi.rpgCommitTriangles(idxBuff, noesis.RPGEODATA_UINT, numIdx, noesis.RPGEO_QUAD, 1) def parse_materials(self, numMat): for i in range(numMat): line = self.tokenize(self.inFile.readline().strip()) matName = line[0] material = NoeMaterial(matName, "") for field in line[1:]: attr, value = self.split_field(field) if attr == "tex": material.setTexture(value.strip('"')) self.matList.append(material) def parse_faces(self, numFaces): #I use a dictionary to store index buffers based on material index #The keys are the matNum, and the value is a bytes index buffer triBuffs = {} quadBuffs = {} for i in range(numFaces): line = self.tokenize(self.inFile.readline().strip()) numVerts = int(line[0]) verts = self.split_field(line[1])[1].split() if len(line) > 2: matNum = int(self.split_field(line[2])[1]) else: matNum = -1 uvs = [] if len(line) > 3: uvs = self.split_field(line[3])[1].split() if numVerts == 3: if not matNum in triBuffs: triBuffs[matNum] = {"idxList" : [], "uvBuff" : bytes()} triBuffs[matNum]["idxList"].extend(map(int, verts)) if uvs: triBuffs[matNum]["uvBuff"] += struct.pack("6f", *(map(float, uvs))) elif numVerts == 4: if not matNum in quadBuffs: quadBuffs[matNum] = {"idxList" : [], "uvBuff" : bytes()} quadBuffs[matNum]["idxList"].extend(map(int, verts)) if uvs: quadBuffs[matNum]["uvBuff"] += struct.pack("8f", *(map(float, uvs))) return triBuffs, quadBuffs def parse_binary_vertices(self, numVerts): print("binary vertices", numVerts) self.inFile.readline() self.inFile.seek(numVerts*12, 1) def parse_vertices(self, numVerts): vertList = [] for i in range(numVerts): coords = self.inFile.readline().strip().split() vertList.append(coords) return vertList def parse_weit(self): line = self.inFile.readline().strip() while not line.startswith("}"): line = self.inFile.readline().strip() def parse_mesh(self, line): line = self.tokenize(line) meshName = line[1] rapi.rpgSetName(meshName) line = self.inFile.readline().strip() while not line.startswith("}"): if not line: continue args = line.split() if args[0] == "BVertex": numVerts = int(args[1]) self.parse_binary_vertices(numVerts) elif args[0] == "vertex": numVerts = int(args[1]) vertList = self.parse_vertices(numVerts) elif args[0] == "weit": self.parse_weit() line = self.inFile.readline().strip() line = self.skip_lines("face") numFaces = int(line.strip().strip('{').split()[1]) triBuffs, quadBuffs = self.parse_faces(numFaces) self.build_mesh_tris(vertList, triBuffs) self.build_mesh_quads(vertList, quadBuffs) def parse_file(self): '''Main parser method''' line = self.inFile.readline() while line: line = line.strip() if line.startswith("Scene"): print("scene") elif line.startswith("Material"): numMat = int(line.strip().strip('{').split()[1]) self.parse_materials(numMat) elif line.startswith("Object"): self.parse_mesh(line) line = self.inFile.readline()