Script Importacion Project Zomboid Modelos Blender
Script Importacion Project Zomboid Modelos Blender
should_optimize_armature = BoolProperty(
name="Optimize Armature (Biped Models)",
description="Optimizes the imported Armature for animation purposes.",
default=True,
)
# Get the current scene
scene = context.scene
################################################################################
#####
###
###
### File Interpretation methods
###
###
###
################################################################################
#####
def read_header(self,file):
self.version
self.modelName
self.amtname
self.vertexStrideElementCount
self.vertexStrideSize
=
=
=
=
=
read_float(file)
read_line(file)
self.modelName + "_armature"
read_int(file)
read_int(file)
line = read_line(file)
vs = line.split(', ')
self.verts.append(Vector((float(vs[0]), float(vs[1]), float(
vs[2]))))
elif self.vertexStrideType[element] == "TextureCoordArray":
line = read_line(file)
vs = line.split(', ')
self.uvs.append(Vector((float(vs[0]),float(1) - float(vs[1])
)))
elif self.vertexStrideType[element] == "BlendWeightArray":
self.read_vertex_weight_values(file)
elif self.vertexStrideType[element] == "BlendIndexArray":
self.read_vertex_weight_indexes(file)
else:
line = read_line(file)
def read_vertex_weight_values(self,file):
weights = read_line(file)
split = weights.split(", ")
array = []
for s in split:
array.append(float(s))
self.BlendWeightArray.append(array)
def read_vertex_weight_indexes(self,file):
indexes = read_line(file)
split = indexes.split(", ")
array = []
for s in split:
array.append(int(s))
self.BlendIndexArray.append(array)
def read_faces(self,file):
for x in range(0,self.numberOfFaces):
face
faceVerts
faceVerts[0]
faceVerts[1]
faceVerts[2]
=
=
=
=
=
read_line(file)
face.split(", ")
int(faceVerts[0])
int(faceVerts[1])
int(faceVerts[2])
if self.hasTex:
self.faceUVs.append([self.uvs[faceVerts[0]],self.uvs[faceVerts[1
]],self.uvs[faceVerts[2]]])
self.faces.append([faceVerts[0], faceVerts[1], faceVerts[2]])
self.faceBuffer.append(faceVerts)
def read_bone_hierarchy(self,file):
for index in range(0,self.numberBones):
boneIndex
boneParentIndex
boneName
self.bone_ids[boneName]
=
=
=
=
read_int(file)
read_int(file)
read_line(file)
boneIndex
animation_frame_count = read_int(file)
print("Reading Animation: " + animation_name + "...")
key_frames
frame
current_index
last_index
first
=
=
=
=
=
[]
Frame()
-1
-1
False
animation = Animation(animation_name,animation_time,animation_frame_
count)
self.animations.append(animation)
for keyframe_index in range(0, animation_frame_count):
current_index
= read_int(file)
=
=
=
=
=
read_line(file)
read_float(file)
read_vector(file)
read_quaternion(file)#.inverted().copy()
matrix_from_quaternion_position(rot,loc)
bpy.ops.object.mode_set(mode = 'OBJECT')
def create_armature(self):
# Try to clear all selections.
try:
bpy.ops.object.mode_set(mode='OBJECT')
bpy.ops.object.select_all(action='DESELECT')
except:
ok = None
self.armature
ob
= bpy.data.armatures.new(self.amtname)
= bpy.data.objects.new(self.amtname, self.armatu
scn
scn.objects.link(ob)
scn.objects.active
ob.select
= bpy.context.scene
re)
= ob
= True
obj_armature
= bpy.data.objects[self.amtname]
obj_armature.show_x_ray = True
bpy.ops.object.mode_set(mode='EDIT')
###
### Create the Root transformation manually to set up the parent inherit
ance.
###
########################################################################
##
#-----------------------------------------------------------------------#
# Create the Root transformation manually to set up parent inheritance.
#
bone
= self.armature.edit_bones.new(self.bone_names[0])
#
matrix_location = self.bone_matrix_offset_data[0]
#
mat
= set_identity()
mat_world
= matrix_location * mat
#
#
#
self.world_transforms.append(mat_world)
#
self.bones.append(bone)
#
#
bone.matrix = mat_world
#
bone.tail
#
########################################################################
##
# Set up each bone.
for x in range(1, self.numberBones):
bone = self.armature.edit_bones.new(self.bone_names[x])
print("Creating Bone: " + bone.name + "...")
if bone.name == "Bip01":
bone.head = Vector((0,0.05,0))
bone.tail = Vector((bone.head[0], bone.head[1] + 0.05, bone.head
[2]))
self.bones.append(bone)
parent_index = self.bone_parent[x]
parent_bone = self.bones[parent_index]
matrix_location = self.bone_matrix_offset_data[x]
matrix_rotation = self.bone_matrix_inverse_bind_pose_data[x]
mat_world
= matrix_location * self.bone_matrix_offset_data[par
ent_index].copy().inverted()
mat
= matrix_location.inverted().copy()
self.world_transforms.append(mat_world)
####################################################################
#################
#----------------------------------------------------------------------------------#
# TODO: Could improve this by using the bind-pose rotation to create
the rest pose. #
#
Might have to do this later.
#
bone.head = mat.decompose()[0]
#
bone.tail = Vector((bone.head[0], bone.head[1] + 0.05, bone.head[2])
)
#
#
if parent_index != -1:
#
if bone.tail[0] == 0 and bone.tail[1] == 0 and bone.tail[2] == 0
:
#
bone.tail = Vector((bone.head[0], bone.head[1] + 0.05, bone.
head[2]))
#
else:
#
bone.tail = Vector((bone.head[0], bone.head[1] + 0.05, bone.head
[2]))
#
####################################################################
#################
bone.parent = parent_bone
# TODO: Add a option to not load animations and optimize the armature fo
r personal animation use.
if self.should_optimize_armature:
self.optimize_armature()
bpy.ops.object.mode_set(mode='OBJECT')
obj_armature["ZOMBOID_ARMATURE"] = 1
#
#
#
#
#
new_parent.parent = None
bpy.ops.armature.select_all(action='DESELECT')
bone.select = True
bone.parent = new_parent
break
Euler()
= 0
= 0
= 0
frame_offset = 0
world_transforms = dict()
bone_transforms = dict()
skin_transforms = dict()
# Go through each Animation.
for animation in self.animations:
if animation.name != "Idle":
continue
# Create a new Action for the Animation.
action = bpy.data.actions.new(name=animation.name)
print("Rendering Animation: " + animation.name + "...")
# Offset counter for each frame.
#frame_offset = 0
loc_parent_dic = dict()
rot_parent_dic = dict()
mat_parent_dic = dict()
identity = set_identity()
vec3
= Vector((0.0,1.0,0.0))
identity = rotate(0.0,vec3,identity)
bone_transforms[0] = self.bones[self.bone_ids["Root"]].matrix
world_transforms[0] = self.mul(bone_transforms[0], identity)
skin_transforms[0] = self.mul(self.bone_matrix_offset_data[0], worl
d_transforms[0])
frame_offset = 0
# Loop through each frame.
for frame in animation.frames:
# Bone offset to track which in the frame arrays is being access
ed.
bone_offset = 0
# Set the current frame in the scene to the offset.
bpy.data.scenes[0].frame_current = frame_offset
for bone_name in frame.bone_names:
if "Root" in bone_name:
bone_offset += 1
continue
# Grab the bone responsible for this action
bone = bpy.data.objects[self.amtname].pose.bones[bone_name]
bone_id = self.bone_ids[bone.name]
parent_id = self.bone_parent[bone_id]
ebone
ebone_matrix
ebone_location
ebone_rotation
=
=
=
=
self.bones[bone_id]
self.bone_matrix_offset_data[bone_id]
ebone_matrix.decompose()[0]
ebone_matrix.decompose()[1]
file.
loc = frame.bone_locs[bone_offset]
rot = frame.bone_rots[bone_offset]
mat = frame.bone_mats[bone_offset]
self.bone_matrix_bind_pose_data[bone_id]
# Bone_transform
bt = bone_transforms[bone_id] = matrix_from_quaternion_posi
tion(rot, loc)
# World_transform
wt = world_transforms[bone_id] = mul(bt,world_transforms[par
ent_id])
# Skin_transform
st = skin_transforms[bone_id] = mul(self.bone_matrix_offset
_data[bone_id], wt)
print(bone_name + "'s: \t\n" + str(bt))
#bone.location
= wt.transposed().decompose()[0]
#bone.rotation_quaternion = wt.transposed().decompose()[1]
#bone.matrix_basis = st.transposed().copy()
#bone.matrix = wt * bt #.transposed()
#bone.matrix_basis = wt.transposed()
#bt.transpose()
#bt.transpose()
q = Quaternion()
#q = st.decompose()[1]
#q.w
#q.z
#q.y
#q.x
q.x
q.y
q.z
q.w
=
=
=
=
=
=
=
=
st[3][3]
st[2][3]
st[1][3]
st[0][3]
st[3][0]
st[3][1]
st[3][2]
st[3][3]
#bt.invert()
bone.rotation_quaternion = q
#bone.location = Vector((bt[0][3], bt[1][3], bt[2][3]))
#bone.matrix = self.translate(ebone_location,src=bone.matrix
)
#bone.location = Vector((bone.location[0] + ebone_location[0
], bone.location[1] + ebone_location[1], bone.location[2] + ebone_location[2]))
bone.scale = Vector((1,1,1))
#bone.location
= Vector((bt[3][0], bt[3][1], bt[3
][2]))
#q = bt.inverted().decompose()[1]
#q.w = -q.w
#q.invert()
#q.negate()
#bone.rotation_quaternion = q
# Set the bones in the dictionaries for future use.
loc_parent_dic[bone_name] = loc
rot_parent_dic[bone_name] = rot
bpy.context.scene.update()
bpy.ops.object.select_pattern(pattern=bone_name)
# Increment the offset.
bone_offset += 1
try:
# Create a Blender KeyFrame at this offset.
bpy.ops.anim.keyframe_insert_menu(type='BUILTIN_KSI_LocRot')
except:
ok = None
# De-select all bones to optimize the Blender KeyFrame.
bpy.ops.pose.select_all(action='DESELECT')
# Increment the offset.
frame_offset += 1
# For debug, we load one animation.
break
def execute(self, context):
old_cursor = bpy.context.scene.cursor_location
# Center the cursor.
bpy.context.scene.cursor_location = (0.0, 0.0, 0.0)
# The offset in the file read
offset = 0
with io.open(self.filepath, 'r') as file:
end_of_file = False
while file.readable() and end_of_file == False:
if offset == 0:
self.read_header(file)
elif offset == 1:
self.read_stride_data(file)
elif offset == 2:
self.vertexCount
= read_int(file)
elif offset == 3:
self.read_vertex_buffer(file)
elif offset == 4:
self.numberOfFaces
= read_int(file)
elif offset == 5:
self.read_faces(file)
elif offset == 6:
try:
self.numberBones = read_int(file)
self.has_armature = True
except:
end_of_file
= True
elif offset == 7:
self.read_bone_hierarchy(file)
elif offset == 8:
self.read_bone_bind_pose_data(file)
elif offset == 9:
self.read_bone_bind_inverse_pose_data(file)
elif offset == 10:
self.read_bone_offset_data(file)
elif offset == 11:
if not self.load_animations:
end_of_file
= True
else:
try:
self.animation_count = read_int(file)
self.has_animations = True
except:
end_of_file
= True
elif offset == 12:
self.read_animations(file)
offset+=1
if offset > 13 or end_of_file:
break
# Close the file.
file.close()
if self.has_armature and self.load_armature:
# Create the Armature for proceeding animation data
self.create_armature()
#if self.has_animations:
# WIP for now.
#self.create_animations()
# Check for meshes with Blend data and no armature.
if self.has_armature == False and self.has_vert_bone_data == True and se
lf.load_model_weights:
valid_arm
= False
armature_name = ''
for armature in bpy.data.objects:
try:
test = armature["ZOMBOID_ARMATURE"]
if test != -1:
armature_name = armature.name
self.armature = armature.data
self.amtname = armature.name
self.has_armature = True
break
except:
ok = True
if valid_arm:
obj_armature = bpy.data.objects[armature_name]
for bone in obj_armature.data.bones:
bone_name = bone.name
id = self.bone_ids[bone_name] = obj_armature[bone_name]
self.bone_names[id] = bone_name
if self.load_model:
self.create_mesh()
bpy.context.scene.cursor_location = old_cursor
return {'FINISHED'}
def __init__(self):
self.vertexStrideData
self.bone_matrix_bind_pose_data
self.bone_matrix_inverse_bind_pose_data
self.bone_matrix_offset_data
self.bone_map
self.bone_ids
=
=
=
=
=
=
dict()
dict()
dict()
dict()
dict()
dict()
self.animations
self.BlendWeightArray
self.BlendIndexArray
self.empties
self.bone_names
self.bone_parent
self.vertexElements
self.vertexStrideType
self.vertexBuffer
self.faceBuffer
self.verts
self.uvs
self.faceUVs
self.edges
self.faces
self.bones
self.bone_location
self.quats
self.world_transforms
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
self.modelName
= ' '
self.version
self.vertexStrideElementCount
self.vertexStrideSize
self.numberBones
self.vertexCount
self.VertexArray
self.NormalArray
self.TangentArray
self.TextureCoordArray
self.animation_count
=
=
=
=
=
=
=
=
=
=
#self.load_armature
self.hasTex
self.has_armature
self.has_animations
self.has_vert_bone_data
=
=
=
=
0
0
0
0
0
0
0
0
0
0
= True
False
False
False
False
class Animation:
def __init__(self,name,time,frame_count):
self.name
self.time
self.frame_count
self.key_frames
self.frames
=
=
=
=
=
name
time
frame_count
[]
[]
class Frame:
def __init__(self):
self.bones
self.bone_names
self.times
self.bone_mats
self.bone_locs
self.bone_rots
=
=
=
=
=
=
[]
[]
[]
[]
[]
[]
class KeyFrame:
def __init__(self,bone_index,bone_name,frame_time,mat):
self.bone_index
= bone_index
self.bone_name
= bone_name
self.time
= frame_time
self.matrix
= mat
self.loc
= Vector((0,0,0))
self.rot
= Quaternion()
# Only needed if you want to add into a dynamic menu
def menu_func_import(self, context):
self.layout.operator(ImportSomeData.bl_idname, text="Text Import Operator")
def register():
bpy.utils.register_class(ZomboidImport)
bpy.types.INFO_MT_file_import.append(menu_func_import)
def unregister():
bpy.utils.unregister_class(ZomboidImport)
bpy.types.INFO_MT_file_import.remove(menu_func_import)
if __name__ == "__main__":
register()
# test call
bpy.ops.zomboid.import_model('INVOKE_DEFAULT')
################################################################################
#####
###
###
### File I/O methods
###
###
###
################################################################################
#####
def read_line(file):
string = '#'
while string.startswith("#"):
string = str(file.readline().strip())
return string
def read_int(file):
return int(read_line(file))
def read_float(file):
return float(read_line(file))
def read_vector(file):
line = read_line(file)
split = line.split(", ")
return Vector((float(split[0]), float(split[1]), float(split[2])))
def read_quaternion(file):
line = read_line(file)
split = line.split(", ")
quat = Quaternion()
quat.x
quat.y
quat.z
quat.w
return
= float(split[0])
= float(split[1])
= float(split[2])
= float(split[3])
quat
def read_matrix(file):
matrix_line = []
for i in range(0,4):
matrix_array = []
string_mat = read_line(file).split(", ")
matrix_array.append(float(string_mat[0]))
matrix_array.append(float(string_mat[1]))
matrix_array.append(float(string_mat[2]))
matrix_array.append(float(string_mat[3]))
matrix_line.append((matrix_array[0],matrix_array[1],matrix_array[2],matr
ix_array[3]))
return Matrix((matrix_line[0], matrix_line[1], matrix_line[2], matrix_line[3
]))
################################################################################
#####
###
###
### Matrix methods
###
###
(Note: Java-translated methods to test accuracy of Animation code.)
###
###
Will likely get rid of these.
###
###
###
################################################################################
#####
def matrix_from_quaternion_position(quaternion, position):
mat = matrix_from_quaternion(quaternion)
mat2 = set_identity()
mat2 = translate(position, mat2)
#mat2 = mat2.transposed().copy()
#print(mat)
#mat3 = mat2 * mat
mat3 = mul(mat2, mat)
return mat3
def translate(vec,src=None):
m = Matrix.Identity(4)
set_identity(m)
if src == None:
set_identity(src)
m[3][0]
m[3][1]
m[3][2]
m[3][3]
+=
+=
+=
+=
src[0][0]
src[0][1]
src[0][2]
src[0][3]
*
*
*
*
vec.x
vec.x
vec.x
vec.x
+
+
+
+
src[1][0]
src[1][1]
src[1][2]
src[1][3]
*
*
*
*
vec.y
vec.y
vec.y
vec.y
+
+
+
+
src[2][0]
src[2][1]
src[2][2]
src[2][3]
*
*
*
*
vec.z
vec.z
vec.z
vec.z
return m
def length(quat):
return math.sqrt(quat.x * quat.x + quat.y * quat.y + quat.z * quat.z + quat.
w * quat.w)
def matrix_from_quaternion(quaternion):
m = set_identity()
q = quaternion
if length(quaternion) > 0.0:
q = quaternion.normalized()
xx
xy
xz
wx
=
=
=
=
(q.x)
(q.x)
(q.x)
(q.x)
*
*
*
*
(q.x)
(q.y)
(q.z)
(q.w)
yy = (q.y) * (q.y)
yz = (q.y) * (q.z)
wy = (q.y) * (q.w)
zz = (q.z) * (q.z)
wz = (q.z) * (q.w)
m[0][0] = 1.0 - 2.0 * (float(yy) + float(zz))
m[1][0] =
2.0 * (float(xy) - float(wz))
m[2][0] =
2.0 * (float(xz) + float(wy))
m[3][0] = 0.0
m[0][1]
m[1][1]
m[2][1]
m[3][1]
=
2.0 * (float(xy) + float(wz))
= 1.0 - 2.0 * (float(xx) + float(zz))
=
2.0 * (float(yz) - float(wx)) * float(1.0)
= 0.0
m[0][2]
m[1][2]
m[2][2]
m[3][2]
=
2.0 * (float(xz) - float(wy))
=
2.0 * (float(yz) + float(wx))
= 1.0 - 2.0 * (float(xx) + float(yy))
= 0.0
m[0][3]
m[1][3]
m[2][3]
m[3][3]
=
=
=
=
0.0
0.0
0.0
1.0
return m.transposed().copy()
def rotate(angle, axis, src):
m = set_identity()
c = math.cos(angle)
s = math.sin(angle)
oneminusc = 1.0 - c
xy = axis[0] * axis[1]
yz = axis[1] * axis[2]
xz = axis[0] * axis[2]
xs = axis[0] * s
ys = axis[1] * s
zs = axis[2] * s
f00 = axis[0] * axis[0] * oneminusc + c
f01 = xy * oneminusc + zs
f02 = xz * oneminusc - ys
f10 = xy * oneminusc - zs
f11 = axis[1] * axis[1] * oneminusc + c
f12 = yz * oneminusc + xs
f20 = xz * oneminusc + ys
f21 = yz * oneminusc - xs
f22 = axis[2] * axis[2] * oneminusc + c
t00
t01
t02
t03
=
=
=
=
src[0][0]
src[0][1]
src[0][2]
src[0][3]
*
*
*
*
f00
f00
f00
f00
+
+
+
+
src[1][0]
src[1][1]
src[1][2]
src[1][3]
*
*
*
*
f01
f01
f01
f01
+
+
+
+
src[2][0]
src[2][1]
src[2][2]
src[2][3]
*
*
*
*
f02
f02
f02
f02
t10
t11
t12
t13
=
=
=
=
src[0][0]
src[0][1]
src[0][2]
src[0][3]
*
*
*
*
f10
f10
f10
f10
+
+
+
+
src[1][0]
src[1][1]
src[1][2]
src[1][3]
*
*
*
*
f11
f11
f11
f11
+
+
+
+
src[2][0]
src[2][1]
src[2][2]
src[2][3]
*
*
*
*
f12
f12
f12
f12
=
=
=
=
t00
t01
t02
t03
m[1][0]
m[1][1]
m[1][2]
m[1][3]
=
=
=
=
t10
t11
t12
t13
return m
def mul(left,right,dest=Matrix.Identity(4)):
if dest == None:
dest = set_identity()
dest[0][0] = left[0][0] * right[0][0] +
] * right[0][2] + left[3][0] * right[0][3]
dest[0][1] = left[0][1] * right[0][0] +
] * right[0][2] + left[3][1] * right[0][3]
dest[0][2] = left[0][2] * right[0][0] +
] * right[0][2] + left[3][2] * right[0][3]
dest[0][3] = left[0][3] * right[0][0] +
] * right[0][2] + left[3][3] * right[0][3]
dest[1][0] = left[0][0] * right[1][0] +
] * right[1][2] + left[3][0] * right[1][3]
dest[1][1] = left[0][1] * right[1][0] +
] * right[1][2] + left[3][1] * right[1][3]
dest[1][2] = left[0][2] * right[1][0] +
] * right[1][2] + left[3][2] * right[1][3]
dest[1][3] = left[0][3] * right[1][0] +
] * right[1][2] + left[3][3] * right[1][3]
dest[2][0] = left[0][0] * right[2][0] +
] * right[2][2] + left[3][0] * right[2][3]
dest[2][1] = left[0][1] * right[2][0] +
] * right[2][2] + left[3][1] * right[2][3]
dest[2][2] = left[0][2] * right[2][0] +
] * right[2][2] + left[3][2] * right[2][3]
dest[2][3] = left[0][3] * right[2][0] +
] * right[2][2] + left[3][3] * right[2][3]
dest[3][0] = left[0][0] * right[3][0] +
] * right[3][2] + left[3][0] * right[3][3]
dest[3][1] = left[0][1] * right[3][0] +
] * right[3][2] + left[3][1] * right[3][3]
dest[3][2] = left[0][2] * right[3][0] +
] * right[3][2] + left[3][2] * right[3][3]
dest[3][3] = left[0][3] * right[3][0] +
] * right[3][2] + left[3][3] * right[3][3]
return dest
def set_identity(mat=Matrix.Identity(4)):
mat[0][0] = 1.0
mat[0][1] = 0.0
mat[0][2] = 0.0
mat[0][3] = 0.0
mat[1][0] =
mat[1][1] =
mat[1][2] =
mat[1][3] =
mat[2][0] =
mat[2][1] =
mat[2][2] =
mat[2][3] =
mat[3][0] =
mat[3][1] =
mat[3][2] =
mat[3][3] =
return mat
0.0
1.0
0.0
0.0
0.0
0.0
1.0
0.0
0.0
0.0
0.0
1.0