Ok I feel like a mad scientist
genetically changing a poor alligator ^_^
Ok here is my "progress" (alligator and man), as you can see, the arms are (generally) in the right direction now, don't know if it's that useful or not
Anyway, I checked the formulas from euclid and it seems it's in fact ok.
Though there are two possible way to calculate a quaternion so I used the second and the second gives those two screenshots, without extra rotation.
So all calculations are done "manually" and as you can see, it's pretty much the same
If you use the first version to calculate the quaternion, you find the very same results as with euclid.
Anyway, here is the code:
-
- #!BPY
- """
- Name: 'RoseOnline ZMD binary File Format (.zmd)...'
- Blender: 248
- Group: 'Import'
- Tooltip: 'Import RoseOnline binary ZMD file format (.zmd)'
- """
-
- __author__= ['cssvb94']
- __url__ = ("forum.dev-osrose.com", "www.blender.org", "pyeuclid.googlecode.com")
- __version__= '0.0.5'
- __bpydoc__= '''\
-
- 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:
- cssvb94
- -> 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
- -> cssvb94 initial release,
- '''
- import struct
- from struct import *
- import Blender
- from Blender.Mesh.Primitives import *
- from Blender import Scene, Object
- from euclid import *
- import math
-
- class zmdObject:
-
- 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 = -1
-
- #self.co = Vector3(0.0, 0.0, 0.0)
-
- #~ w, x, y, z
- #~ self.quaternion = Quaternion(1.0, 0.0, 0.0, 0.0)
- self.quaternion = Quaternion()
- #LMA: Quaternion "manual".
- self.MatQ = Matrix3()
-
- 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)
-
- self.co = Vector3(data[0]/ 100.0, data[2]/ 100.0, data[1]/ 100.0)
-
- #some tests:
- #xyzw (values), then rotation test for first bone (see #ROTATION_TEST later)
- #4563
- # 4653 looks ok but not perfect (arms inverted) with +-pi/2 on z
- #6543
- #6453 looks right but not perfect even with +- pi on z axis
- #5643
- #5463 looks right but not perfect even with pi/2 on y axis
- '''
- self.quaternion.x=data[4]
- self.quaternion.y=data[6]
- self.quaternion.z=data[5]
- self.quaternion.w=data[3]
- '''
- self.quaternion.x=data[4]
- self.quaternion.y=data[6]
- self.quaternion.z=data[5]
- self.quaternion.w=data[3]
-
- #LMA: Quaternion, manual...
- '''
- #First version
- self.MatQ.a=1-2*self.quaternion.y*self.quaternion.y-2*self.quaternion.z*self.quaternion.z
- self.MatQ.e=2*(self.quaternion.x*self.quaternion.y+self.quaternion.w*self.quaternion.z)
- self.MatQ.i=2*(self.quaternion.x*self.quaternion.z-self.quaternion.w*self.quaternion.y)
-
- self.MatQ.b=2*(self.quaternion.x*self.quaternion.y-self.quaternion.w*self.quaternion.z)
- self.MatQ.f=1-2*self.quaternion.x*self.quaternion.x-2*self.quaternion.z*self.quaternion.z
- self.MatQ.j=2*(self.quaternion.y*self.quaternion.z+self.quaternion.w*self.quaternion.x)
-
- self.MatQ.c=2*(self.quaternion.x*self.quaternion.z+self.quaternion.w*self.quaternion.y)
- self.MatQ.g=2*(self.quaternion.y*self.quaternion.z-self.quaternion.w*self.quaternion.x)
- self.MatQ.k=1-2*self.quaternion.x*self.quaternion.x-2*self.quaternion.y*self.quaternion.y
- '''
- #second version
- self.MatQ.a=1-2*self.quaternion.y*self.quaternion.y-2*self.quaternion.z*self.quaternion.z
- self.MatQ.e=2*(self.quaternion.x*self.quaternion.y-self.quaternion.w*self.quaternion.z)
- self.MatQ.i=2*(self.quaternion.x*self.quaternion.z+self.quaternion.w*self.quaternion.y)
-
- self.MatQ.b=2*(self.quaternion.x*self.quaternion.y+self.quaternion.w*self.quaternion.z)
- self.MatQ.f=1-2*self.quaternion.x*self.quaternion.x-2*self.quaternion.z*self.quaternion.z
- self.MatQ.j=2*(self.quaternion.y*self.quaternion.z-self.quaternion.w*self.quaternion.x)
-
- self.MatQ.c=2*(self.quaternion.x*self.quaternion.z-self.quaternion.w*self.quaternion.y)
- self.MatQ.g=2*(self.quaternion.y*self.quaternion.z+self.quaternion.w*self.quaternion.x)
- self.MatQ.k=1-2*self.quaternion.x*self.quaternion.x-2*self.quaternion.y*self.quaternion.y
- '''
- print self.name
- print "%.10f,%.10f,%.10f,%.10f" % (self.quaternion.w,self.quaternion.x,self.quaternion.y,self.quaternion.z)
- print self.MatQ
- '''
- return self
-
-
- def __str__(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.
- binaryDummyFormat = "<i"
-
- #That was for tests.
- filename = 'D:/ZMD/ALLIGATOR_BONE.ZMD'
- #filename = 'D:/ZMD/YOUNGMAN_BONE.ZMD'
-
- # Open a binary file
- file=open(filename,"rb")
-
- zmd = zmdObject()
- zmd.load(file)
- print zmd
-
- # Empty dictionary
- bones = {}
-
- #LMA: Rotation matrixes.
- lmaAngle=math.pi/2
- RotX=Matrix3()
- RotY=Matrix3()
- RotZ=Matrix3()
- RotX.a=1
- RotX.b=0
- RotX.c=0
- RotX.e=0
- RotX.f=math.cos(lmaAngle)
- RotX.g=math.sin(lmaAngle)
- RotX.i=0
- RotX.j=-math.sin(lmaAngle)
- RotX.k=math.cos(lmaAngle)
-
- RotY.a=math.cos(lmaAngle)
- RotY.b=0
- RotY.c=-math.sin(lmaAngle)
- RotY.e=0
- RotY.f=1
- RotY.g=0
- RotY.i=math.sin(lmaAngle)
- RotY.j=0
- RotY.k=math.cos(lmaAngle)
-
- RotZ.a=math.cos(lmaAngle)
- RotZ.b=math.sin(lmaAngle)
- RotZ.c=0
- RotZ.e=-math.sin(lmaAngle)
- RotZ.f=math.cos(lmaAngle)
- RotZ.g=0
- RotZ.i=0
- RotZ.j=0
- RotZ.k=1
-
- # Read and transform each bone position
- #LMA: Other test begin.
- print "\n\n\nReading time..."
- for i in range(zmd.boneCount):
- zbone = zmdBoneObject(i)
- # Read bone
- zbone.load(file)
- v_result = zbone.co
-
- print "Org %s=%.10f,%.10f,%.10f" % (zbone.name,zbone.co.x,zbone.co.y,zbone.co.z)
- '''
- print "%.10f,%.10f,%.10f,%.10f" % (zbone.quaternion.w,zbone.quaternion.x,zbone.quaternion.y,zbone.quaternion.z)
- #print zbone.MatQ
- print "%.4f,%.4f,%.4f" % (v_result.x,v_result.y,v_result.z)
- print "%.4f,%.4f,%.4f,%.4f" % (zbone.quaternion.w,zbone.quaternion.x,zbone.quaternion.y,zbone.quaternion.z)
- '''
-
- #ROTATION_TEST (see above)
- if i==0:
- print "i==0"
- '''
- #v_result=zbone.quaternion*v_result
- qrot = Quaternion.new_rotate_axis(-1*math.pi/2, Vector3(0, 0, 1))
- zbone.quaternion=qrot*zbone.quaternion
- #v_result=zbone.quaternion*v_result
- v_result=qrot*v_result
- zbone.co = v_result
- '''
- #v_result=zbone.quaternion*v_result
-
- #LMA: manual version
- '''
- qrot = RotZ
- zbone.MatQ=zbone.MatQ*qrot
- #v_result=zbone.quaternion*v_result
- temp_result=Vector3(0,0,0)
- temp_result.x=qrot.a*v_result.x+qrot.b*v_result.y+qrot.c*v_result.z
- temp_result.y=qrot.e*v_result.x+qrot.f*v_result.y+qrot.g*v_result.z
- temp_result.z=qrot.i*v_result.x+qrot.j*v_result.y+qrot.k*v_result.z
- v_result=temp_result
- zbone.co = v_result
- '''
-
-
- if i>0:
- zbone.parentName = bones[zbone.parentID].name
- v_parent = bones[zbone.parentID].co
- '''
- q_parent = bones[zbone.parentID].quaternion
- v_result=(q_parent * v_result) + v_parent
- zbone.quaternion = zbone.quaternion * q_parent
- print zbone.name
- print "%10f,%.10f,%.10f" % (v_result.x,v_result.y,v_result.z)
- '''
-
-
- '''test LMA'''
- temp_result=Vector3(0,0,0)
- q_parent=bones[zbone.parentID].MatQ
- print "Parent MatQ %s" % (zbone.parentName)
- print q_parent
- temp_result.x=q_parent.a*v_result.x+q_parent.b*v_result.y+q_parent.c*v_result.z
- temp_result.y=q_parent.e*v_result.x+q_parent.f*v_result.y+q_parent.g*v_result.z
- temp_result.z=q_parent.i*v_result.x+q_parent.j*v_result.y+q_parent.k*v_result.z
- v_result=temp_result
- print "before + v_parent, %s=%.10f,%.10f,%.10f" % (zbone.name,v_result.x,v_result.y,v_result.z)
- v_result=v_result+v_parent
- print "after + v_parent= %.10f,%.10f,%.10f" % (v_result.x,v_result.y,v_result.z)
- zbone.MatQ = zbone.MatQ * q_parent
- '''END test'''
-
- zbone.co = v_result
-
- #print zbone.parentName
- #print q_parent
- #print q_parent.get_matrix()
-
- #~ print zbone.co
- #zbone.co = v_result
-
- #print zbone.name
- #print v_result
- #print zbone.quaternion
- #print "%.4f,%.4f,%.4f" % (v_result.x,v_result.y,v_result.z)
- #print "%.4f,%.4f,%.4f,%.4f" % (zbone.quaternion.w,zbone.quaternion.x,zbone.quaternion.y,zbone.quaternion.z)
-
- bones[i] = zbone
- #LMA: Other test end.
-
- # Close the file
- file.close()
- print "\n\n\nCube time..."
- # Put some cubes instead bones
- sc = Blender.Scene.GetCurrent()
-
- #~ Read bone dictionary and create dummy cubes instead of bones for a test purpose
- if True:
-
- for i, bone in enumerate(bones.values()):
- #~ Create dummy object
- obj = Object.New("Empty", bone.name)
- obj.drawMode=obj.drawMode|8
- obj.emptyShape = 5 # Cube shape = 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]
-
- #~ Print some info
- #print bone
- #~ 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, zmd, bones, file, zbone, parent_obj, bone, i
-
-