Skip to content

Instantly share code, notes, and snippets.

@Dragorn421
Last active April 14, 2020 10:55
Show Gist options
  • Save Dragorn421/9332e031d0139d795c5ef1207ea05fac to your computer and use it in GitHub Desktop.
Save Dragorn421/9332e031d0139d795c5ef1207ea05fac to your computer and use it in GitHub Desktop.
Edited script to import all animations at once from an OoT64 object

Changes compared to the script from Nokaubure shipped with SharpOcarina

Import Options

  • Added option to use shadeless materials (don't use environment colors in-game) (default False)
  • Added original object scale option, blender objects will be at inverted scale (default 100, use 48 for maps?)
  • Made Texture Clamp and Texture Mirror options True by default
  • Removed option for animation to import

Features

  • Made all animations import at once, each action named animXX_FF where XX is some number and FF the number of frames
  • Removed the (useless) actions every mesh part had
  • Made armature modifier show in edit mode by default
  • Made end frame (in Timeline) be the maximum duration of animations
  • Improved console log a tiny bit (open console before importing to see progress)

Remarks

  • Importing normals only lacks ability to apply them reliably (Blender limitations), currently data is lost when merging doubles, so in this version normals are not used at all
bl_info = {
"name": "Zelda64 Importer - all animations",
"author": "SoulofDeity",
"blender": (2, 6, 0),
"location": "File > Import-Export",
"description": "Import Zelda64 - all animations",
"warning": "",
"wiki_url": "https://code.google.com/p/sods-blender-plugins/",
"tracker_url": "https://code.google.com/p/sods-blender-plugins/",
"support": 'COMMUNITY',
"category": "Import-Export"}
"""Anim stuff: RodLima http://www.facebook.com/rod.lima.96?ref=tn_tnmn"""
import bpy, os, struct, time
import mathutils
from bpy import ops
from bpy.props import *
from bpy_extras.image_utils import load_image
from bpy_extras.io_utils import ExportHelper, ImportHelper
from math import *
from mathutils import *
from struct import pack, unpack_from
from mathutils import Vector, Euler, Matrix
def splitOffset(offset):
return offset >> 24, offset & 0x00FFFFFF
def translateRotation(rot):
""" axis, angle """
return Matrix.Rotation(rot[3], 4, Vector(rot[:3]))
def validOffset(segment, offset):
seg, offset = splitOffset(offset)
if seg > 15:
return False
if offset >= len(segment[seg]):
return False
return True
def pow2(val):
i = 1
while i < val:
i <<= 1
return int(i)
def powof(val):
num, i = 1, 0
while num < val:
num <<= 1
i += 1
return int(i)
def mulVec(v1, v2):
return Vector([v1.x * v2.x, v1.y * v2.y, v1.z * v2.z])
def mulVec2(v1, v2):
return Vector([v1.x * v2.x, v1.y * v2.y, v1.z * v2.z, v1[3] * v2[3]])
class Tile:
def __init__(self):
self.texFmt, self.texBytes = 0x00, 0
self.width, self.height = 0, 0
self.rWidth, self.rHeight = 0, 0
self.txlSize = 0
self.lineSize = 0
self.rect = Vector([0, 0, 0, 0])
self.scale = Vector([1, 1])
self.ratio = Vector([1, 1])
self.clip = Vector([0, 0])
self.mask = Vector([0, 0])
self.shift = Vector([0, 0])
self.tshift = Vector([0, 0])
self.offset = Vector([0, 0])
self.data = 0x00000000
self.palette = 0x00000000
def create(self, segment):
if exportTextures:
try:
os.mkdir(fpath + "/textures")
except:
pass
#Noka here
extrastring = ""
w = self.rWidth
if int(self.clip.x) & 1 != 0:
if enableTexMirror:
w <<= 1
else:
extrastring += "#MirrorX"
h = self.rHeight
if int(self.clip.y) & 1 != 0:
if enableTexMirror:
h <<= 1
else:
extrastring += "#MirrorY"
if int(self.clip.x) & 2 != 0 and enableTexClamp == False:
extrastring += "#ClampX"
if int(self.clip.y) & 2 != 0 and enableTexClamp == False:
extrastring += "#ClampY"
file = open(fpath + ("/textures/%08X" % self.data) + str(extrastring) + ".tga", 'wb')
if self.texFmt == 0x40 or self.texFmt == 0x48 or self.texFmt == 0x50:
file.write(pack("<BBBHHBHHHHBB", 0, 1, 1, 0, 256, 24, 0, 0, w, h, 8, 0))
self.writePalette(file, segment)
else:
file.write(pack("<BBBHHBHHHHBB", 0, 0, 2, 0, 0, 0, 0, 0, w, h, 32, 8))
if int(self.clip.y) & 1 != 0 and enableTexMirror:
self.writeImageData(file, segment, True)
else:
self.writeImageData(file, segment)
file.close()
try:
tex = bpy.data.textures.new(name="tex_%08X" % self.data, type='IMAGE')
img = load_image(fpath + ("/textures/%08X" % self.data) + str(extrastring) + ".tga")
if img:
tex.image = img
if int(self.clip.x) & 2 != 0 and enableTexClamp:
img.use_clamp_x = True
if int(self.clip.y) & 2 != 0 and enableTexClamp:
img.use_clamp_y = True
mtl = bpy.data.materials.new(name="mtl_%08X" % self.data)
# Dragorn421 here
if enableShadelessMaterials:
mtl.use_shadeless = True
#mtl.use_shadeless = True
#
mt = mtl.texture_slots.add()
mt.texture = tex
mt.texture_coords = 'UV'
mt.use_map_color_diffuse = True
if (img.depth == 32):
mt.use_map_alpha = True
tex.use_mipmap = True
tex.use_interpolation = True
tex.use_alpha = True
mtl.use_transparency = True
mtl.alpha = 0.0
mtl.game_settings.alpha_blend = 'ALPHA'
return mtl
except:
return None
def calculateSize(self):
maxTxl, lineShift = 0, 0
if self.texFmt == 0x00 or self.texFmt == 0x40:
maxTxl = 4096
lineShift = 4
elif self.texFmt == 0x60 or self.texFmt == 0x80:
maxTxl = 8192
lineShift = 4
elif self.texFmt == 0x08 or self.texFmt == 0x48:
maxTxl = 2048
lineShift = 3
elif self.texFmt == 0x68 or self.texFmt == 0x88:
maxTxl = 4096
lineShift = 3
elif self.texFmt == 0x10 or self.texFmt == 0x70:
maxTxl = 2048
lineShift = 2
elif self.texFmt == 0x50 or self.texFmt == 0x90:
maxTxl = 2048
lineShift = 0
elif self.texFmt == 0x18:
maxTxl = 1024
lineShift = 2
lineWidth = self.lineSize << lineShift
self.lineSize = lineWidth
tileWidth = self.rect.z - self.rect.x + 1
tileHeight = self.rect.w - self.rect.y + 1
maskWidth = 1 << int(self.mask.x)
maskHeight = 1 << int(self.mask.y)
lineHeight = 0
if lineWidth > 0:
lineHeight = min(int(maxTxl / lineWidth), tileHeight)
if self.mask.x > 0 and (maskWidth * maskHeight) <= maxTxl:
self.width = maskWidth
elif (tileWidth * tileHeight) <= maxTxl:
self.width = tileWidth
else:
self.width = lineWidth
if self.mask.y > 0 and (maskWidth * maskHeight) <= maxTxl:
self.height = maskHeight
elif (tileWidth * tileHeight) <= maxTxl:
self.height = tileHeight
else:
self.height = lineHeight
clampWidth, clampHeight = 0, 0
if self.clip.x == 1:
clampWidth = tileWidth
else:
clampWidth = self.width
if self.clip.y == 1:
clampHeight = tileHeight
else:
clampHeight = self.height
if maskWidth > self.width:
self.mask.x = powof(self.width)
maskWidth = 1 << int(self.mask.x)
if maskHeight > self.height:
self.mask.y = powof(self.height)
maskHeight = 1 << int(self.mask.y)
if int(self.clip.x) & 2 != 0:
self.rWidth = pow2(clampWidth)
elif int(self.clip.x) & 1 != 0:
self.rWidth = pow2(maskWidth)
else:
self.rWidth = pow2(self.width)
if int(self.clip.y) & 2 != 0:
self.rHeight = pow2(clampHeight)
elif int(self.clip.y) & 1 != 0:
self.rHeight = pow2(maskHeight)
else:
self.rHeight = pow2(self.height)
self.shift.x, self.shift.y = 1.0, 1.0
if self.tshift.x > 10:
self.shift.x = 1 << int(16 - self.tshift.x)
elif self.tshift.x > 0:
self.shift.x /= 1 << int(self.tshift.x)
if self.tshift.y > 10:
self.shift.y = 1 << int(16 - self.tshift.y)
elif self.tshift.y > 0:
self.shift.y /= 1 << int(self.tshift.y)
self.ratio.x = (self.scale.x * self.shift.x) / self.rWidth
if not enableToon:
self.ratio.x /= 32;
if int(self.clip.x) & 1 != 0 and enableTexMirror:
self.ratio.x /= 2
self.offset.x = self.rect.x
self.ratio.y = (self.scale.y * self.shift.y) / self.rHeight
if not enableToon:
self.ratio.y /= 32;
if int(self.clip.y) & 1 != 0 and enableTexMirror:
self.ratio.y /= 2
self.offset.y = 1.0 + self.rect.y
def writePalette(self, file, segment):
if self.texFmt == 0x40:
palSize = 16
else:
palSize = 256
if not validOffset(segment, self.palette + int(palSize * 2) - 1):
for i in range(256):
file.write(pack("L", 0))
return
seg, offset = splitOffset(self.palette)
for i in range(256):
if i < palSize:
color = unpack_from(">H", segment[seg], offset + (i << 1))[0]
r = int(8 * ((color >> 11) & 0x1F))
g = int(8 * ((color >> 6) & 0x1F))
b = int(8 * ((color >> 1) & 0x1F))
file.write(pack("BBB", b, g, r))
else:
file.write(pack("BBB", 0, 0, 0))
def writeImageData(self, file, segment, fy=False, df=False):
if fy == True:
dir = (0, self.rHeight, 1)
else:
dir = (self.rHeight - 1, -1, -1)
if self.texFmt == 0x40 or self.texFmt == 0x60 or self.texFmt == 0x80 or self.texFmt == 0x90:
bpp = 0.5
elif self.texFmt == 0x00 or self.texFmt == 0x08 or self.texFmt == 0x10 or self.texFmt == 0x70:
bpp = 2
elif self.texFmt == 0x48 or self.texFmt == 0x50 or self.texFmt == 0x68 or self.texFmt == 0x88:
bpp = 1
else:
bpp = 4
lineSize = self.rWidth * bpp
if not validOffset(segment, self.data + int(self.rHeight * lineSize) - 1):
size = self.rWidth * self.rHeight
if int(self.clip.x) & 1 != 0 and enableTexMirror:
size *= 2
if int(self.clip.y) & 1 != 0 and enableTexMirror:
size *= 2
for i in range(size):
if self.texFmt == 0x40 or self.texFmt == 0x48 or self.texFmt == 0x50:
file.write(pack("B", 0))
else:
file.write(pack(">L", 0x000000FF))
return
seg, offset = splitOffset(self.data)
for i in range(dir[0], dir[1], dir[2]):
off = offset + int(i * lineSize)
line = []
j = 0
while j < int(self.rWidth * bpp):
if bpp < 2:
color = unpack_from("B", segment[seg], off + int(floor(j)))[0]
elif bpp == 2:
color = unpack_from(">H", segment[seg], off + j)[0]
else:
color = unpack_from(">L", segment[seg], off + j)[0]
if self.texFmt == 0x40:
if floor(j) == j:
a = color >> 4
else:
a = color & 0x0F
elif self.texFmt == 0x48 or self.texFmt == 0x50:
a = color
elif self.texFmt == 0x00 or self.texFmt == 0x08 or self.texFmt == 0x10:
r = int(8 * ((color >> 11) & 0x1F))
g = int(8 * ((color >> 6) & 0x1F))
b = int(8 * ((color >> 1) & 0x1F))
a = int(255 * (color & 0x01))
elif self.texFmt == 0x80:
if floor(j) == j:
r = int(16 * (color >> 4))
else:
r = int(16 * (color & 0x0F))
g = r
b = g
a = 0xFF
elif self.texFmt == 0x88:
r = color
g = r
b = g
a = 0xFF
elif self.texFmt == 0x68:
r = int(16 * (color >> 4))
g = r
b = g
a = int(16 * (color & 0x0F))
elif self.texFmt == 0x70:
r = color >> 8
g = r
b = g
a = color & 0xFF
elif self.texFmt == 0x18:
r = color >> 24
g = (color >> 16) & 0xFF
b = (color >> 8) & 0xFF
a = color & 0xFF
else:
r = 0
g = 0
b = 0
a = 0xFF
if self.texFmt == 0x40 or self.texFmt == 0x48 or self.texFmt == 0x50:
line.extend([a])
else:
line.extend([(b << 24) | (g << 16) | (r << 8) | a])
j += bpp
if self.texFmt == 0x40 or self.texFmt == 0x48 or self.texFmt == 0x50:
file.write(pack("B" * len(line), *line))
else:
file.write(pack(">" + "L" * len(line), *line))
if int(self.clip.x) & 1 != 0 and enableTexMirror:
line.reverse()
if self.texFmt == 0x40 or self.texFmt == 0x48 or self.texFmt == 0x50:
file.write(pack("B" * len(line), *line))
else:
file.write(pack(">" + "L" * len(line), *line))
if int(self.clip.y) & 1 != 0 and df == False and enableTexMirror:
if fy == True:
self.writeImageData(file, segment, False, True)
else:
self.writeImageData(file, segment, True, True)
class Vertex:
def __init__(self):
self.pos = Vector([0, 0, 0])
self.uv = Vector([0, 0])
self.normal = Vector([0, 0, 0])
self.color = [0, 0, 0, 0]
self.limb = None
def read(self, segment, offset):
if not validOffset(segment, offset + 16):
return
seg, offset = splitOffset(offset)
self.pos.x = unpack_from(">h", segment[seg], offset)[0]
self.pos.z = unpack_from(">h", segment[seg], offset + 2)[0]
self.pos.y = -unpack_from(">h", segment[seg], offset + 4)[0]
# Dragorn421 here
global scaleFactor
self.pos *= scaleFactor
#self.pos /= 48 # UDK Scale
#
self.uv.x = float(unpack_from(">h", segment[seg], offset + 8)[0])
self.uv.y = float(unpack_from(">h", segment[seg], offset + 10)[0])
self.normal.x = 0.00781250 * unpack_from("b", segment[seg], offset + 12)[0]
self.normal.z = 0.00781250 * unpack_from("b", segment[seg], offset + 13)[0]
self.normal.y = 0.00781250 * unpack_from("b", segment[seg], offset + 14)[0]
self.color[0] = min(0.00392157 * segment[seg][offset + 12], 1.0)
self.color[1] = min(0.00392157 * segment[seg][offset + 13], 1.0)
self.color[2] = min(0.00392157 * segment[seg][offset + 14], 1.0)
if bpy.app.version >= (2, 79, 0):
self.color[3] = min(0.00392157 * segment[seg][offset + 15], 1.0)
class Mesh:
def __init__(self):
self.verts, self.uvs, self.colors, self.faces = [], [], [], []
self.vgroups = {}
# Dragorn421 here
# import normals
self.normals = []
#
def create(self, hierarchy, offset):
if len(self.faces) == 0:
return
me = bpy.data.meshes.new("me_%08X" % offset)
ob = bpy.data.objects.new("ob_%08X" % offset, me)
bpy.context.scene.objects.link(ob)
bpy.context.scene.objects.active = ob
me.vertices.add(len(self.verts))
for i in range(len(self.verts)):
me.vertices[i].co = self.verts[i]
me.tessfaces.add(len(self.faces))
vcd = me.tessface_vertex_colors.new().data
for i in range(len(self.faces)):
me.tessfaces[i].vertices = self.faces[i]
vcd[i].color1 = self.colors[i * 3 + 2]
vcd[i].color2 = self.colors[i * 3 + 1]
vcd[i].color3 = self.colors[i * 3]
uvd = me.tessface_uv_textures.new().data
for i in range(len(self.faces)):
if self.uvs[i * 4 + 3]:
if not self.uvs[i * 4 + 3].name in me.materials:
me.materials.append(self.uvs[i * 4 + 3])
uvd[i].image = self.uvs[i * 4 + 3].texture_slots[0].texture.image
uvd[i].uv[0] = self.uvs[i * 4 + 2]
uvd[i].uv[1] = self.uvs[i * 4 + 1]
uvd[i].uv[2] = self.uvs[i * 4]
me.calc_normals()
me.validate()
me.update()
# Dragorn421 here
# import normals
"""
# doesn't work, blender eventually automatically recalculates normals
for i in range(len(self.verts)):
print(i)
print(self.normals[i])
me.vertices[i].normal = self.normals[i]
print(me.vertices[i].normal)
"""
"""
# https://blender.stackexchange.com/questions/104650/there-is-a-way-to-add-custom-split-normal-use-python-api/104664#104664
# this does set normals in a way that would carry through to oot
# however it seems like removing double vertices ruins most of it
me.normals_split_custom_set([(0, 0, 0) for l in me.loops])
me.normals_split_custom_set_from_vertices(self.normals)
"""
#
if hierarchy:
for name, vgroup in self.vgroups.items():
grp = ob.vertex_groups.new(name)
for v in vgroup:
grp.add([v], 1.0, 'REPLACE')
ob.parent = hierarchy.armature
mod = ob.modifiers.new(hierarchy.name, 'ARMATURE')
mod.object = hierarchy.armature
mod.use_bone_envelopes = False
mod.use_vertex_groups = True
# Dragorn421 here
mod.show_in_editmode = True
mod.show_on_cage = True
#
# Dragorn421 here
# don't have one animation per object part
# TODO what was this for? does removing it break some other tools?
"""
if hierarchy is not None:
ob.animation_data_create()
action = bpy.data.actions.new(hierarchy.name)
ob.animation_data.action = action
"""
#
#print("Action", action)
class Limb:
def __init__(self):
self.parent, self.child, self.sibling = -1, -1, -1
self.pos = Vector([0, 0, 0])
self.near, self.far = 0x00000000, 0x00000000
self.poseBone = None
self.poseLocPath, self.poseRotPath = None, None
self.poseLoc, self.poseRot = Vector([0, 0, 0]), None
def read(self, segment, offset, actuallimb, BoneCount):
seg, offset = splitOffset(offset)
rot_offset = offset & 0xFFFFFF
rot_offset += (0 * (BoneCount * 6 + 8));
self.pos.x = unpack_from(">h", segment[seg], offset)[0]
self.pos.z = unpack_from(">h", segment[seg], offset + 2)[0]
self.pos.y = -unpack_from(">h", segment[seg], offset + 4)[0]
# Dragorn421 here
global scaleFactor
self.pos *= scaleFactor
#self.pos /= 48 # UDK Scale
#
self.child = unpack_from("b", segment[seg], offset + 6)[0]
self.sibling = unpack_from("b", segment[seg], offset + 7)[0]
self.near = unpack_from(">L", segment[seg], offset + 8)[0]
self.far = unpack_from(">L", segment[seg], offset + 12)[0]
self.poseLoc.x = unpack_from(">h", segment[seg], rot_offset)[0]
self.poseLoc.z = unpack_from(">h", segment[seg], rot_offset + 2)[0]
self.poseLoc.y = unpack_from(">h", segment[seg], rot_offset + 4)[0]
#print(" Limb ", actuallimb, ":", self.poseLoc.x, ",", self.poseLoc.z, ",", self.poseLoc.y)
class Hierarchy:
def __init__(self):
self.name, self.offset = "", 0x00000000
self.limbCount, self.dlistCount = 0x00, 0x00
self.limb = []
self.armature = None
def read(self, segment, offset):
if not validOffset(segment, offset + 9):
return
self.name = "sk_%08X" % offset
self.offset = offset
seg, offset = splitOffset(offset)
limbIndex_offset = unpack_from(">L", segment[seg], offset)[0]
if not validOffset(segment, limbIndex_offset):
print(" ERROR: Limb index table 0x%08X out of range" % limbIndex_offset)
return
limbIndex_seg, limbIndex_offset = splitOffset(limbIndex_offset)
self.limbCount = segment[seg][offset + 4]
self.dlistCount = segment[seg][offset + 8]
for i in range(self.limbCount):
limb_offset = unpack_from(">L", segment[limbIndex_seg], limbIndex_offset + 4 * i)[0]
limb = Limb()
limb.index = i
self.limb.extend([limb])
if validOffset(segment, limb_offset + 12):
limb.read(segment, limb_offset, i, self.limbCount)
else:
print(" ERROR: Limb 0x%02X offset 0x%08X out of range" % (i, limb_offset))[0]
self.limb[0].pos = Vector([0, 0, 0])
self.initLimbs(0x00)
def create(self):
rx, ry, rz = 90,0,0
if (bpy.context.active_object):
bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
for i in bpy.context.selected_objects:
i.select = False
self.armature = bpy.data.objects.new(self.name, bpy.data.armatures.new("armature"))
self.armature.show_x_ray = True
self.armature.data.draw_type = 'STICK'
bpy.context.scene.objects.link(self.armature)
bpy.context.scene.objects.active = self.armature
bpy.ops.object.mode_set(mode='EDIT', toggle=False)
for i in range(self.limbCount):
bone = self.armature.data.edit_bones.new("limb_%02i" % i)
bone.use_deform = True
bone.head = self.limb[i].pos
for i in range(self.limbCount):
bone = self.armature.data.edit_bones["limb_%02i" % i]
if (self.limb[i].parent != -1):
bone.parent = self.armature.data.edit_bones["limb_%02i" % self.limb[i].parent]
bone.use_connect = False
bone.tail = bone.head + Vector([0, 0, 0.0001])
bpy.ops.object.mode_set(mode='OBJECT')
def initLimbs(self, i):
if (self.limb[i].child > -1 and self.limb[i].child != i):
self.limb[self.limb[i].child].parent = i
self.limb[self.limb[i].child].pos += self.limb[i].pos
self.initLimbs(self.limb[i].child)
if (self.limb[i].sibling > -1 and self.limb[i].sibling != i):
self.limb[self.limb[i].sibling].parent = self.limb[i].parent
self.limb[self.limb[i].sibling].pos += self.limb[self.limb[i].parent].pos
self.initLimbs(self.limb[i].sibling)
def getMatrixLimb(self, offset):
j = 0
index = (offset & 0x00FFFFFF) / 0x40
for i in range(self.limbCount):
if self.limb[i].near != 0:
if (j == index):
return self.limb[i]
j += 1
return self.limb[0]
class F3DZEX:
def __init__(self):
self.segment, self.vbuf, self.tile = [], [], []
self.animTotal = 0
self.TimeLine = 0
self.TimeLinePosition = 0
self.displaylists = []
for i in range(16):
self.segment.extend([[]])
self.vbuf.extend([Vertex()])
for i in range(2):
self.tile.extend([Tile()])
self.vbuf.extend([Vertex()])
for i in range(14 + 32):
self.vbuf.extend([Vertex()])
self.curTile = 0
self.material = []
self.hierarchy = []
self.resetCombiner()
def loaddisplaylists(self, path):
try:
file = open(path, 'rb')
self.displaylists = file.readlines()
file.close()
print("Loaded the display list list successfully!")
except:
print("Did not find displaylists.txt!")
pass
def setSegment(self, seg, path):
try:
file = open(path, 'rb')
self.segment[seg] = file.read()
file.close()
except:
pass
def locateHierarchies(self):
data = self.segment[0x06]
for i in range(0, len(data), 4):
if data[i] == 0x06 and (data[i+3] & 3) == 0 and data[i+4] != 0:
offset = unpack_from(">L", data, i)[0] & 0x00FFFFFF
if offset < len(data):
offset_end = offset + (data[i+4] << 2)
if offset_end < len(data):
j = offset
while j < offset_end:
if data[j] != 0x06 or (data[j+3] & 3) != 0 or (unpack_from(">L", data, j)[0] & 0x00FFFFFF) > len(data):
break
j += 4
if (j == i):
j |= 0x06000000
print(" hierarchy found at 0x%08X" % j)
h = Hierarchy()
h.read(self.segment, j)
self.hierarchy.extend([h])
def locateAnimations(self):
data = self.segment[0x06]
self.animation = []
self.offsetAnims = []
# Dragorn421 here
self.durationAnims = []
#
for i in range(0, len(data), 4):
if ((data[i] == 0) and (data[i+1] > 1) and
(data[i+2] == 0) and (data[i+3] == 0) and
(data[i+4] == 0x06) and
(((data[i+5] << 16)|(data[i+6] << 8)|data[i+7]) < len(data)) and
(data[i+8] == 0x06) and
(((data[i+9] << 16)|(data[i+10] << 8)|data[i+11]) < len(data)) and
(data[i+14] == 0) and (data[i+15] == 0)):
print(" Anims found at %08X" % i, "Frames:", data[i+1] & 0x00FFFFFF)
self.animation.extend([i])
self.offsetAnims.extend([i])
self.offsetAnims[self.animTotal] = (0x06 << 24) | i
# Dragorn421 here
self.durationAnims.extend([data[i+1] & 0x00FFFFFF])
#
self.animTotal += 1
if(self.animTotal > 0):
print(" Total Anims :", self.animTotal)
def locateExternAnimations(self):
data = self.segment[0x0F]
self.animation = []
self.offsetAnims = []
for i in range(0, len(data), 4):
if ((data[i] == 0) and (data[i+1] > 1) and
(data[i+2] == 0) and (data[i+3] == 0) and
(data[i+4] == 0x06) and
(((data[i+5] << 16)|(data[i+6] << 8)|data[i+7]) < len(data)) and
(data[i+8] == 0x06) and
(((data[i+9] << 16)|(data[i+10] << 8)|data[i+11]) < len(data)) and
(data[i+14] == 0) and (data[i+15] == 0)):
print(" Ext Anims found at %08X" % i, "Frames:", data[i+1] & 0x00FFFFFF)
self.animation.extend([i])
self.offsetAnims.extend([i])
self.offsetAnims[self.animTotal] = (0x0F << 24) | i
self.animTotal += 1
if(self.animTotal > 0):
print(" Total Anims :", self.animTotal)
def locateLinkAnimations(self):
data = self.segment[0x04]
self.animation = []
self.offsetAnims = []
self.animFrames = []
self.animTotal = -1
if (len( self.segment[0x04] ) > 0):
if (MajorasAnims):
for i in range(0xD000, 0xE4F8, 8):
self.animTotal += 1
self.animation.extend([self.animTotal])
self.animFrames.extend([self.animTotal])
self.offsetAnims.extend([self.animTotal])
self.offsetAnims[self.animTotal] = unpack_from(">L", data, i + 4)[0]
self.animFrames[self.animTotal] = unpack_from(">h", data, i)[0]
#print("- Animation #", self.animTotal+1, "offset: %07X" % self.offsetAnims[self.animTotal], "frames:", self.animFrames[self.animTotal])
else:
for i in range(0x2310, 0x34F8, 8):
self.animTotal += 1
self.animation.extend([self.animTotal])
self.animFrames.extend([self.animTotal])
self.offsetAnims.extend([self.animTotal])
self.offsetAnims[self.animTotal] = unpack_from(">L", data, i + 4)[0]
self.animFrames[self.animTotal] = unpack_from(">h", data, i)[0]
#print("- Animation #", self.animTotal+1, "offset: %07X" % self.offsetAnims[self.animTotal], "frames:", self.animFrames[self.animTotal])
print(" Link has come to town!!!!")
if ( (len( self.segment[0x07] ) > 0) and (self.animTotal > 0)):
self.buildLinkAnimations(self.hierarchy[0], 0)
def importMap(self):
data = self.segment[0x03]
for i in range(0, len(data), 8):
if (data[i] == 0x0A and data[i+4] == 0x03):
mho = (data[i+5] << 16) | (data[i+6] << 8) | data[i+7]
if (mho < len(data)):
type = data[mho]
count = data[mho+1]
seg = data[mho+4]
start = (data[mho+5] << 16) | (data[mho+6] << 8) | data[mho+7]
end = (data[mho+9] << 16) | (data[mho+10] << 8) | data[mho+11]
print(" Mesh Type: %02X" % type)
if (data[mho+4] == 0x03 and start < end and end < len(data)):
print(" start %08X" % start)
print(" end %08X" % end)
if (type == 0):
for j in range(start, end, 4):
self.buildDisplayList(None, [None], unpack_from(">L", data, j)[0], False)
elif (type == 1 and count == 1):
#Noka here
end = start + 8
for j in range(start, end, 4):
self.buildDisplayList(None, [None], unpack_from(">L", data, j)[0], False)
elif (type == 2):
for j in range(start, end, 16):
near = (data[j+8] << 24)|(data[j+9] << 16)|(data[j+10] << 8)|data[j+11]
far = (data[j+12] << 24)|(data[j+13] << 16)|(data[j+14] << 8)|data[j+15]
if (near != 0):
self.buildDisplayList(None, [None], near, False)
elif (far != 0):
self.buildDisplayList(None, [None], far, False)
return
elif (data[i] == 0x14):
break
print("ERROR: Map header not found")
def importObj(self):
print("\nLocating hierarchies...")
self.locateHierarchies()
if len(self.hierarchy) == 0:
print("\nFound zilch. Using display lists, then...")
if len(self.displaylists) == 0:
print("\n...but none were found...")
for dll in self.displaylists:
s = dll.decode("utf-8")
self.buildDisplayList(None, 0, int(s, 0), True)
return
for hierarchy in self.hierarchy:
print("\nBuilding hierarchy '%s'..." % hierarchy.name)
hierarchy.create()
for i in range(hierarchy.limbCount):
limb = hierarchy.limb[i]
if limb.near != 0:
if validOffset(self.segment, limb.near):
print(" 0x%02X : building display lists..." % i)
self.resetCombiner()
self.buildDisplayList(hierarchy, limb, limb.near, False)
else:
print(" 0x%02X : out of range" % i)
else:
print(" 0x%02X : n/a" % i)
if len(self.hierarchy) > 0:
bpy.context.scene.objects.active = self.hierarchy[0].armature
self.hierarchy[0].armature.select = True
bpy.ops.object.mode_set(mode='POSE', toggle=False)
# Dragorn421 here
global AnimtoPlay
#
if (AnimtoPlay > 0):
bpy.context.scene.frame_end = 1
if(ExternalAnimes and len(self.segment[0x0F]) > 0):
self.locateExternAnimations()
else:
self.locateAnimations()
if len(self.animation) > 0:
# Dragorn421 here
armature = self.hierarchy[0].armature
if armature.animation_data is None:
armature.animation_data_create()
for i in range(len(self.animation)):
AnimtoPlay = i + 1
print(" Loading animation %d" % AnimtoPlay)
action = bpy.data.actions.new('anim%d_%d' % (AnimtoPlay, self.durationAnims[i]))
# action.id_root changes became irrelevant after commenting out the code adding individual actions to each obXXXXXX child object
# ?????? "Error: Could not set action 'anim1' onto ID 'ARarmature', as it does not have suitably rooted paths for this purpose"
# api says "DO NOT CHANGE UNLESS YOU KNOW WHAT YOU ARE DOING" PepeHands
#action.id_root = 'ARMATURE'
# not sure what users an action is supposed to have, or what it should be linked to
action.use_fake_user = True
armature.animation_data.action = action
self.buildAnimations(self.hierarchy[0], 0)
# ?????? fixes subsequent (when setting action after import) "Error: Could not set action 'anim1' onto ID 'OBsk_06007958', as it does not have suitably rooted paths for this purpose" (I still don't understand this error message though)
#action.id_root = 'OBJECT'
bpy.context.scene.frame_end = max(self.durationAnims)
#self.buildAnimations(self.hierarchy[0], 0)
#
else:
self.locateLinkAnimations()
else:
print(" Load anims OFF.")
def resetCombiner(self):
self.primColor = Vector([1.0, 1.0, 1.0])
self.envColor = Vector([1.0, 1.0, 1.0])
if bpy.app.version >= (2, 79, 0):
self.vertexColor = Vector([1.0, 1.0, 1.0, 1.0])
else:
self.vertexColor = Vector([1.0, 1.0, 1.0])
self.shadeColor = Vector([1.0, 1.0, 1.0])
def getCombinerColor(self):
cc = Vector([1.0, 1.0, 1.0])
if enablePrimColor:
cc = mulVec(cc, self.primColor)
if enableEnvColor:
cc = mulVec(cc, self.envColor)
if vertexMode == 'COLORS':
if bpy.app.version >= (2, 79, 0):
cc = Vector([1.0, 1.0, 1.0, 1.0])
cc = mulVec2(cc, self.vertexColor)
else:
cc = mulVec(cc, self.vertexColor)
elif vertexMode == 'NORMALS':
cc = mulVec(cc, self.shadeColor)
return cc
def buildDisplayList(self, hierarchy, limb, offset, static):
if static:
data = self.segment[0x6]
else:
data = self.segment[offset >> 24]
mesh = Mesh()
has_tex = False
material = None
if hierarchy:
matrix = [limb]
else:
matrix = [None]
for i in range(offset & 0x00FFFFFF, len(data), 8):
w0 = unpack_from(">L", data, i)[0]
w1 = unpack_from(">L", data, i + 4)[0]
if data[i] == 0x01:
count = (data[i + 1] << 4) | (data[i + 2] >> 4)
index = (data[i + 3] >> 1) - count
offset = unpack_from(">L", data, i + 4)[0]
if validOffset(self.segment, offset + int(16 * count) - 1):
for j in range(count):
self.vbuf[index + j].read(self.segment, offset + 16 * j)
if hierarchy:
self.vbuf[index + j].limb = matrix[len(matrix) - 1]
if self.vbuf[index + j].limb:
self.vbuf[index + j].pos += self.vbuf[index + j].limb.pos
elif data[i] == 0x02:
index = ((data[i + 2] & 0x0F) << 3) | (data[i + 3] >> 1)
if data[i + 1] == 0x10:
self.vbuf[index].normal.x = 0.00781250 * unpack_from("b", data, i + 4)[0]
self.vbuf[index].normal.z = 0.00781250 * unpack_from("b", data, i + 5)[0]
self.vbuf[index].normal.y = 0.00781250 * unpack_from("b", data, i + 6)[0]
self.vbuf[index].color = 0.00392157 * unpack_from("BBBB", data, i + 4)[0]
elif data[i + 1] == 0x14:
self.vbuf[index].uv.x = float(unpack_from(">h", data, i + 4)[0])
self.vbuf[index].uv.y = float(unpack_from(">h", data, i + 6)[0])
elif data[i] == 0x05 or data[i] == 0x06:
if has_tex:
material = None
for j in range(len(self.material)):
if self.material[j].name == "mtl_%08X" % self.tile[0].data:
material = self.material[j]
break
if material == None:
material = self.tile[0].create(self.segment)
if material:
self.material.extend([material])
has_tex = False
v1, v2 = None, None
vi1, vi2 = -1, -1
if not importTextures:
material = None
count = 0
try:
for j in range(1, (data[i] - 4) * 4):
if j != 4:
v3 = self.vbuf[data[i + j] >> 1]
vi3 = -1
for k in range(len(mesh.verts)):
if mesh.verts[k] == (v3.pos.x, v3.pos.y, v3.pos.z):
vi3 = k
break
if vi3 == -1:
mesh.verts.extend([(v3.pos.x, v3.pos.y, v3.pos.z)])
# Dragorn421 here
# import normals
mesh.normals.extend([(v3.normal.x, v3.normal.y, v3.normal.z)])
#
vi3 = len(mesh.verts) - 1
count += 1
if j == 1 or j == 5:
v1 = v3
vi1 = vi3
elif j == 2 or j == 6:
v2 = v3
vi2 = vi3
elif j == 3 or j == 7:
sc = (((v3.normal.x + v3.normal.y + v3.normal.z) / 3) + 1.0) / 2
if bpy.app.version >= (2, 79, 0):
self.vertexColor = Vector([v3.color[0], v3.color[1], v3.color[2], v3.color[3]])
else:
self.vertexColor = Vector([v3.color[0], v3.color[1], v3.color[2]])
self.shadeColor = Vector([sc, sc, sc])
mesh.colors.extend([self.getCombinerColor()])
sc = (((v2.normal.x + v2.normal.y + v2.normal.z) / 3) + 1.0) / 2
if bpy.app.version >= (2, 79, 0):
self.vertexColor = Vector([v2.color[0], v2.color[1], v2.color[2], v2.color[3]])
else:
self.vertexColor = Vector([v2.color[0], v2.color[1], v2.color[2]])
self.shadeColor = Vector([sc, sc, sc])
mesh.colors.extend([self.getCombinerColor()])
sc = (((v1.normal.x + v1.normal.y + v1.normal.z) / 3) + 1.0) / 2
if bpy.app.version >= (2, 79, 0):
self.vertexColor = Vector([v1.color[0], v1.color[1], v1.color[2], v1.color[3]])
else:
self.vertexColor = Vector([v1.color[0], v1.color[1], v1.color[2]])
self.shadeColor = Vector([sc, sc, sc])
mesh.colors.extend([self.getCombinerColor()])
mesh.uvs.extend([(self.tile[0].offset.x + v3.uv.x * self.tile[0].ratio.x, self.tile[0].offset.y - v3.uv.y * self.tile[0].ratio.y),
(self.tile[0].offset.x + v2.uv.x * self.tile[0].ratio.x, self.tile[0].offset.y - v2.uv.y * self.tile[0].ratio.y),
(self.tile[0].offset.x + v1.uv.x * self.tile[0].ratio.x, self.tile[0].offset.y - v1.uv.y * self.tile[0].ratio.y),
material])
if hierarchy:
if v3.limb:
if not (("limb_%02i" % v3.limb.index) in mesh.vgroups):
mesh.vgroups["limb_%02i" % v3.limb.index] = []
mesh.vgroups["limb_%02i" % v3.limb.index].extend([vi3])
if v2.limb:
if not (("limb_%02i" % v2.limb.index) in mesh.vgroups):
mesh.vgroups["limb_%02i" % v2.limb.index] = []
mesh.vgroups["limb_%02i" % v2.limb.index].extend([vi2])
if v1.limb:
if not (("limb_%02i" % v1.limb.index) in mesh.vgroups):
mesh.vgroups["limb_%02i" % v1.limb.index] = []
mesh.vgroups["limb_%02i" % v1.limb.index].extend([vi1])
mesh.faces.extend([(vi1, vi2, vi3)])
except:
for i in range(count):
mesh.verts.pop()
elif data[i] == 0xD7:
# for i in range(2):
# if ((w1 >> 16) & 0xFFFF) < 0xFFFF:
# self.tile[i].scale.x = ((w1 >> 16) & 0xFFFF) * 0.0000152587891
# else:
# self.tile[i].scale.x = 1.0
# if (w1 & 0xFFFF) < 0xFFFF:
# self.tile[i].scale.y = (w1 & 0xFFFF) * 0.0000152587891
# else:
# self.tile[i].scale.y = 1.0
pass
elif data[i] == 0xD8 and enableMatrices:
if hierarchy and len(matrix) > 1:
matrix.pop()
elif data[i] == 0xDA and enableMatrices:
if hierarchy and data[i + 4] == 0x0D:
if (data[i + 3] & 0x04) == 0:
matrixLimb = hierarchy.getMatrixLimb(unpack_from(">L", data, i + 4)[0])
if (data[i + 3] & 0x02) == 0:
newMatrixLimb = Limb()
newMatrixLimb.index = matrixLimb.index
newMatrixLimb.pos = (Vector([matrixLimb.pos.x, matrixLimb.pos.y, matrixLimb.pos.z]) + matrix[len(matrix) - 1].pos) / 2
matrixLimb = newMatrixLimb
if (data[i + 3] & 0x01) == 0:
matrix.extend([matrixLimb])
else:
matrix[len(matrix) - 1] = matrixLimb
else:
matrix.extend([matrix[len(matrix) - 1]])
elif hierarchy:
print("unknown limb %08X %08X" % (w0, w1))
elif data[i] == 0xDE:
mesh.create(hierarchy, offset)
mesh.__init__()
offset = (offset >> 24) | i + 8
if validOffset(self.segment, w1):
self.buildDisplayList(hierarchy, limb, w1, False)
if data[i + 1] != 0x00:
return
elif data[i] == 0xDF:
mesh.create(hierarchy, offset)
return
elif data[i] == 0xE7:
mesh.create(hierarchy, offset)
mesh.__init__()
offset = (offset >> 24) | i
elif data[i] == 0xF0:
self.palSize = ((w1 & 0x00FFF000) >> 13) + 1
elif data[i] == 0xF2:
self.tile[self.curTile].rect.x = (w0 & 0x00FFF000) >> 14
self.tile[self.curTile].rect.y = (w0 & 0x00000FFF) >> 2
self.tile[self.curTile].rect.z = (w1 & 0x00FFF000) >> 14
self.tile[self.curTile].rect.w = (w1 & 0x00000FFF) >> 2
self.tile[self.curTile].width = (self.tile[self.curTile].rect.z - self.tile[self.curTile].rect.x) + 1
self.tile[self.curTile].height = (self.tile[self.curTile].rect.w - self.tile[self.curTile].rect.y) + 1
self.tile[self.curTile].texBytes = int(self.tile[self.curTile].width * self.tile[self.curTile].height) << 1
if (self.tile[self.curTile].texBytes >> 16) == 0xFFFF:
self.tile[self.curTile].texBytes = self.tile[self.curTile].size << 16 >> 15
self.tile[self.curTile].calculateSize()
elif data[i] == 0xF4 or data[i] == 0xE4 or data[i] == 0xFE or data[i] == 0xFF:
print("%08X : %08X" % (w0, w1))
elif data[i] == 0xF5:
self.tile[self.curTile].texFmt = (w0 >> 16) & 0xFF
self.tile[self.curTile].txlSize = (w0 >> 19) & 0x03
self.tile[self.curTile].lineSize = (w0 >> 9) & 0x1F
self.tile[self.curTile].clip.x = (w1 >> 8) & 0x03
self.tile[self.curTile].clip.y = (w1 >> 18) & 0x03
self.tile[self.curTile].mask.x = (w1 >> 4) & 0x0F
self.tile[self.curTile].mask.y = (w1 >> 14) & 0x0F
self.tile[self.curTile].tshift.x = w1 & 0x0F
self.tile[self.curTile].tshift.y = (w1 >> 10) & 0x0F
elif data[i] == 0xFA:
self.primColor = Vector([min(0.003922 * ((w1 >> 24) & 0xFF), 1.0), min(0.003922 * ((w1 >> 16) & 0xFF), 1.0), min(0.003922 * ((w1 >> 8) & 0xFF), 1.0)])
elif data[i] == 0xFB:
self.envColor = Vector([min(0.003922 * ((w1 >> 24) & 0xFF), 1.0), min(0.003922 * ((w1 >> 16) & 0xFF), 1.0), min(0.003922 * ((w1 >> 8) & 0xFF), 1.0)])
if invertEnvColor:
self.envColor = Vector([1.0, 1.0, 1.0]) - self.envColor
elif data[i] == 0xFD:
try:
if data[i - 8] == 0xF2:
self.curTile = 1
else:
self.curTile = 0
except:
pass
try:
if data[i + 8] == 0xE8:
self.tile[0].palette = w1
else:
self.tile[self.curTile].data = w1
except:
pass
has_tex = True
def LinkTpose(self, hierarchy):
segment = []
data = self.segment[0x06]
segment = self.segment
RX, RY, RZ = 0,0,0
BoneCount = hierarchy.limbCount
bpy.context.scene.tool_settings.use_keyframe_insert_auto = True
bonesIndx = [0,-90,0,0,0,0,0,0,0,90,0,0,0,180,0,0,-180,0,0,0,0]
bonesIndy = [0,90,0,0,0,90,0,0,90,-90,-90,-90,0,0,0,90,0,0,90,0,0]
bonesIndz = [0,0,0,0,0,0,0,0,0,0,0,0,0,-90,0,0,90,0,0,0,0]
print("Link T Pose...")
for i in range(BoneCount):
bIndx = ((BoneCount-1) - i)
if (i > -1):
bone = bpy.data.armatures["armature"].bones["limb_%02i" % (bIndx)]
bone.select = True
bpy.ops.transform.rotate(value = radians(bonesIndx[bIndx]), axis=(0, 0, 0), constraint_axis=(True, False, False))
bpy.ops.transform.rotate(value = radians(bonesIndz[bIndx]), axis=(0, 0, 0), constraint_axis=(False, False, True))
bpy.ops.transform.rotate(value = radians(bonesIndy[bIndx]), axis=(0, 0, 0), constraint_axis=(False, True, False))
bpy.ops.pose.select_all(action="DESELECT")
bpy.data.armatures["armature"].bones["limb_00"].select = True ## Translations
bpy.ops.transform.translate(value =(0, 0, 0), constraint_axis=(True, False, False))
bpy.ops.transform.translate(value = (0, 0, 50), constraint_axis=(False, False, True))
bpy.ops.transform.translate(value = (0, 0, 0), constraint_axis=(False, True, False))
bpy.ops.pose.select_all(action="DESELECT")
bpy.context.scene.tool_settings.use_keyframe_insert_auto = False
for i in range(BoneCount):
bIndx = i
if (i > -1):
bone = bpy.data.armatures["armature"].bones["limb_%02i" % (bIndx)]
bone.select = True
bpy.ops.transform.rotate(value = radians(-bonesIndy[bIndx]), axis=(0, 0, 0), constraint_axis=(False, True, False))
bpy.ops.transform.rotate(value = radians(-bonesIndz[bIndx]), axis=(0, 0, 0), constraint_axis=(False, False, True))
bpy.ops.transform.rotate(value = radians(-bonesIndx[bIndx]), axis=(0, 0, 0), constraint_axis=(True, False, False))
bpy.ops.pose.select_all(action="DESELECT")
bpy.data.armatures["armature"].bones["limb_00"].select = True ## Translations
bpy.ops.transform.translate(value =(0, 0, 0), constraint_axis=(True, False, False))
bpy.ops.transform.translate(value = (0, 0, -50), constraint_axis=(False, False, True))
bpy.ops.transform.translate(value = (0, 0, 0), constraint_axis=(False, True, False))
bpy.ops.pose.select_all(action="DESELECT")
def buildLinkAnimations(self, hierarchy, newframe):
global AnimtoPlay
global Animscount
# if (AnimtoPlay == 429 and newframe == 0):
# bpy.context.scene.frame_current = 0
# self.LinkTpose(hierarchy)
# bpy.context.scene.frame_end += 1
# bpy.context.scene.frame_current += 1
# return
segment = []
rot_indx = 0
rot_indy = 0
rot_indz = 0
data = self.segment[0x06]
segment = self.segment
n_anims = self.animTotal
seg, offset = splitOffset(hierarchy.offset)
BoneCount = hierarchy.limbCount
RX, RY, RZ = 0,0,0
frameCurrent = newframe
if (AnimtoPlay > 0 and AnimtoPlay <= n_anims):
currentanim = AnimtoPlay - 1
else:
currentanim = 0
print("currentanim:", currentanim+1, "frameCurrent:", frameCurrent+1)
AnimationOffset = self.offsetAnims[currentanim]
TAnimationOffset = self.offsetAnims[currentanim]
AniSeg = AnimationOffset >> 24
AnimationOffset &= 0xFFFFFF
rot_offset = AnimationOffset
rot_offset += (frameCurrent * (BoneCount * 6 + 8))
frameTotal = self.animFrames[currentanim]
rot_offset += BoneCount * 6
Trot_offset = TAnimationOffset & 0xFFFFFF
Trot_offset += (frameCurrent * (BoneCount * 6 + 8))
TRX = unpack_from(">h", segment[AniSeg], Trot_offset)[0]
Trot_offset += 2
TRZ = unpack_from(">h", segment[AniSeg], Trot_offset)[0]
Trot_offset += 2
TRY = -unpack_from(">h", segment[AniSeg], Trot_offset)[0]
Trot_offset += 2
BoneListListOffset = unpack_from(">L", segment[seg], offset)[0]
BoneListListOffset &= 0xFFFFFF
BoneOffset = unpack_from(">L", segment[seg], BoneListListOffset + (0 << 2))[0]
S_Seg = (BoneOffset >> 24) & 0xFF
BoneOffset &= 0xFFFFFF
TRX += unpack_from(">h", segment[S_Seg], BoneOffset)[0]
TRZ += unpack_from(">h", segment[S_Seg], BoneOffset + 2)[0]
TRY += -unpack_from(">h", segment[S_Seg], BoneOffset + 4)[0]
newLocx = TRX / 79
newLocz = -25.5
newLocz += TRZ / 79
newLocy = TRY / 79
bpy.context.scene.tool_settings.use_keyframe_insert_auto = True
for i in range(BoneCount):
bIndx = ((BoneCount-1) - i) # Had to reverse here, cuz didn't find a way to rotate bones on LOCAL space, start rotating from last to first bone on hierarchy GLOBAL.
RX = unpack_from(">h", segment[AniSeg], rot_offset)[0]
rot_offset -= 2
RY = unpack_from(">h", segment[AniSeg], rot_offset + 4)[0]
rot_offset -= 2
RZ = unpack_from(">h", segment[AniSeg], rot_offset + 8)[0]
rot_offset -= 2
RX /= (182.04444444444444444444)
RY /= (182.04444444444444444444)
RZ /= (182.04444444444444444444)
RXX = (RX)
RYY = (-RZ)
RZZ = (RY)
#print("limb:", bIndx,"RX", int(RXX), "RZ", int(RZZ), "RY", int(RYY), "anim:", currentanim+1, "frame:", frameCurrent+1)
if (i > -1):
bone = bpy.data.armatures["armature"].bones["limb_%02i" % (bIndx)]
bone.select = True
bpy.ops.transform.rotate(value = radians(RXX), axis=(0, 0, 0), constraint_axis=(True, False, False))
bpy.ops.transform.rotate(value = radians(RZZ), axis=(0, 0, 0), constraint_axis=(False, False, True))
bpy.ops.transform.rotate(value = radians(RYY), axis=(0, 0, 0), constraint_axis=(False, True, False))
bpy.ops.pose.select_all(action="DESELECT")
bpy.data.armatures["armature"].bones["limb_00"].select = True ## Translations
bpy.ops.transform.translate(value =(newLocx, 0, 0), constraint_axis=(True, False, False))
bpy.ops.transform.translate(value = (0, 0, newLocz), constraint_axis=(False, False, True))
bpy.ops.transform.translate(value = (0, newLocy, 0), constraint_axis=(False, True, False))
bpy.ops.pose.select_all(action="DESELECT")
if (frameCurrent < (frameTotal - 1)):## Next Frame ### Could have done some math here but... just reverse previus frame, so it just repose.
bpy.context.scene.tool_settings.use_keyframe_insert_auto = False
bpy.data.armatures["armature"].bones["limb_00"].select = True ## Translations
bpy.ops.transform.translate(value = (-newLocx, 0, 0), constraint_axis=(True, False, False))
bpy.ops.transform.translate(value = (0, 0, -newLocz), constraint_axis=(False, False, True))
bpy.ops.transform.translate(value = (0, -newLocy, 0), constraint_axis=(False, True, False))
bpy.ops.pose.select_all(action="DESELECT")
rot_offset = AnimationOffset
rot_offset += (frameCurrent * (BoneCount * 6 + 8))
rot_offset += 6
for i in range(BoneCount):
RX = unpack_from(">h", segment[AniSeg], rot_offset)[0]
rot_offset += 2
RY = unpack_from(">h", segment[AniSeg], rot_offset)[0]
rot_offset += 2
RZ = unpack_from(">h", segment[AniSeg], rot_offset)[0]
rot_offset += 2
RX /= (182.04444444444444444444)
RY /= (182.04444444444444444444)
RZ /= (182.04444444444444444444)
RXX = (-RX)
RYY = (RZ)
RZZ = (-RY)
#print("limb:", i,"RX", int(RXX), "RZ", int(RZZ), "RY", int(RYY), "anim:", currentanim+1, "frame:", frameCurrent+1)
if (i > -1):
bone = bpy.data.armatures["armature"].bones["limb_%02i" % (i)]
bone.select = True
bpy.ops.transform.rotate(value = radians(RYY), axis=(0, 0, 0), constraint_axis=(False, True, False))
bpy.ops.transform.rotate(value = radians(RZZ), axis=(0, 0, 0), constraint_axis=(False, False, True))
bpy.ops.transform.rotate(value = radians(RXX), axis=(0, 0, 0), constraint_axis=(True, False, False))
bpy.ops.pose.select_all(action="DESELECT")
bpy.context.scene.frame_end += 1
bpy.context.scene.frame_current += 1
frameCurrent += 1
self.buildLinkAnimations(hierarchy, frameCurrent)
else:
bpy.context.scene.tool_settings.use_keyframe_insert_auto = False
# bpy.context.scene.frame_end += 1
# bpy.context.scene.frame_current += 1
# rot_offset = AnimationOffset
# rot_offset += (frameCurrent * (BoneCount * 6 + 8))
# rot_offset += 6
# for i in range(BoneCount):
# RX = unpack_from(">h", segment[AniSeg], rot_offset)[0]
# rot_offset += 2
# RY = unpack_from(">h", segment[AniSeg], rot_offset)[0]
# rot_offset += 2
# RZ = unpack_from(">h", segment[AniSeg], rot_offset)[0]
# rot_offset += 2
# RX /= (182.04444444444444444444)
# RY /= (182.04444444444444444444)
# RZ /= (182.04444444444444444444)
# RXX = (-RX)
# RYY = (RZ)
# RZZ = (-RY)
# if (i > -1):
# bone = bpy.data.armatures["armature"].bones["limb_%02i" % (i)]
# bone.select = True
# bpy.ops.transform.rotate(value = radians(RYY), axis=(0, 0, 0), constraint_axis=(False, True, False))
# bpy.ops.transform.rotate(value = radians(RZZ), axis=(0, 0, 0), constraint_axis=(False, False, True))
# bpy.ops.transform.rotate(value = radians(RXX), axis=(0, 0, 0), constraint_axis=(True, False, False))
# bpy.ops.pose.select_all(action="DESELECT")
# bpy.data.armatures["armature"].bones["limb_00"].select = True ## Translations
# bpy.ops.transform.translate(value = (-newLocx, 0, 0), constraint_axis=(True, False, False))
# bpy.ops.transform.translate(value = (0, 0, -newLocz), constraint_axis=(False, False, True))
# bpy.ops.transform.translate(value = (0, -newLocy, 0), constraint_axis=(False, True, False))
# bpy.ops.pose.select_all(action="DESELECT")
# self.LinkTpose(hierarchy)
# bpy.context.scene.frame_end += 1
# bpy.context.scene.frame_current += 1
# if (AnimtoPlay == 429):
# Animscount += 1
# if(Animscount > 7):
# AnimtoPlay = 428
# self.buildLinkAnimations(hierarchy, 0)
# if (AnimtoPlay == 429):
# Animscount += 1
# AnimtoPlay = 430
# self.buildLinkAnimations(hierarchy, 0)
# else:
bpy.context.scene.frame_current = 1
def buildAnimations(self, hierarchy, newframe):
segment = []
rot_indx = 0
rot_indy = 0
rot_indz = 0
Trot_indx = 0
Trot_indy = 0
Trot_indz = 0
data = self.segment[0x06]
segment = self.segment
RX, RY, RZ = 0,0,0
rot_valsx = [0x2000]
n_anims = self.animTotal
if (AnimtoPlay > 0 and AnimtoPlay <= n_anims):
currentanim = AnimtoPlay - 1
else:
currentanim = 0
AnimationOffset = self.offsetAnims[currentanim]
seg, offset = splitOffset(hierarchy.offset)
BoneCount = hierarchy.limbCount
frameCurrent = newframe
if not validOffset(segment, AnimationOffset):
return
AniSeg = AnimationOffset >> 24
AnimationOffset &= 0xFFFFFF
frameTotal = unpack_from(">h", segment[AniSeg], (AnimationOffset))[0]
rot_vals_addr = unpack_from(">L", segment[AniSeg], (AnimationOffset + 4))[0]
RotIndexoffset = unpack_from(">L", segment[AniSeg], (AnimationOffset + 8))[0]
Limit = unpack_from(">h", segment[AniSeg], (AnimationOffset + 12))[0]
rot_vals_n = int ((RotIndexoffset - rot_vals_addr) / 2)
rot_vals_addr &= 0xFFFFFF
RotIndexoffset &= 0xFFFFFF
bpy.context.scene.tool_settings.use_keyframe_insert_auto = True
bpy.context.scene.frame_end = frameTotal
bpy.context.scene.frame_current = frameCurrent + 1
# Dragorn421 here
print("anim: %d frame: %d/%d" % (currentanim+1, frameCurrent+1, frameTotal))
#print("currentanim:", currentanim+1, "frameCurrent:", frameCurrent+1)
#
for j in range(rot_vals_n):
rot_valsx.extend([j])
rot_valsx[j] = unpack_from(">h", segment[AniSeg], (rot_vals_addr) + (j * 2))[0]
## Translations
Trot_indexx = unpack_from(">h", segment[AniSeg], RotIndexoffset)[0]
Trot_indexy = unpack_from(">h", segment[AniSeg], RotIndexoffset + 2)[0]
Trot_indexz = unpack_from(">h", segment[AniSeg], RotIndexoffset + 4)[0]
Trot_indx = Trot_indexx
Trot_indz = Trot_indexz
Trot_indy = Trot_indexy
if (Trot_indx >= Limit):
Trot_indx += frameCurrent
if (Trot_indz >= Limit):
Trot_indz += frameCurrent
if (Trot_indy >= Limit):
Trot_indy += frameCurrent
TRX = rot_valsx[Trot_indx]
TRZ = rot_valsx[Trot_indy]
TRY = rot_valsx[Trot_indz]
# Dragorn421 here
global scaleFactor
newLocx = TRX * scaleFactor
newLocz = TRZ * scaleFactor
newLocy = -TRY * scaleFactor
"""
newLocx = TRX / 79
newLocz = 10
newLocz += TRZ / 79
newLocy = -TRY / 79
"""
#
#print("X",int(TRX),"Y",int(TRZ),"Z",int(TRY))
#print(" ",frameTotal, "Frames", Limit, "still values", ((rot_vals_n - Limit) / frameTotal), "tracks\n" )
for i in range(BoneCount):
bIndx = ((BoneCount-1) - i) # Had to reverse here, cuz didn't find a way to rotate bones on LOCAL space, start rotating from last to first bone on hierarchy GLOBAL.
rot_indexx = unpack_from(">h", segment[AniSeg], RotIndexoffset + (bIndx * 6) + 6)[0]
rot_indexy = unpack_from(">h", segment[AniSeg], RotIndexoffset + (bIndx * 6) + 8)[0]
rot_indexz = unpack_from(">h", segment[AniSeg], RotIndexoffset + (bIndx * 6) + 10)[0]
rot_indx = rot_indexx
rot_indy = rot_indexy
rot_indz = rot_indexz
if (rot_indx >= Limit):
rot_indx += frameCurrent
if (rot_indy >= Limit):
rot_indy += frameCurrent
if (rot_indz >= Limit):
rot_indz += frameCurrent
RX = rot_valsx[rot_indx] / 182.04444444444444444444
RY = -rot_valsx[rot_indz] / 182.04444444444444444444
RZ = rot_valsx[rot_indy] / 182.04444444444444444444
RXX = radians(RX)
RYY = radians(RY)
RZZ = radians(RZ)
#print("limb:", bIndx, "XIdx:", rot_indexx, "YIdx:", rot_indexy , "ZIdx:", rot_indexz, "frameTotal:", frameTotal)
#print("limb:", bIndx,"RX", int(RX), "RZ", int(RZ), "RY", int(RY), "anim:", currentanim+1, "frame:", frameCurrent+1, "frameTotal:", frameTotal)
if (bIndx > -1):
bone = bpy.data.armatures["armature"].bones["limb_%02i" % (bIndx)]
bone.select = True
bpy.ops.transform.rotate(value = RXX, axis=(0, 0, 0), constraint_axis=(True, False, False))
bpy.ops.transform.rotate(value = RZZ, axis=(0, 0, 0), constraint_axis=(False, False, True))
bpy.ops.transform.rotate(value = RYY, axis=(0, 0, 0), constraint_axis=(False, True, False))
bpy.ops.pose.select_all(action="DESELECT")
# Dragorn421 here
# fix "convertViewVec: called in an invalid context"
# TODO: new code works but causes the usual z<->-y axis switch issue between blender/oot if using (x,y,z) and not (x,z,-y)
# somehow bpy.ops.transform.translate calls with (x,y,z) lead to correct behavior, is there some transformation set somewhere?
"""
bpy.data.armatures["armature"].bones["limb_00"].select = True ## Translations
bpy.ops.transform.translate(value =(newLocx, 0, 0), constraint_axis=(True, False, False))
bpy.ops.transform.translate(value = (0, 0, newLocz), constraint_axis=(False, False, True))
bpy.ops.transform.translate(value = (0, newLocy, 0), constraint_axis=(False, True, False))
bpy.ops.pose.select_all(action="DESELECT")
"""
bone = hierarchy.armature.pose.bones["limb_00"]
bone.location += mathutils.Vector((newLocx,newLocz,-newLocy))
bone.keyframe_insert(data_path='location')
#
# Dragorn421 here
# also "repose" after last frame, for next animation: move "repose" code before if/else
"""
if (frameCurrent < (frameTotal - 1)):## Next Frame ### Could have done some math here but... just reverse previus frame, so it just repose.
bpy.context.scene.tool_settings.use_keyframe_insert_auto = False
bpy.data.armatures["armature"].bones["limb_00"].select = True ## Translations
bpy.ops.transform.translate(value = (-newLocx, 0, 0), constraint_axis=(True, False, False))
bpy.ops.transform.translate(value = (0, 0, -newLocz), constraint_axis=(False, False, True))
bpy.ops.transform.translate(value = (0, -newLocy, 0), constraint_axis=(False, True, False))
bpy.ops.pose.select_all(action="DESELECT")
for i in range(BoneCount):
bIndx = i
rot_indexx = unpack_from(">h", segment[AniSeg], RotIndexoffset + (bIndx * 6) + 6)[0]
rot_indexy = unpack_from(">h", segment[AniSeg], RotIndexoffset + (bIndx * 6) + 8)[0]
rot_indexz = unpack_from(">h", segment[AniSeg], RotIndexoffset + (bIndx * 6) + 10)[0]
rot_indx = rot_indexx
rot_indy = rot_indexy
rot_indz = rot_indexz
if (rot_indx > Limit):
rot_indx += frameCurrent
if (rot_indy > Limit):
rot_indy += frameCurrent
if (rot_indz > Limit):
rot_indz += frameCurrent
RX = -rot_valsx[rot_indx] / 182.04444444444444444444
RY = rot_valsx[rot_indz] / 182.04444444444444444444
RZ = -rot_valsx[rot_indy] / 182.04444444444444444444
RXX = radians(RX)
RYY = radians(RY)
RZZ = radians(RZ)
#print("limb:", i, "XIdx:", rot_indexx, "YIdx:", rot_indexy , "ZIdx:", rot_indexz, "frameTotal:", frameTotal)
#print("limb:", bIndx,"RX", int(RX), "RZ", int(RZ), "RY", int(RY), "anim:", currentanim+1, "frame:", frameCurrent+1, "frameTotal:", frameTotal)
if (bIndx > -1):
bone = bpy.data.armatures["armature"].bones["limb_%02i" % (bIndx)]
bone.select = True
bpy.ops.transform.rotate(value = RYY, axis=(0, 0, 0), constraint_axis=(False, True, False))
bpy.ops.transform.rotate(value = RZZ, axis=(0, 0, 0), constraint_axis=(False, False, True))
bpy.ops.transform.rotate(value = RXX, axis=(0, 0, 0), constraint_axis=(True, False, False))
bpy.ops.pose.select_all(action="DESELECT")
frameCurrent += 1
self.buildAnimations(hierarchy, frameCurrent)
else:
bpy.context.scene.tool_settings.use_keyframe_insert_auto = False
bpy.context.scene.frame_current = 1
"""
### Could have done some math here but... just reverse previus frame, so it just repose.
bpy.context.scene.tool_settings.use_keyframe_insert_auto = False
## Dragorn421 here
"""
bpy.data.armatures["armature"].bones["limb_00"].select = True ## Translations
bpy.ops.transform.translate(value = (-newLocx, 0, 0), constraint_axis=(True, False, False))
bpy.ops.transform.translate(value = (0, 0, -newLocz), constraint_axis=(False, False, True))
bpy.ops.transform.translate(value = (0, -newLocy, 0), constraint_axis=(False, True, False))
bpy.ops.pose.select_all(action="DESELECT")
"""
bone = hierarchy.armature.pose.bones["limb_00"]
bone.location -= mathutils.Vector((newLocx,newLocz,-newLocy))
##
for i in range(BoneCount):
bIndx = i
rot_indexx = unpack_from(">h", segment[AniSeg], RotIndexoffset + (bIndx * 6) + 6)[0]
rot_indexy = unpack_from(">h", segment[AniSeg], RotIndexoffset + (bIndx * 6) + 8)[0]
rot_indexz = unpack_from(">h", segment[AniSeg], RotIndexoffset + (bIndx * 6) + 10)[0]
rot_indx = rot_indexx
rot_indy = rot_indexy
rot_indz = rot_indexz
if (rot_indx > Limit):
rot_indx += frameCurrent
if (rot_indy > Limit):
rot_indy += frameCurrent
if (rot_indz > Limit):
rot_indz += frameCurrent
RX = -rot_valsx[rot_indx] / 182.04444444444444444444
RY = rot_valsx[rot_indz] / 182.04444444444444444444
RZ = -rot_valsx[rot_indy] / 182.04444444444444444444
RXX = radians(RX)
RYY = radians(RY)
RZZ = radians(RZ)
#print("limb:", i, "XIdx:", rot_indexx, "YIdx:", rot_indexy , "ZIdx:", rot_indexz, "frameTotal:", frameTotal)
#print("limb:", bIndx,"RX", int(RX), "RZ", int(RZ), "RY", int(RY), "anim:", currentanim+1, "frame:", frameCurrent+1, "frameTotal:", frameTotal)
if (bIndx > -1):
bone = bpy.data.armatures["armature"].bones["limb_%02i" % (bIndx)]
bone.select = True
bpy.ops.transform.rotate(value = RYY, axis=(0, 0, 0), constraint_axis=(False, True, False))
bpy.ops.transform.rotate(value = RZZ, axis=(0, 0, 0), constraint_axis=(False, False, True))
bpy.ops.transform.rotate(value = RXX, axis=(0, 0, 0), constraint_axis=(True, False, False))
bpy.ops.pose.select_all(action="DESELECT")
if (frameCurrent < (frameTotal - 1)):## Next Frame
frameCurrent += 1
self.buildAnimations(hierarchy, frameCurrent)
else:
bpy.context.scene.frame_current = 1
#
global Animscount
Animscount = 1
class ImportZ64(bpy.types.Operator, ImportHelper):
"""Load a Zelda64 File"""
bl_idname = "file.zobjallanimations"
bl_label = "Import Zelda64 - all animations"
bl_options = {'PRESET', 'UNDO'}
filename_ext = ".zobj"
filter_glob = StringProperty(default="*.zobj;*.zmap", options={'HIDDEN'})
loadOtherSegments = BoolProperty(name="Load Data From Other Segments",
description="Load data from other segments",
default=True,)
vertexMode = EnumProperty(name="Vtx Mode",
items=(('COLORS', "COLORS", "Use vertex colors"),
('NORMALS', "NORMALS", "Use vertex normals as shading"),
('NONE', "NONE", "Don't use vertex colors or normals"),),
default='NORMALS',)
enableMatrices = BoolProperty(name="Matrices",
description="Enable texture mirroring",
default=True,)
enablePrimColor = BoolProperty(name="Prim Color",
description="Enable blending with primitive color",
default=True,)
enableEnvColor = BoolProperty(name="Env Color",
description="Enable blending with environment color",
default=True,)
invertEnvColor = BoolProperty(name="Invert Env Color",
description="Invert environment color (temporary fix)",
default=False,)
exportTextures = BoolProperty(name="Export Textures",
description="Export textures for the model",
default=True,)
importTextures = BoolProperty(name="Import Textures",
description="Import textures for the model",
default=True,)
enableTexClamp = BoolProperty(name="Texture Clamp",
description="Enable texture clamping, will not place #Clamp tag if enabled",
default=True,)# Dragorn421 here: False -> True
enableTexMirror = BoolProperty(name="Texture Mirror",
description="Enable texture mirroring, will not place #Mirror tag if enabled",
default=True,)# Dragorn421 here: False -> True
# Dragorn421 here
enableShadelessMaterials = BoolProperty(name="Shadeless Materials",
description="Set materials to be shadeless, prevents using environment colors in-game",
default=False,)
#
enableToon = BoolProperty(name="Toony UVs",
description="Obtain a toony effect by not scaling down the uv coords",
default=False,)
# Dragorn421 here
originalObjectScale = IntProperty(name="File Scale",
description="Scale of imported object, blender model will be scaled 1/(file scale) (use 48 for maps?)",
default=100,)
"""
AnimtoPlay = IntProperty(name="Anim",
description="Choose an anim to Play, if < 1 don't load anims.",
default=1,)
"""
#
MajorasAnims = BoolProperty(name="MajorasAnims",
description="Majora's Mask Link's Anims.",
default=False,)
ExternalAnimes = BoolProperty(name="ExternalAnimes",
description="Load External Animes.",
default=False,)
def execute(self, context):
global fpath
fpath, fext = os.path.splitext(self.filepath)
fpath, fname = os.path.split(fpath)
global vertexMode, enableMatrices
global enablePrimColor, enableEnvColor, invertEnvColor
global importTextures, exportTextures
global enableTexClamp, enableTexMirror
global enableMatrices, enableToon
global AnimtoPlay, MajorasAnims, ExternalAnimes
vertexMode = self.vertexMode
enableMatrices = self.enableMatrices
enablePrimColor = self.enablePrimColor
enableEnvColor = self.enableEnvColor
invertEnvColor = self.invertEnvColor
importTextures = self.importTextures
exportTextures = self.exportTextures
enableTexClamp = self.enableTexClamp
enableTexMirror = self.enableTexMirror
enableToon = self.enableToon
# Dragorn421 here
AnimtoPlay = 1
#AnimtoPlay = self.AnimtoPlay
#
MajorasAnims = self.MajorasAnims
ExternalAnimes = self.ExternalAnimes
# Dragorn421 here
global enableShadelessMaterials
enableShadelessMaterials = self.enableShadelessMaterials
global scaleFactor
scaleFactor = 1 / self.originalObjectScale
#
print("Importing '%s'..." % fname)
time_start = time.time()
f3dzex = F3DZEX()
f3dzex.loaddisplaylists(os.path.join(fpath, "displaylists.txt"))
if self.loadOtherSegments:
for i in range(16):
scenefile = fpath + "/" + fname[0:find_str(fname,"_room")] + "_scene.zscene"
if (i == 2 and os.path.isfile(scenefile)):
f3dzex.setSegment(i, scenefile)
else:
f3dzex.setSegment(i, fpath + "/segment_%02X.zdata" % i)
if fext.lower() == '.zmap':
f3dzex.setSegment(0x03, self.filepath)
f3dzex.importMap()
else:
f3dzex.setSegment(0x06, self.filepath)
f3dzex.importObj()
#Noka here
if fext.lower() == '.zmap':
for ob in context.scene.objects:
if ob.type == 'MESH': # or ob.type == 'ARMATURE'
ob.scale = (48.0, 48.0, 48.0)
ob.select = True
context.scene.objects.active = ob
bpy.ops.object.transform_apply(location=False, rotation=False, scale=True)
ob.select = False
for screen in bpy.data.screens:
for area in screen.areas:
if area.type == 'VIEW_3D':
area.spaces.active.grid_lines = 500
area.spaces.active.grid_scale = 10
area.spaces.active.grid_subdivisions = 10
area.spaces.active.clip_end = 900000
#
print("SUCCESS: Elapsed time %.4f sec" % (time.time() - time_start))
bpy.context.scene.update()
return {'FINISHED'}
def draw(self, context):
layout = self.layout
row = layout.row(align=True)
row.prop(self, "vertexMode")
row = layout.row(align=True)
row.prop(self, "loadOtherSegments")
box = layout.box()
row = box.row(align=True)
row.prop(self, "enableMatrices")
row.prop(self, "enablePrimColor")
row = box.row(align=True)
row.prop(self, "enableEnvColor")
row.prop(self, "invertEnvColor")
row = box.row(align=True)
row.prop(self, "exportTextures")
row.prop(self, "importTextures")
row = box.row(align=True)
row.prop(self, "enableTexClamp")
row.prop(self, "enableTexMirror")
row = box.row(align=True)
# Dragorn421 here
row.prop(self, "enableShadelessMaterials")
#
row.prop(self, "enableToon")
row = box.row(align=True)
# Dragorn421 here
row.prop(self, "originalObjectScale")
#row.prop(self, "AnimtoPlay")
#
row = box.row(align=True)
row.prop(self, "MajorasAnims")
row.prop(self, "ExternalAnimes")
def menu_func_import(self, context):
self.layout.operator(ImportZ64.bl_idname, text="Zelda64 - all animations (.zobj;.zmap)")
def register():
bpy.utils.register_module(__name__)
bpy.types.INFO_MT_file_import.append(menu_func_import)
def unregister():
bpy.utils.unregister_module(__name__)
bpy.types.INFO_MT_file_import.remove(menu_func_import)
def find_str(s, char):
index = 0
if char in s:
c = char[0]
for ch in s:
if ch == c:
if s[index:index+len(char)] == char:
return index
index += 1
return -1
if __name__ == "__main__":
register()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment