Hello, I'm not python developer, my knowledge about this language it's very basic, but anyway I'm trying to develop a "plugin" (technically based in other languages that I've knowledge) to a tool called noesis. Its purpose is to export meshs, nodes, animations and other stuffs of 3D models. I found the specific plugin for the model I need to extract, but it's limited to not export animations. So, as the project looks abandoned, I decided to implement what I need or try at least.
Until now, I only managed to loop through model animations, there is still a long way to go. Basically I'm with trouble about how to read animation's data, such as "Extents", "Name", etc., as explained in this wiki: https://github.com/cmbasnett/io_scene_abc/wiki/ABC
Here's is my code, I hope I've provided all needed informations.
https://paste.ofcode.org/8GbELJ4x7sBjWxCEv7q3jT
Until now, I only managed to loop through model animations, there is still a long way to go. Basically I'm with trouble about how to read animation's data, such as "Extents", "Name", etc., as explained in this wiki: https://github.com/cmbasnett/io_scene_abc/wiki/ABC
Here's is my code, I hope I've provided all needed informations.
https://paste.ofcode.org/8GbELJ4x7sBjWxCEv7q3jT
from inc_noesis import * import noesis import rapi import os import ctypes def registerNoesisTypes(): '''Register the plugin. Just change the Game name and extension.''' handle = noesis.register("LithTech", ".abc") noesis.setHandlerTypeCheck(handle, noepyCheckType) noesis.setHandlerLoadModel(handle, noepyLoadModel) return 1 def noepyCheckType(data): '''Verify that the format is supported by this plugin. Default yes''' if len(data) < 8: return 0 try: bs = NoeBitStream(data) length = bs.readUShort() idstring = noeStrFromBytes(bs.readBytes(length)) if idstring != "Header": return 0 return 1 except: return 0 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)''' inputName = rapi.getLocalFileName(rapi.getInputName()) filename, ext = os.path.splitext(inputName) ctx = rapi.rpgCreateContext() parser = SanaeParser(data, filename) 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 LithTechNode(object): '''A LithTech node object''' def __init__(self): self.nodeName = "" self.offset = 0 class Mesh(object): def __init__(self): self.submeshes = [] self.normList = [] self.vertList = [] self.uvList = [] self.idxList = [] self.meshName = "" self.numIdx = 0 self.numVerts = 0 class SanaeParser(object): def __init__(self, data, filename): '''Initialize some data. Refer to Sanae.py to see what is already initialized''' self.inFile = NoeBitStream(data) self.animList = [] self.texList = [] self.matList = [] self.boneList = [] self.nodeList = [] self.numMesh = 0 self.numSubmesh = 0 self.numBones = 0 self.basename = filename self.meshList = [] def read_name(self): string = self.inFile.readBytes(self.inFile.readUShort()) return noeStrFromBytes(string) def build_vertices(self, mesh, meshName): vertBuff = bytes() normBuff = bytes() uvBuff = bytes() idxBuff = bytes() rapi.rpgSetName(meshName) for i in range(mesh.numIdx): index = mesh.idxList[i] vertBuff += struct.pack('3f', *mesh.vertList[index]) normBuff += struct.pack('3f', *mesh.normList[index]) uvBuff += struct.pack('2f', *mesh.uvList[i]) idxBuff += struct.pack('H', i) rapi.rpgBindPositionBuffer(vertBuff, noesis.RPGEODATA_FLOAT, 12) rapi.rpgBindNormalBuffer(normBuff, noesis.RPGEODATA_FLOAT, 12) rapi.rpgBindUV1Buffer(uvBuff, noesis.RPGEODATA_FLOAT, 8) matName = meshName material = NoeMaterial(matName, self.basename) material.setDefaultBlend(0) self.matList.append(material) rapi.rpgSetMaterial(meshName) rapi.rpgCommitTriangles(idxBuff, noesis.RPGEODATA_USHORT, mesh.numIdx, noesis.RPGEO_TRIANGLE, 1) def build_meshes(self): i = 0 for mesh in self.meshList: for submesh in mesh.submeshes: meshName = "%s[%i]" %(mesh.meshName, i) self.build_vertices(submesh, meshName) i += 1 def parse_vertices(self, numVerts, mesh): #vertBuff = bytes() #normBuff = bytes() vertList = [] normList = [] verts = 0 for i in range(numVerts): count = self.inFile.readUShort() self.inFile.readUShort() for j in range(count): self.inFile.readUInt() self.inFile.read('4f') #vertBuff += self.inFile.readBytes(12) #normBuff += self.inFile.readBytes(12) vertList.append(self.inFile.read('3f')) normList.append(self.inFile.read('3f')) #rapi.rpgBindPositionBuffer(vertBuff, noesis.RPGEODATA_FLOAT, 12) #rapi.rpgBindNormalBuffer(normBuff, noesis.RPGEODATA_FLOAT, 12) mesh.numVerts = numVerts mesh.vertList = vertList mesh.normList = normList def parse_faces(self, numFaces, mesh): idxList = [] uvList = [] for i in range(numFaces): uvList.append(self.inFile.read('2f')) idxList.append(self.inFile.readUShort()) uvList.append(self.inFile.read('2f')) idxList.append(self.inFile.readUShort()) uvList.append(self.inFile.read('2f')) idxList.append(self.inFile.readUShort()) mesh.numIdx = numFaces * 3 mesh.idxList = idxList mesh.uvList = uvList def parse_bones(self): for i in range(self.numBones): boneName = self.read_name() self.inFile.readShort() self.inFile.readByte() matrix = self.inFile.read('16f') self.inFile.readUInt() count = self.inFile.readUInt() for i in range(count): self.read_name() numBones = self.inFile.readUInt() self.inFile.seek(numBones*4, 1) def parse_meshes(self): self.inFile.read('2L') for i in range(self.numMesh): mesh = Mesh() self.inFile.readUShort() self.inFile.read('3f') self.inFile.readUShort() meshName = self.read_name() mesh.meshName = meshName for j in range(self.numSubmesh): submesh = Mesh() numFaces = self.inFile.readUInt() idxBuff = self.parse_faces(numFaces, submesh) numVerts = self.inFile.readUInt() self.parse_vertices(numVerts, submesh) mesh.submeshes.append(submesh) self.meshList.append(mesh) def parse_header(self): #number of integers count = self.inFile.readUInt() self.inFile.read('2L') self.numBones = self.inFile.readUInt() self.numMesh = self.inFile.readUInt() self.inFile.read('4L') self.numSubmesh = self.inFile.readUInt() def parse_animations(self): count = self.inFile.readUInt() #ctypes.windll.user32.MessageBoxW(0, "Anim Count = %i" % count, "Your title", 1) for i in range(count): def parse_nodes(self): for node in self.nodeList: name = node.name offset = node.offset self.inFile.seek(offset) if name == "Header": self.parse_header() elif name == "Pieces": self.parse_meshes() elif name == "Nodes": self.parse_bones() elif name == "ChildModels": pass elif name == "Animation": self.parse_animations() #pass elif name == "Sockets": pass elif name == "AnimBindings": pass def parse_file(self): offset = 0 while offset != -1: self.inFile.seek(offset) node = LithTechNode() node.name = self.read_name() self.nodeList.append(node) offset = self.inFile.readInt() #record the start of node data node.offset = self.inFile.tell() self.parse_nodes() self.build_meshes()