Sanae3D - 3D format converter
Posted: Thu Jul 07, 2011 5:05 pm
Downloads: Mediafire folder
I wrote a simple tool in python that can help people figure out formats (at least, the geometry. No bones. Maybe in the future. I will need some more tools for that, like a simple way to draw the skeleton) I want to try to get pyOpenGL to work though, so that you can just view the model without downloading anything else.
This post will explain how to use it.
It's designed so that you don't have to understand how to write the code to read a file, nor do you need to worry about export functions, though you would need to have basic working knowledge of python syntax.
It's plugin based, so you only need to concern yourself with writing a plugin for the format you're interested in.
It uses command-line for input (since it's designed for batch conversion), though I've used unix-style options cause I'm using a deprecated library lol
Requirements
Python (should be compatible with most versions since I don't use anything special).
And 3.x also, once I fix all of my print statements.
Usage:
Sanae.py [-options] file1 [, file2, file3, ... ]
You might need to type "python" before that if it doesn't work.
Some preparation
1. I have provided a template (binaryParsertemplate.py).
Copy that into a new file and replace all "temp" with a class name of your choice.
2. The definitions function tells the engine how to identify your format. My auto-detection scheme is pretty weak right now so it only checks for the header (I've limited to first 8 bytes for now) and the extension.
The first string is the header
Second string is the extension (without the period)
Third string is just a description (maybe for a help interface later)
3. The read_file function is what the engine will call from the appropriate plugin.
The only thing you have to change is the "temp", but if you did a replace-all earlier then you're fine. It assumes you're reading a binary file, so if you're not you might want to change that to something else.
4. The write_file function is what the engine will call when exporting the object. You don't need to worry about this (and I haven't written anything suitable for it either).
5. I have a Model3D class that will store the data that make up the 3D object, as well as a bunch of methods that allow you to get info in and out. You should use these methods instead of directly accessing the object, though it doesn't really matter if you're just using this to help figure out formats.
6. StructReader is just a class that implements various struct reading methods so you can say "file.read_float()" or "file.read_long()". It doesn't implement every possible method, but I haven't thought about how I would deal with this.
Writing your plugin
The parse_file method in the class is really all you need. You will need to familiarize yourself with the methods defined in Model3D and StructReader, but once that's done all you need to worry about is the file format.
I'll use chrrox's shaiya 3DC tutorial to demonstrate how you would go about writing the parse_file method.
Here's the format:
You can read it from top to bottom and write the appropriate method calls.
We see that there's no magic word in the header besides a 0, which is probably not very unique, so our definition would be
0. We note that nowhere does it indicate the mesh names, so you will need to create one yourself.
My data structures require mesh names, so you can just make one at the beginning
1. Now we begin. First is a dword 0, so we can just read it, or skip it using seek.
2. We then get the number of bones, which is also a dword. You want to store it. Note that all of the value-reading methods use struct.unpack, and that thing returns a tuple, so you will need to get the first value in the tuple.
3. Next is the bone struct, and the format implies that there are numBones of these (which is why we stored it earlier)
As mentioned, I don't support bones yet, so we don't do anything else with it.
4. Now we get the number of vertices
5. and then the vert structure. Just call the appropriate methods based on the format.
Note that I am storing all of this information in my model3D object
6. read the number of faces
7. parse faces, and store them. Note that materials are not specified anywhere.
This is because each model contains only one mesh, and all faces use the same material, so it would
be material index 0. Texture name is the same as the model filename, but I don't care about that here.
And now you're done. All you have to do is put this module in the plugins folder, then pass a shaiya 3DC object to sanae.
If successful, you will get an MQO file (this is default behavior, I haven't worked on this yet).
Open it with metasequoia (free download: http://www.metaseq.net/english/) to see the results.
You might have to zoom in cause I don't do any scaling (although the option "--scale n" can scale it by a factor of n...but only for single-mesh models right now)
Everything I've used up there is pretty much all you need for simple formats:
-create the object
-create vertices, and fill them with data
-create faces, and fill them with data
I have included the 3DC plugin with the download for reference, as well as a sample 3DC file.
Downloads: MF folder
I wrote a simple tool in python that can help people figure out formats (at least, the geometry. No bones. Maybe in the future. I will need some more tools for that, like a simple way to draw the skeleton) I want to try to get pyOpenGL to work though, so that you can just view the model without downloading anything else.
This post will explain how to use it.
It's designed so that you don't have to understand how to write the code to read a file, nor do you need to worry about export functions, though you would need to have basic working knowledge of python syntax.
It's plugin based, so you only need to concern yourself with writing a plugin for the format you're interested in.
It uses command-line for input (since it's designed for batch conversion), though I've used unix-style options cause I'm using a deprecated library lol
Requirements
Python (should be compatible with most versions since I don't use anything special).
And 3.x also, once I fix all of my print statements.
Usage:
Sanae.py [-options] file1 [, file2, file3, ... ]
You might need to type "python" before that if it doesn't work.
Some preparation
1. I have provided a template (binaryParsertemplate.py).
Copy that into a new file and replace all "temp" with a class name of your choice.
2. The definitions function tells the engine how to identify your format. My auto-detection scheme is pretty weak right now so it only checks for the header (I've limited to first 8 bytes for now) and the extension.
The first string is the header
Second string is the extension (without the period)
Third string is just a description (maybe for a help interface later)
3. The read_file function is what the engine will call from the appropriate plugin.
The only thing you have to change is the "temp", but if you did a replace-all earlier then you're fine. It assumes you're reading a binary file, so if you're not you might want to change that to something else.
4. The write_file function is what the engine will call when exporting the object. You don't need to worry about this (and I haven't written anything suitable for it either).
5. I have a Model3D class that will store the data that make up the 3D object, as well as a bunch of methods that allow you to get info in and out. You should use these methods instead of directly accessing the object, though it doesn't really matter if you're just using this to help figure out formats.
6. StructReader is just a class that implements various struct reading methods so you can say "file.read_float()" or "file.read_long()". It doesn't implement every possible method, but I haven't thought about how I would deal with this.
Writing your plugin
The parse_file method in the class is really all you need. You will need to familiarize yourself with the methods defined in Model3D and StructReader, but once that's done all you need to worry about is the file format.
I'll use chrrox's shaiya 3DC tutorial to demonstrate how you would go about writing the parse_file method.
Here's the format:
Code: Select all
#Provided from Fatduck
dword constant00
dword numBones
struct Bone {
float_16 Matrix4X4
}
dword numVerts
struct Vert {
float_3 CoordXYZ
float Weight
byte BoneID01
byte BoneID02
byte NULL1
byte NULL2
float_3 NormalXYZ
float_2 TexCoordUV
}
dword numFaces
struct Face {
word_3 FaceIndices123
}
We see that there's no magic word in the header besides a 0, which is probably not very unique, so our definition would be
Code: Select all
return "", "3DC", "Shaiya 3DC file"
My data structures require mesh names, so you can just make one at the beginning
Code: Select all
meshName = "obj"
self.create_object(meshName)
Code: Select all
self.inFile.read_long()
#self.inFile.seek(4, os.SEEK_CUR)
Code: Select all
numBones = self.inFile.read_long()
As mentioned, I don't support bones yet, so we don't do anything else with it.
Code: Select all
for i in range(numBones):
boneStuff = self.inFile.read_float(16) #read 16 floats
4. Now we get the number of vertices
Code: Select all
numVerts = self.inFile.read_long()[0]
Note that I am storing all of this information in my model3D object
Code: Select all
for i in xrange(numVerts):
vx, vy, vz = self.inFile.read_float(3)
weight = self.inFile.read_float()
b1, b2, null1, null2 = self.inFile.read_byte(4)
nx, ny, nz = self.inFile.read_float(3)
tu, tv = self.inFile.read_float(2)
#now store them. I don't support bones yet.
self.create_vertex(meshName, i)
self.add_coords(meshName, i, [vx, vy, vz])
self.add_vert_normals(meshName, i, [nx, ny, nz]) #normals stored with vertices
self.add_vert_uv(meshName, i, [tu, tv]) #uv stored with vertices
Code: Select all
numFaces = self.inFile.read_long()[0]
This is because each model contains only one mesh, and all faces use the same material, so it would
be material index 0. Texture name is the same as the model filename, but I don't care about that here.
Code: Select all
For i in range(numFaces):
v1, v2, v3 = self.inFile.read_short(3)
self.create_face(meshName, i)
self.add_face_verts(meshName, i, [v1, v2, v3])
self.add_face_material(meshName, i, 0)
If successful, you will get an MQO file (this is default behavior, I haven't worked on this yet).
Open it with metasequoia (free download: http://www.metaseq.net/english/) to see the results.
You might have to zoom in cause I don't do any scaling (although the option "--scale n" can scale it by a factor of n...but only for single-mesh models right now)
Everything I've used up there is pretty much all you need for simple formats:
-create the object
-create vertices, and fill them with data
-create faces, and fill them with data
I have included the 3DC plugin with the download for reference, as well as a sample 3DC file.
Downloads: MF folder