0% found this document useful (0 votes)
424 views22 pages

Script Importacion Project Zomboid Modelos Blender

en un pequeño script desarrollado por jab en la comunidad de project zomboid con el fin de modificar los modelos del juego
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as TXT, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
424 views22 pages

Script Importacion Project Zomboid Modelos Blender

en un pequeño script desarrollado por jab en la comunidad de project zomboid con el fin de modificar los modelos del juego
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as TXT, PDF, TXT or read online on Scribd
You are on page 1/ 22

# Author: Jab (or 40BlocksUnder) | Joshua Edwards

# Link for more info: http://theindiestone.com/forums/index.php/topic/12864-blen


der
# Imports models from Zomboid format.
import io,math,bmesh,bpy
from
from
from
from
from
from
from
from

bpy import context


bpy.types import Operator
bpy.props import FloatVectorProperty
bpy_extras.object_utils import AddObjectHelper, object_data_add
mathutils import Vector, Euler, Quaternion, Matrix
bpy_extras.io_utils import ImportHelper
bpy.props import StringProperty, BoolProperty, EnumProperty
bpy.types import Operator

class ZomboidImport(Operator, ImportHelper):


"""This appears in the tooltip of the operator and in the generated docs"""
# important since its how bpy.ops.import_test.some_data is constructed
bl_idname
= "zomboid.import_model"
bl_label
= "Import a Zomboid Model"
filename_ext = ".txt"
filter_glob = StringProperty(
default="*.txt",
options={'HIDDEN'},
)
load_model = BoolProperty(
name="Load Model",
description="Whether or not to import the model mesh.",
default=True,
)
load_armature = BoolProperty(
name="Load Armature",
description="Whether or not to import the armature, if present.",
default=True,
)
load_model_weights = BoolProperty(
name="Load Bone Weights",
description="Load Bone weights if PZ armature is detected. (RECOMENDED!)
",
default=True,
)
load_animations = BoolProperty(
name="Load Animations (WIP!)",
description="Whether or not to import animations. (Not done yet!)",
default=False,
)
lock_model_on_armature_detection = BoolProperty(
name="Lock Model Transforms If Armature Present",
description="Whether or not to lock the model, if an armature is present
.",
default=True,
)

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)

# Vertex Stride Data:


# (Int)
Offset
# (String)
def read_stride_data(self,file):
for x in range(0,self.vertexStrideElementCount):
value = read_line(file)
type = read_line(file)
self.vertexStrideType.append(type)
if type == "TextureCoordArray":
self.hasTex = True
elif type == "BlendWeightArray":
self.has_vert_bone_data = True
# Place it in the dictionary
self.vertexStrideData[type] = value
def read_vertex_buffer(self,file):
for x in range(0,int(self.vertexCount)):
elementArray = []
for element in range(0,self.vertexStrideElementCount):
if self.vertexStrideType[element] == "VertexArray":

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

# Append the name and the parent ID


self.bone_names.append(boneName)
self.bone_parent.append(boneParentIndex)
# Bind Pose:
# (Int)
Bone Index
# (Matrix) Bind Matrix
def read_bone_bind_pose_data(self,file):
for index in range(0,self.numberBones):
boneIndex = read_int(file)
bone_matrix = read_matrix(file)
self.bone_matrix_bind_pose_data[index] = bone_matrix
def read_bone_bind_inverse_pose_data(self,file):
for index in range(0,self.numberBones):
boneIndex
= read_int(file)
matrix_inverse = read_matrix(file)
self.bone_matrix_inverse_bind_pose_data[index] = matrix_inverse
def read_bone_offset_data(self,file):
for index in range(0,self.numberBones):
boneIndex
= read_int(file)
bone_offset_matrix = read_matrix(file)
self.bone_matrix_offset_data[index] = bone_offset_matrix
# Animations:
# (String) Anim Name
# (Float) Duration in Seconds
# (Int)
Number of Frames
# -- Frame (Int)
Bone Index
# -- Frame (String)
Bone Name
# -- Frame (Float)
Time in Seconds
# -- Frame (Vector3)
Translation
# -- Frame (Quaternion) Rotation
def read_animations(self,file):
for animation_index in range(0,self.animation_count):
animation_name
= read_line(file)
animation_time
= read_float(file)

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)

# If this is true, one true frame loop occured.


