Index: Read.py =================================================================== --- Read.py (revision 2128) +++ Read.py (working copy) @@ -276,10 +276,10 @@ _READ_PROGRESS = 0.0 _BLOCKS_READ = 0.0 # preprocessing: - + # scale tree - scale_tree(root_block, _CONFIG['IMPORT_SCALE_CORRECTION']) - + root_block.applyScale(_CONFIG['IMPORT_SCALE_CORRECTION']) + # sets the root block parent to None, so that when crawling back the script won't barf root_block._parent = None @@ -290,7 +290,8 @@ mark_armatures_bones(root_block) # and merge armatures that are bones of others armatures - merge_armatures() + # *** DISABLED *** only single skeleton root supported + #merge_armatures() if _VERBOSE and _MSG_LEVEL >= 4: for arm in _ARMATURES.keys(): @@ -390,9 +391,9 @@ if b_mesh: # correct the transform # it's parented to the armature! - armature_matrix_inverse = fb_global_bind_matrix(niArmature) + armature_matrix_inverse = fb_global_matrix(niArmature) armature_matrix_inverse.invert() - b_mesh.setMatrix(fb_global_bind_matrix(child) * armature_matrix_inverse) + b_mesh.setMatrix(fb_global_matrix(child) * armature_matrix_inverse) # add a vertex group if it's parented to a bone par_bone = get_closest_bone(child) if par_bone: @@ -452,14 +453,7 @@ # Retrieves a niBlock's transform matrix as a Mathutil.Matrix def fb_matrix(niBlock): """Retrieves a niBlock's transform matrix as a Mathutil.Matrix""" - r = niBlock.rotation # 3*3 matrix - t = niBlock.translation # translation, vector3 - v = niBlock.velocity # ??? velocity, vector3, pretty much always 0, 0, 0 - b_matrix = Matrix( [r.m11, r.m12, r.m13, v.x],\ - [r.m21, r.m22, r.m23, v.y],\ - [r.m31, r.m32, r.m33, v.z],\ - [ t.x, t.y, t.z, 1.0]) - return b_matrix + return Matrix(*niBlock.getTransform().asList()) # Retrieves a block's global transform matrix def fb_global_matrix(niBlock): @@ -470,20 +464,23 @@ return b_matrix # Retrieves a node's bind position matrix -def fb_bind_matrix(niBlock): - """Retrieves a node's bind position matrix""" - # I need this hack to trap a few errors - try: - return niBlock._bindMatrix - except AttributeError: - return fb_matrix(niBlock) +# *** DISABLED *** doesn't do what it should do... +##def fb_bind_matrix(niBlock): +## """Retrieves a node's bind position matrix relative to the armature.""" +## # I need this hack to trap a few errors +## try: +## return niBlock._bindMatrix +## except AttributeError: +## return fb_matrix(niBlock) + # Retrieves a block's global bind position matrix -def fb_global_bind_matrix(niBlock): - """Retrieves a node's global bind position matrix""" - if niBlock._parent: - return fb_bind_matrix(niBlock) * fb_global_bind_matrix(niBlock._parent) # yay, recursion - return fb_bind_matrix(niBlock) +# *** DISABLED *** doesn't do what it should do... +##def fb_global_bind_matrix(niBlock): +## """Retrieves a node's global bind position matrix""" +## if niBlock._parent: +## return fb_bind_matrix(niBlock) * fb_global_bind_matrix(niBlock._parent) # yay, recursion +## return fb_bind_matrix(niBlock) # Decompose Blender transform matrix as a scale, rotation matrix, and translation vector def decompose_srt(m): @@ -524,7 +521,7 @@ def fb_armature(niBlock): global _SCENE armature_name = fb_name(niBlock,22) - armature_matrix_inverse = fb_global_bind_matrix(niBlock) + armature_matrix_inverse = fb_global_matrix(niBlock) armature_matrix_inverse.invert() b_armature = Blender.Object.New('Armature', armature_name) b_armatureData = Blender.Armature.Armature() @@ -551,6 +548,9 @@ # The armature has been created in editmode, # now we are ready to set the bone keyframes. + # *** DISABLED *** until everything else works :-) + return b_armature + # create an action action = Blender.Armature.NLA.NewAction() action.setActive(b_armature) @@ -720,6 +720,7 @@ # Adds a bone to the armature in edit mode. def fb_bone(niBlock, b_armature, b_armatureData, armature_matrix_inverse): + # check if it is used at all global _BONES, _BONES_EXTRA_MATRIX, _BONE_CORRECTION_MATRICES, _CONFIG bone_name = fb_name(niBlock, 32) niChildren = niBlock.children @@ -729,20 +730,22 @@ # create bones here... b_bone = Blender.Armature.Editbone() # head: get position from niBlock - armature_space_matrix = fb_global_bind_matrix(niBlock) * armature_matrix_inverse + #armature_space_matrix = fb_global_bind_matrix(niBlock) * armature_matrix_inverse + armature_space_matrix = getattr(niBlock, '_bindMatrix', fb_global_matrix(niBlock) * armature_matrix_inverse) b_bone_head_x = armature_space_matrix[3][0] b_bone_head_y = armature_space_matrix[3][1] b_bone_head_z = armature_space_matrix[3][2] # tail: average of children location if len(niChildNodes) > 0: - child_matrices = [(fb_global_bind_matrix(child) * armature_matrix_inverse) for child in niChildNodes] + child_matrices = [(fb_global_matrix(child) * armature_matrix_inverse) for child in niChildNodes] b_bone_tail_x = sum([child_matrix[3][0] for child_matrix in child_matrices]) / len(child_matrices) b_bone_tail_y = sum([child_matrix[3][1] for child_matrix in child_matrices]) / len(child_matrices) b_bone_tail_z = sum([child_matrix[3][2] for child_matrix in child_matrices]) / len(child_matrices) else: # no children... continue bone sequence in the same direction as parent, with the same length # this seems to work fine - parent_matrix = fb_global_bind_matrix(niBlock._parent) * armature_matrix_inverse + #parent_matrix = fb_global_bind_matrix(niBlock._parent) * armature_matrix_inverse + parent_matrix = niBlock._parent._bindMatrix b_parent_head_x = parent_matrix[3][0] b_parent_head_y = parent_matrix[3][1] b_parent_head_z = parent_matrix[3][2] @@ -765,30 +768,33 @@ # sets the bone heads & tails b_bone.head = Vector(b_bone_head_x, b_bone_head_y, b_bone_head_z) b_bone.tail = Vector(b_bone_tail_x, b_bone_tail_y, b_bone_tail_z) - # here we explicitly try to set the matrix from the NIF matrix; this has the following consequences: - # - head is preserved - # - bone length is preserved - # - tail is lost (up to bone length) - (sum_x, sum_y, sum_z, dummy) = fb_matrix(niBlock)[3] - if len(niChildNodes) > 0: - child_local_matrices = [fb_matrix(child) for child in niChildNodes] - sum_x = sum([cm[3][0] for cm in child_local_matrices]) - sum_y = sum([cm[3][1] for cm in child_local_matrices]) - sum_z = sum([cm[3][2] for cm in child_local_matrices]) - listXYZ = [int(c*200) for c in (sum_x, sum_y, sum_z, -sum_x, -sum_y, -sum_z)] - idx_correction = listXYZ.index(max(listXYZ)) - alignment_offset = 0.0 - if idx_correction == 0 or idx_correction == 3: - alignment_offset = float(abs(sum_y) + abs(sum_z)) / abs(sum_x) - elif idx_correction == 1 or idx_correction == 4: - alignment_offset = float(abs(sum_z) + abs(sum_x)) / abs(sum_y) - else: - alignment_offset = float(abs(sum_x) + abs(sum_y)) / abs(sum_z) - # if alignment is good enough, use the (corrected) NIF matrix - if alignment_offset < 0.25: - m_correction = _BONE_CORRECTION_MATRICES[idx_correction] - b_bone.matrix = m_correction * armature_space_matrix.rotationPart() - # set bone name and store the niBlock for future reference + b_bone.matrix = armature_space_matrix.rotationPart() + +# *** DISABLED *** no bone correction until binding works +## # here we explicitly try to set the matrix from the NIF matrix; this has the following consequences: +## # - head is preserved +## # - bone length is preserved +## # - tail is lost (up to bone length) +## (sum_x, sum_y, sum_z, dummy) = fb_matrix(niBlock)[3] +## if len(niChildNodes) > 0: +## child_local_matrices = [fb_matrix(child) for child in niChildNodes] +## sum_x = sum([cm[3][0] for cm in child_local_matrices]) +## sum_y = sum([cm[3][1] for cm in child_local_matrices]) +## sum_z = sum([cm[3][2] for cm in child_local_matrices]) +## listXYZ = [int(c*200) for c in (sum_x, sum_y, sum_z, -sum_x, -sum_y, -sum_z)] +## idx_correction = listXYZ.index(max(listXYZ)) +## alignment_offset = 0.0 +## if idx_correction == 0 or idx_correction == 3: +## alignment_offset = float(abs(sum_y) + abs(sum_z)) / abs(sum_x) +## elif idx_correction == 1 or idx_correction == 4: +## alignment_offset = float(abs(sum_z) + abs(sum_x)) / abs(sum_y) +## else: +## alignment_offset = float(abs(sum_x) + abs(sum_y)) / abs(sum_z) +## # if alignment is good enough, use the (corrected) NIF matrix +## if alignment_offset < 0.25: +## m_correction = _BONE_CORRECTION_MATRICES[idx_correction] +## b_bone.matrix = m_correction * armature_space_matrix.rotationPart() +## # set bone name and store the niBlock for future reference b_armatureData.bones[bone_name] = b_bone _BONES[bone_name] = niBlock # calculate bone difference matrix; we will need this when importing animation @@ -1054,7 +1060,7 @@ b_mesh.setDrawType(4) # not hidden: shaded # Mesh transform matrix, sets the transform matrix for the object. - b_mesh.setMatrix(fb_bind_matrix(niBlock)) + b_mesh.setMatrix(fb_matrix(niBlock)) # Mesh geometry data. From this I can retrieve all geometry info niData = niBlock.data @@ -1474,71 +1480,54 @@ # search for all NiTriShape or NiTriStrips blocks... if isinstance(niBlock, NifFormat.NiTriBasedGeom): # yes, we found one, get its skin instance - skininst = niBlock.skinInstance - if skininst: - msg("Skin instance found on block '%s'" % niBlock.name,3) - - - # retrieves scaling corrections. I have to do it here because skin instances - # are not affected by the scale_tree function. Might be fixed later - scale = _CONFIG["IMPORT_SCALE_CORRECTION"] - + if niBlock.isSkin(): + msg("skin found on block '%s'" % niBlock.name,3) + # it has a skin instance, so get the skeleton root # which is an armature only if it's not a skinning influence # so mark the node to be imported as an armature - skelroot = skininst.skeletonRoot - - # stores the bind offset matrices for later use - skinInstData = skininst.data - r = skinInstData.rotation - s = skinInstData.scale - t = skinInstData.translation - - - bindMatrix = Matrix( - [r.m11, r.m12, r.m13, 0.0],\ - [r.m21, r.m22, r.m23, 0.0],\ - [r.m31, r.m32, r.m33, 0.0],\ - [t.x*scale, t.y*scale, t.z*scale, 1.0]) - - # only non-identity matrices are stored in the bind offset list - # sets the bind offset for the affected skin - if (bindMatrix != _IDENTITY44): - niBlock._bindMatrix = bindMatrix - + skelroot = niBlock.skinInstance.skeletonRoot if not _ARMATURES.has_key(skelroot): + if _ARMATURES.keys(): + raise NIFImportError('models with multiple skeleton roots not yet supported') _ARMATURES[skelroot] = [] msg("'%s' is an armature" % skelroot.name,3) + + # stores the mesh bind position for later use + # note that this matrix is relative to the skeleton root + bindMatrix = Matrix(*niBlock.getGeometryRestPosition().asList()) + niBlock._bindMatrix = bindMatrix - for i, bone in enumerate(skininst.bones): + for boneBlock, boneRestPos in niBlock.getBoneRestPositions().items(): # add them, if we haven't already - if not bone in _ARMATURES[skelroot]: - skinData = skinInstData.boneList[i] - r = skinData.rotation - s = skinData.scale - t = skinData.translation - bindMatrix = Matrix( - [r.m11, r.m12, r.m13, 0.0],\ - [r.m21, r.m22, r.m23, 0.0],\ - [r.m31, r.m32, r.m33, 0.0],\ - [t.x*s*scale, t.y*s*scale, t.z*s*scale, 1.0]) - # only non-identity matrices are stored in the bind offset list - # sets the bind offset for the affected skin - if (bindMatrix != _IDENTITY44): - bone._bindMatrix = bindMatrix - _ARMATURES[skelroot].append(bone) - msg("'%s' is a bone of armature '%s'" % (bone.name, skelroot.name), 3) + if not boneBlock in _ARMATURES[skelroot]: + # sets the rest position for the affected skin + boneBlock._bindMatrix = Matrix(*boneRestPos.asList()) + _ARMATURES[skelroot].append(boneBlock) + msg("'%s' is a bone of armature '%s'" % (boneBlock.name, skelroot.name), 3) + else: + # ensure that rest position is unique + # ... hmmm Blender docs say matrix comparison works + # but apparently it doesn't so commented out for now ... + #if Matrix(*boneRestPos.asList()) != boneBlock._bindMatrix: + tmp = 0.0 + for v in Matrix(*boneRestPos.asList())-boneBlock._bindMatrix: + tmp += v.length + if tmp > _EPSILON: + print 'warning for bone', boneBlock.name + print Matrix(*boneRestPos.asList()) + print boneBlock._bindMatrix + print 'multiple geometries influenced by the same bone with different rest poses' # now we "attach" the bone to the armature: # we make sure all NiNodes from this bone all the way # down to the armature NiNode are marked as bones - complete_bone_tree(bone, skelroot) + # *** DISABLED *** + #complete_bone_tree(bone, skelroot) # nope, it's not a NiTriShape or NiTriStrips - # so if it's a NiNode - elif isinstance(niBlock, NifFormat.NiNode): - children = niBlock.children - if children: # empty tuple is false - for child in children: - mark_armatures_bones(child) + # so continue down the tree + else: + for child in niBlock.getRefs(): + mark_armatures_bones(child) @@ -1546,6 +1535,9 @@ # all the way down to the armature node # just call it on all bones of a skin instance def complete_bone_tree(bone, skelroot): + # *** DISABLED *** + return + global _ARMATURES # we must already have marked this one as a bone assert _ARMATURES.has_key(skelroot) # debug @@ -1568,6 +1560,9 @@ # merge armatures that are bones of other armatures def merge_armatures(): + # *** DISABLED *** + return + global _ARMATURES for arm, bones in _ARMATURES.items(): for arm2, bones2 in _ARMATURES.items(): @@ -1633,6 +1628,9 @@ # Loads basic animation info for this object def set_animation(niBlock, b_obj): + # *** DISABLED *** + return + progress = 0.1 kfc = find_controller(niBlock, NifFormat.NiKeyframeController) if kfc: @@ -1702,60 +1700,3 @@ b_obj.insertIpoKey(Blender.Object.LOC) Blender.Set('curframe', 1) - - -# Scale NIF file. -def scale_tree(niBlock, scale): - # scale only works for nodes, and there's no point in scaling if the scale is 1/1 - if abs(1.0 - scale) > _EPSILON: - # is it a node? - # data should be scaled here as well, but isn't necessarily linked in the tree, so I scale it as I find it - if isinstance(niBlock, NifFormat.NiAVObject): - niBlock.translation.x *= scale - niBlock.translation.y *= scale - niBlock.translation.z *= scale - # Controller data block scale - ctrl = niBlock.controller - while ctrl: - if isinstance(ctrl, NifFormat.NiKeyframeController): - kfd = ctrl.data - assert(kfd) - # just to make sure, NiNode/NiTriShape controllers should have keyframe data - assert(isinstance(kfd, NifFormat.NiKeyframeData)) - translations = kfd.translations - if translations: - for key in translations.keys: - key.value.x *= scale - key.value.y *= scale - key.value.z *= scale - elif isinstance(ctrl, NifFormat.NiGeomMorpherController): - gmd = ctrl.data - assert(gmd) - assert(isinstance(gmd, NifFormat.NiMorphData)) - for morph in gmd.morphs: - vects = morph.vectors - for v in range( len( vects ) ): - vects[v].x *= scale - vects[v].y *= scale - vects[v].z *= scale - ctrl = ctrl.nextController - # Child block scale - if isinstance(niBlock, NifFormat.NiNode): - children = niBlock.children - if children: # block has children - for child in children: - scale_tree(child, scale) - else: - blockData = niBlock.data - if blockData: # block has data - scale_tree(blockData, scale) # scale the data - - if isinstance(niBlock, NifFormat.NiTriBasedGeom): # is it a shape? - # Scale all vertices - shapedata = niBlock.data - vertlist = shapedata.vertices - if vertlist: - for vert in vertlist: - vert.x *= scale - vert.y *= scale - vert.z *= scale