I removed the euclid and math module dependency using only Blender's in-built functions.
Next step will be creating the bones instead 'Empty' objects.
Once more, thank you Imame. This work is a good example of team work in some form.
Here is the code itself:
place it into C:\Program Files\Blender Fondation\.blender\scripts\import_zmd_bin.py
When you start Blender new menu item should appear "RoseOnline ZMD binary File Format (.zmd)..."
- #!BPY
- """
- Name: 'RoseOnline ZMD binary File Format (.zmd)...'
- Blender: 248
- Group: 'Import'
- Tooltip: 'Import RoseOnline binary ZMD file format (.zmd)'
- """
- __author__= ['Spark', 'Imame']
- __url__ = ("http://forum.dev-osrose.com")
- __version__= '0.0.7'
- __bpydoc__= '''\
- 0.0.7:
- Spark: 11/11/2008
- -> Works perfectly! =)
- -> Replaced euclid module with Blender.Mathutils, removed external modules dependency
- -> Removed math module
- -> Some cleanup of the code
- 0.0.6:
- lmame:
- -> math attack time ^_^, seems to getting better now,
- 0.0.5:
- lmame:
- -> added the i==0 from 0.0.3
- -> quaternion is calculated 'manually', same for mats multiplication and rot matrix (if needed),
- -> two formula for quaternion exist,
- 0.0.4:
- Spark
- -> Cleanup
- -> Empty objects instead of cubes
- 0.0.3:
- lmame:
- -> test of a new way, works not perfectly...
- 0.0.2:
- lmame:
- -> added a "load file stuff",
- -> Just changed a stuff in the read vector (format was z,y,x instead of x,y,z),
- 0.0.1
- -> Spark initial release,
- '''
- import struct
- import Blender
- from Blender.Mesh.Primitives import *
- from Blender.Mathutils import *
- from Blender import Scene, Object
- class zmdHeaderObject:
- def __init__(self):
- self.binaryFormat = "<7ci"
- self.format = ''
- self.boneCount = 0
- def load(self, file):
- tmpData = file.read(struct.calcsize(self.binaryFormat))
- data = struct.unpack(self.binaryFormat, tmpData)
- self.format = ''.join(data[0:7])
- self.boneCount = data[7] #~ DWORD = 4 bytes
- return self
- def __repr__(self):
- return "Format:\t%s\tBones:\t%d" % (self.format, self.boneCount)
- class zmdBoneObject:
- def __init__(self, ID):
- self.binaryFormat = "<7f"
- self.parentID = 0
- self.quaternion = Quaternion()
- self.matrix = Matrix()
- self.ID = ID
- self.name = ''
- self.parentName = 'NONE'
- def readName(self, file):
- #~ Reads byte by byte until \0x00, i.e cstr
- bname = ''
- while True:
- tmpData = file.read(1)
- if tmpData[0] == chr(0): break
- bname = bname + tmpData
- return bname
- def readParentID(self, file):
- tmpData = file.read(struct.calcsize('i'))
- return struct.unpack('i', tmpData)[0]
- def load(self, file):
- self.parentID = self.readParentID(file)
- self.name = self.readName(file)
- tmpData = file.read(struct.calcsize(self.binaryFormat))
- data = struct.unpack(self.binaryFormat, tmpData)
- #~ Vector(x, z, y)
- self.co = Vector(data[0], data[2], data[1]) / 100.0
- #~ Quaternion(w, x, z, y)
- self.quaternion.w = data[3]
- self.quaternion.x = data[4]
- self.quaternion.z = data[5]
- self.quaternion.y = data[6]
- #~ Blender way
- self.matrix = self.quaternion.toMatrix()
- return self
- def __repr__(self):
- return 'Bone %d name: "%s" \tparendBoneID: %d\tparentName: "%s"\n' % (self.ID, self.name, self.parentID, self.parentName) + \
- 'Coord:\t\t Vector(%.4f, %.4f, %.4f)\n' % tuple(self.co) + \
- 'Rotation:\t Quaternion(%.4f, %.4f, %.4f, %.4f)\n' % (self.quaternion.w, self.quaternion.x, self.quaternion.y, self.quaternion.z)
- #~ Loading file procedure.
- def load_zmd(filename):
- Blender.Window.WaitCursor(1)
- #~ Loading file procedure.
- binaryDummyFormat = "<i"
- #~ Open a binary file
- file = open(filename,"rb")
- zmd = zmdHeaderObject()
- zmd.load(file)
- #~ Empty dictionary
- bones = {}
- #~ Read and transform each bone position
- print "\n\nReading %s ..." % filename
- print zmd
- for i in range(zmd.boneCount):
- zbone = zmdBoneObject(i)
- #~ Read bone
- zbone.load(file)
- print "%s \t=\t Vector(%.6f, %.6f, %.6f)" % (zbone.name, zbone.co.x, zbone.co.y, zbone.co.z)
- #~ Apply the calculations only for bones after the root one, index > 0
- if i > 0:
- #~ Set parent's name
- zbone.parentName = bones[zbone.parentID].name
- #~ Calculate new absolute coordinates
- v_result = bones[zbone.parentID].matrix * zbone.co + bones[zbone.parentID].co
- #~ Update the matrix, local -> world
- zbone.matrix = bones[zbone.parentID].matrix * zbone.matrix
- #~ Quaternion is not used, but to preserve the conversion chain
- zbone.quaternion = zbone.matrix.toQuat()
- zbone.co = v_result
- #~ Add bone to the dictionary
- bones[i] = zbone
- #~ End of reading, close the file
- file.close()
- #~ Free some memory
- del file, zbone, zmd
- print "\nCreating bones ..."
- #~ Put some cubes instead bones
- sc = Blender.Scene.GetCurrent()
- #~ Read bone dictionary and create dummy cubes instead of bones for a test purpose
- #~ TODO: Create Bones objects instead Dummy objects
- for i, bone in enumerate(bones.values()):
- #~ Create dummy object
- obj = Object.New("Empty", bone.name)
- #~ Show name beside
- obj.drawMode = obj.drawMode | 8
- #~ Cube shape = 5
- obj.emptyShape = 5
- obj.size = (0.0125, 0.0125, 0.0125)
- #~ Put/link the object with the scene
- sc.objects.link(obj)
- #~ All generated objects will appear already selected - much easier to zoom into them - use "." from NUM PAD to zoom on selected !
- obj.sel = True
- #~ Set the position
- obj.LocX = bone.co[0]
- obj.LocY = bone.co[1]
- obj.LocZ = bone.co[2]
- #~ Next part creates parent <-> child connection, not necessary, but visual feedback =)
- if i > 0:
- parent_obj = Blender.Object.Get(bone.parentName)
- parent_obj.makeParent([obj])
- #~ Refresh the views
- Blender.Window.RedrawAll()
- #~ Clean up used memory
- del sc, bones, parent_obj, bone, i
- Blender.Window.WaitCursor(0)
- print "\nDone"
- #~ Loading a ZMD file.
- if __name__=='__main__':
- Blender.Window.FileSelector(load_zmd, 'Import RoseOnline ZMD', '*.zmd')
Enjoy, test and report the bugs here.