if current_index < last_index:
for kf in key_frames:
frame.bones.append(kf.bone_index)
frame.bone_names.append(kf.bone_name)
frame.times.append(kf.time)
frame.bone_mats.append(kf.matrix)
frame.bone_locs.append(kf.loc)
frame.bone_rots.append(kf.rot)
# Add the frame to the animation.
animation.frames.append(frame)
# Create a new frame to work with before continuing.
key_frames = []
frame = Frame()
last_index = current_index
bone_name
frame_time
loc
rot
mat

=
=
=
=
=

read_line(file)
read_float(file)
read_vector(file)
read_quaternion(file)#.inverted().copy()
matrix_from_quaternion_position(rot,loc)

# Create a new key frame.


key_frame
= KeyFrame(current_index,bone_name,frame_time,mat)
key_frame.loc = loc
key_frame.rot = rot
# Add the KeyFrame to the array to package later.
key_frames.append(key_frame)
for kf in key_frames:
frame.bones.append(kf.bone_index)
frame.bone_names.append(kf.bone_name)
frame.times.append(kf.time)
frame.bone_mats.append(kf.matrix)
frame.bone_locs.append(kf.loc)
frame.bone_rots.append(kf.rot)

# Add the frame to the animation.


animation.frames.append(frame)
################################################################################
#####
###
###
### Blender Data Creation methods
###
###
###
################################################################################
#####
def create_mesh(self):
try:
bpy.ops.object.mode_set(mode='OBJECT')
except:
ok = True
try:
bpy.ops.object.select_all(action='DESELECT')
except:
ok = True
mesh = bpy.data.meshes.new(name=self.modelName)
mesh.from_pydata(self.verts, self.edges, self.faces)
mesh.update(calc_tessface=True)
object_data_add(context, mesh)
bpy.ops.object.select_pattern(pattern=self.modelName)
obj = bpy.context.active_object
me = obj.data
bpy.ops.object.mode_set(mode = 'EDIT')
bm = bmesh.from_edit_mesh(me)
# currently blender needs both layers.
uv_layer = bm.loops.layers.uv.verify()
bm.faces.layers.tex.verify()
voffset = 0
# adjust UVs
for f in bm.faces:
index = f.index
uv_array = self.faceUVs[index]
vo = 0
for l in f.loops:
luv = l[uv_layer]
luv.uv = uv_array[vo]
vo += 1
bmesh.update_edit_mesh(me)
if self.has_armature:
if self.lock_model_on_armature_detection:
# Lock the mesh so the armature has complete control.

obj.lock_location = obj.lock_rotation = obj.lock_scale = [True,


True, True]
# Grab the Object-representation of the armature.
obj_armature
= bpy.data.objects[self.amtname]
# Select the model Object in Blender
bpy.ops.object.select_pattern(pattern=self.modelName)
# Set the parent to the Armature.
obj.parent = obj_armature
obj.parent_type = 'ARMATURE'
# Modify the Object with the Armature.
modifier = bpy.ops.object.modifier_add(type='ARMATURE')
# Return to Object mode.
bpy.ops.object.mode_set(mode = 'OBJECT')
# Create Vertex Groups here for each bone and set the Vertex accordi
ngly.
for bone in self.armature.bones:
# New VertexGroup
bpy.ops.object.vertex_group_add()
# Get the active group.
vertex_group
= obj.vertex_groups.active
vertex_group.name = bone.name
# Get the original index of the Armature.
bone_import_index = int(obj_armature[bone.name])
# Offset of the vertex to know which Vert we are dealing with.
offset_vert = 0
for vertex in me.vertices:
# Grab the Vertex's weight data.
vertex_weight_ids = self.BlendIndexArray[offset_vert]
vertex_weights
= self.BlendWeightArray[offset_vert]
# For each bone weight
offset = 0
for vert_weight_id in vertex_weight_ids:
# If this bone is the one currently being looked at, set
the weight.
if vert_weight_id == bone_import_index:
verts = []
verts.append(vertex.index)
vertex_group.add(verts, vertex_weights[offset], 'REP
LACE')
# Increment Bone Weight offset
offset += 1
# Increment Vertex offset
offset_vert += 1
# Return to Edit Mode for optimization.
bpy.ops.object.mode_set(mode = 'EDIT')
# Optimize mesh
bpy.ops.mesh.remove_doubles()
bpy.ops.mesh.tris_convert_to_quads()
# Return to Object mode to finish up.

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

= Vector((bone.head[0], bone.head[1] + 0.05, bone.head[2]))

#
########################################################################
##
# 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

for x in range(0, self.numberBones):


id = x
name = self.bone_names[id]
bpy.ops.wm.properties_add(data_path="object.data")
obj_armature[name] = id
def optimize_armature(self):
for x in range(1, self.numberBones):
bone_name = self.bone_names[x]
bone = self.armature.edit_bones[bone_name]
bone_tail = bone.tail
try:
if self.amtname == "bob_armature":
if "Neck" in bone_name:
bone.tail = bone.children[2].head
continue
if "Bip01" == bone_name:
bone.tail = bone.children[0].head
if bone.children != None:
if bone.children[0] != None:
bone.tail = bone.children[0].head
except:
bone.tail = bone_tail
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]))
if "Nub" in bone.name:
if bone.parent != None:
bone.head = bone.parent.tail
else:
bone.head = self.bone_matrix_offset_data[x].inverted().copy(
).decompose()[0]
bone.tail = Vector((bone.head[0], bone.head[1] + 0.05, bone.head
[2]))
if "Foot" in bone.name:
bone.tail = Vector((bone.head[0], bone.head[1] - 0.05, bone.head
[2]))
if bone.parent != None:
if bone.parent.tail == bone.head:
bone.use_connect = True
for x in range(1, self.numberBones):
bone_name = self.bone_names[x]
bone = self.armature.edit_bones[bone_name]
#if "Pelvis" in bone.name:
#
new_parent = bone.children[0]
#
bone.use_connect = False

#
#
#
#
#

new_parent.parent = None
bpy.ops.armature.select_all(action='DESELECT')
bone.select = True
bone.parent = new_parent
break

for x in range(0, self.numberBones):


bone_name = self.bone_names[x]
bone = self.armature.edit_bones[bone_name]
if bone.tail == bone.head:
print(bone.name)
bone.tail = Vector((bone.head[0], bone.head[1] + 0.05, bone.head
[2]))
bpy.ops.armature.select_all(action='SELECT')
bpy.ops.armature.calculate_roll(type='GLOBAL_Z')
bpy.ops.armature.select_all(action='DESELECT')
# WIP METHOD!!! This method will almost surely change, as this is the "I don
't know what the hell I'm
#
supposed to be doing" phase.
def create_animations(self):
# Set ourselves into the pose mode of the armature with nothing selected
.
bpy.ops.object.select_all(action='DESELECT')
bpy.ops.object.select_pattern(pattern=self.amtname)
bpy.ops.object.mode_set(mode='POSE')
bpy.ops.pose.select_all(action='DESELECT')
euler_rotation_offset =
euler_rotation_offset.x
euler_rotation_offset.y
euler_rotation_offset.z

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]

# Create default Parent transform rotation and location


loc_parent = Vector((0,0,0))
rot_parent = Quaternion()
mat_parent = self.set_identity()
# If the bone indeed has a parent, replace the defaults with
parent.
#if self.bone_parent[self.bone_ids[bone.name]] != -1:
#print(self.bone_names[self.bone_parent[self.bone_ids[bo
ne.name]]])
#loc_parent = loc_parent_dic[self.bone_names[self.bone_p
arent[self.bone_ids[bone.name]]]]
#rot_parent = rot_parent_dic[self.bone_names[self.bone_p
arent[self.bone_ids[bone.name]]]]
# mat_parent = mat_parent_dic[self.bone_names[self.bone_
parent[self.bone_ids[bone.name]]]]
# Grab the raw quaternion and location vector read from the

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

m[2][0] = (src[0][0] * f20 + src[1][0] * f21 + src[2][0] * f22)


m[2][1] = (src[0][1] * f20 + src[1][1] * f21 + src[2][1] * f22)
m[2][2] = (src[0][2] * f20 + src[1][2] * f21 + src[2][2] * f22)

m[2][3] = (src[0][3] * f20 + src[1][3] * f21 + src[2][3] * f22)


m[0][0]
m[0][1]
m[0][2]
m[0][3]

=
=
=
=

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

left[1][0] * right[0][1] + left[2][0


left[1][1] * right[0][1] + left[2][1
left[1][2] * right[0][1] + left[2][2
left[1][3] * right[0][1] + left[2][3
left[1][0] * right[1][1] + left[2][0
left[1][1] * right[1][1] + left[2][1
left[1][2] * right[1][1] + left[2][2
left[1][3] * right[1][1] + left[2][3
left[1][0] * right[2][1] + left[2][0
left[1][1] * right[2][1] + left[2][1
left[1][2] * right[2][1] + left[2][2
left[1][3] * right[2][1] + left[2][3
left[1][0] * right[3][1] + left[2][0
left[1][1] * right[3][1] + left[2][1
left[1][2] * right[3][1] + left[2][2
left[1][3] * right[3][1] + left[2][3

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

You might also like