def test_bounding_box_bsbound(self):
        """Oblivion bounding box (BSBound) test."""
        def check_bsbound(root_blocks):
            bsbound = root_blocks[0].extra_data_list[0]
            assert (isinstance(bsbound, NifFormat.BSBound))
            assert (bsbound.name == "BBX")
            assert (bsbound.next_extra_data is None)
            # using assert_equal because we compare floats
            self.assert_equal(bsbound.center.x, 0.0)
            self.assert_equal(bsbound.center.y, 0.0)
            self.assert_equal(bsbound.center.z, 66.2201843262)
            self.assert_equal(bsbound.dimensions.x, 23.0976696014)
            self.assert_equal(bsbound.dimensions.y, 17.6446208954)
            self.assert_equal(bsbound.dimensions.z, 66.2201843262)

        # import
        with closing(open('test/nif/bounding_box_bsbound.nif')) as stream:
            self.info("Reading test/nif/bounding_box_bsbound.nif")
            nif = NifFormat.Data()
            nif.read(stream)
            check_bsbound(nif.roots)
        nif_import = self.test(filename='test/nif/bounding_box_bsbound.nif')
        b_bbox = Blender.Object.Get("BSBound")
        # test stuff
        assert (b_bbox.display_bounds_type == 'BOX')
        # export
        nif_export = self.test(filename='test/nif/_bounding_box_bsbound.nif',
                               config=dict(game='OBLIVION'),
                               selection=['BSBound'])
        # test stuff...
        with closing(open('test/nif/_bounding_box_bsbound.nif')) as stream:
            self.info("Reading test/nif/_bounding_box_bsbound.nif")
            nif = NifFormat.Data()
            nif.read(stream)
            check_bsbound(nif.roots)
Example #2
0
    def build_nif_matrix(cls):

        n_mat = NifFormat.Matrix44()
        translation = (2.0, 3.0, 4.0)
        scale = 2.0

        n_rhs_rot_x = (1.0, 0.0, 0.0, 0.0, 0.866, 0.5, 0.0, -0.5, 0.866)

        n_rhs_rot_y = (0.5, 0.0, -0.866, 0.0, 1.0, 0.0, 0.866, 0.0, 0.5)

        n_rhs_rot_z = (0, 1, 0, -1, 0, 0, 0, 0, 1)

        n_rhs_rot_x = cls.create_matrix(n_rhs_rot_x)
        n_rhs_rot_y = cls.create_matrix(n_rhs_rot_y)
        n_rhs_rot_z = cls.create_matrix(n_rhs_rot_z)

        n_mat33 = n_rhs_rot_z * n_rhs_rot_y * n_rhs_rot_x

        n_vec3 = NifFormat.Vector3()
        n_vec3.x = translation[0]
        n_vec3.y = translation[1]
        n_vec3.z = translation[2]

        n_mat.set_scale_rotation_translation(scale, n_mat33, n_vec3)

        return n_mat
def n_create_blocks(n_data):
    n_ninode_1 = NifFormat.NiNode()
    n_ninode_2 = NifFormat.NiNode()
    n_data.roots = [n_ninode_1]

    with ref(n_ninode_1) as n_ninode:
        n_ninode.name = b'Scene Root'
        n_ninode.num_children = 1
        n_ninode.children.update_size()
        n_ninode.children[0] = n_ninode_2
    with ref(n_ninode_2) as n_ninode:
        n_ninode.name = b'NifObject'
        n_ninode.flags = 14
        with ref(n_ninode.translation) as n_vector3:
            n_vector3.x = 20
            n_vector3.y = 20
            n_vector3.z = 20
        with ref(n_ninode.rotation) as n_matrix33:
            n_matrix33.m_11 = -1.43774e-14
            n_matrix33.m_21 = -0.866025
            n_matrix33.m_31 = 0.5
            n_matrix33.m_12 = 0.5
            n_matrix33.m_22 = 0.433013
            n_matrix33.m_32 = 0.75
            n_matrix33.m_13 = -0.866025
            n_matrix33.m_23 = 0.25
            n_matrix33.m_33 = 0.433013
            assert (n_matrix33.is_rotation()
                    )  # make sure in case we change values:
        n_ninode.scale = 0.75
    return n_data
    def build_nif_matrix(cls):
         
        n_mat = NifFormat.Matrix44()
        translation = (2.0, 3.0, 4.0)
        scale = 2.0
        
        rhsrotx = (1.0, 0.0, 0.0,
                   0.0, 0.866, 0.5,
                   0.0, -0.5, 0.866)
        
        rhsroty = (0.5, 0.0, -0.866,
                   0.0, 1.0, 0.0,
                   0.866, 0.0, 0.5)
       
        rhsrotz = (0, 1, 0,
                   -1, 0, 0,
                   0, 0, 1)
        
        rhsrotx = cls.create_matrix(rhsrotx)
        rhsroty = cls.create_matrix(rhsroty)
        rhsrotz = cls.create_matrix(rhsrotz)
         
        n_mat33 = rhsrotz * rhsroty * rhsrotx
         
        n_vec3 = NifFormat.Vector3()
        n_vec3.x = translation[0]
        n_vec3.y = translation[1]
        n_vec3.z = translation[2]
 
        n_mat.set_scale_rotation_translation(scale, n_mat33, n_vec3)
        
        return n_mat
Example #5
0
 def branchentry(self, branch):
     if not isinstance(branch, NifFormat.bhkConvexVerticesShape):
         # keep recursing
         return True
     else:
         self.toaster.msg("checking vertices and planes")
         for v4 in branch.vertices:
             v = NifFormat.Vector3()
             v.x = v4.x
             v.y = v4.y
             v.z = v4.z
             num_intersect = 0
             for n4 in branch.normals:
                 n = NifFormat.Vector3()
                 n.x = n4.x
                 n.y = n4.y
                 n.z = n4.z
                 d   = n4.w
                 if abs(v * n + d) < 0.01:
                     num_intersect += 1
             if num_intersect == 0:
                 self.toaster.logger.error(
                     "vertex %s does not intersect with any plane" % v)
             elif num_intersect == 1:
                 self.toaster.logger.warn(
                     "vertex %s only intersects with one plane" % v)
             elif num_intersect == 2:
                 self.toaster.logger.warn(
                     "vertex %s only intersects with two planes" % v)
         # stop recursing
         return False
    def test_mw_nifxnifkf(self):
        """Test the nif xnif kf export option."""
        def check_ctrl_flags(root):
            # test the kfctrl flags to be active + clamp
            for ctrl in root.get_global_iterator():
                if not isinstance(ctrl, NifFormat.NiTimeController):
                    continue
                if ctrl.flags != 12:
                    raise ValueError("bad value for controller flags")

        # import a nif with animation
        dance = self.test(filename='test/nif/mw/dance.nif')
        check_ctrl_flags(dance.root_blocks[0])
        # export as nif + xnif + kf
        self.test(filename='test/nif/mw/_testnifxnifkf.nif',
                  config=dict(game='MORROWIND', animation='ALL_NIF_XNIF_XKF'),
                  selection=['Dance'],
                  next_layer=True)
        # check that these files are present, and check some of their properties
        with closing(open('test/nif/mw/_testnifxnifkf.nif')) as stream:
            self.info("Reading test/nif/mw/_testnifxnifkf.nif")
            nif = NifFormat.Data()
            nif.read(stream)
        with closing(open('test/nif/mw/x_testnifxnifkf.nif')) as stream:
            self.info("Reading test/nif/mw/x_testnifxnifkf.nif")
            xnif = NifFormat.Data()
            xnif.read(stream)
        with closing(open('test/nif/mw/x_testnifxnifkf.kf')) as stream:
            self.info("Reading test/nif/mw/x_testnifxnifkf.kf")
            xkf = NifFormat.Data()
            xkf.read(stream)
        # check root blocks
        assert (len(nif.roots) == 1)
        assert (len(xnif.roots) == 1)
        assert (len(xkf.roots) == 1)
        assert (isinstance(nif.roots[0], NifFormat.NiNode))
        assert (isinstance(xnif.roots[0], NifFormat.NiNode))
        assert (isinstance(xkf.roots[0], NifFormat.NiSequenceStreamHelper))
        # compare text keys
        nif_textkeys = nif.roots[0].extra_data
        xkf_textkeys = xkf.roots[0].extra_data
        assert (isinstance(nif_textkeys, NifFormat.NiTextKeyExtraData))
        assert (isinstance(xkf_textkeys, NifFormat.NiTextKeyExtraData))
        #assert(nif_textkeys == xkf_textkeys) # ... up to extra data chain
        # check that xkf has no target set in keyframe controller
        ctrl = xkf.roots[0].controller
        while ctrl:
            if ctrl.target is not None:
                raise ValueError(
                    "NiKeyframeController target should be None in xkf")
            ctrl = ctrl.next_controller
        # check controller flags
        check_ctrl_flags(xkf.roots[0])
 def export_inventory_marker(n_root, root_objects):
     if bpy.context.scene.niftools_scene.game in ('SKYRIM', ):
         for root_object in root_objects:
             if root_object.niftools_bs_invmarker:
                 for extra_item in n_root.extra_data_list:
                     if isinstance(extra_item, NifFormat.BSInvMarker):
                         raise NifError(
                             "Multiple Items have Inventory marker data only one item may contain this data"
                         )
                 else:
                     n_extra_list = NifFormat.BSInvMarker()
                     n_extra_list.name = root_object.niftools_bs_invmarker[
                         0].name.encode()
                     n_extra_list.rotation_x = (
                         -root_object.niftools_bs_invmarker[0].bs_inv_x %
                         (2 * pi)) * 1000
                     n_extra_list.rotation_y = (
                         -root_object.niftools_bs_invmarker[0].bs_inv_y %
                         (2 * pi)) * 1000
                     n_extra_list.rotation_z = (
                         -root_object.niftools_bs_invmarker[0].bs_inv_z %
                         (2 * pi)) * 1000
                     n_extra_list.zoom = root_object.niftools_bs_invmarker[
                         0].bs_inv_zoom
                     n_root.add_extra_data(n_extra_list)
    def __init__(self):
        """Initialize the test."""
        Base.__init__(self)

        self.n_data = NifFormat.Data()

        fp = INTEGRATION_ROOT
        root = os.path.join(fp, "gen")

        nif_path = os.path.join(root, "nif", self.g_path)
        nif_file_path = nif_path + os.path.sep + self.g_name
        self.n_filepath_0 = nif_file_path + "_py_code.nif"
        self.n_filepath_1 = nif_file_path + "_export_py_code.nif"
        self.n_filepath_2 = nif_file_path + "_export_user_ver.nif"

        blend_path = os.path.join(root, "autoblend", self.g_path)
        blend_file_path = blend_path + os.path.sep + self.g_name
        self.b_filepath_0 = blend_file_path + "_pycode_import.blend"
        self.b_filepath_1 = blend_file_path + "_userver.blend"
        self.b_filepath_2 = blend_file_path + "_userver_reimport.blend"
        self.b_filepath_except = blend_file_path + "_exception.blend"

        if not os.path.exists(nif_path):
            os.makedirs(nif_path)

        if not os.path.exists(blend_path):
            os.makedirs(blend_path)
Example #9
0
    def n_attach_material_prop(self, n_block):
        '''Attach a NiMaterialProperty to a blocks properties array at pos[0]'''

        n_nimaterialprop = NifFormat.NiMaterialProperty()

        # add property to top of list
        n_block.properties.reverse()

        n_block.num_properties += 1
        n_block.properties.update_size()
        n_block.properties[-1] = n_nimaterialprop

        n_block.properties.reverse()

        n_nimaterialprop.name = b'Material'
        with ref(n_nimaterialprop.ambient_color) as ambient_color:
            ambient_color.r = 1.0
            ambient_color.g = 1.0
            ambient_color.b = 1.0
        with ref(n_nimaterialprop.diffuse_color) as diffuse_color:
            diffuse_color.r = 1.0
            diffuse_color.g = 1.0
            diffuse_color.b = 1.0
        with ref(n_nimaterialprop.emissive_color) as emissive_color:
            emissive_color.r = 0.5
        n_nimaterialprop.glossiness = 25.0
        n_nimaterialprop.alpha = 1.0

        return n_block
def n_attach_bhkconvextransform(n_bhkshape):
    '''Attaches a bhkTransform shape to store transform information'''

    n_bhkconvextransformshape = NifFormat.bhkConvexTransformShape()
    n_bhkshape.shape = n_bhkconvextransformshape

    with ref(n_bhkconvextransformshape) as n_bhktransform:
        n_bhktransform.material = NifFormat.HavokMaterial.HAV_MAT_WOOD  # 9
        n_bhktransform.unknown_float_1 = 0.1
        n_bhktransform.unknown_8_bytes.update_size()
        n_bhktransform.unknown_8_bytes[0] = 96
        n_bhktransform.unknown_8_bytes[1] = 120
        n_bhktransform.unknown_8_bytes[2] = 53
        n_bhktransform.unknown_8_bytes[3] = 19
        n_bhktransform.unknown_8_bytes[4] = 24
        n_bhktransform.unknown_8_bytes[5] = 9
        n_bhktransform.unknown_8_bytes[6] = 253
        n_bhktransform.unknown_8_bytes[7] = 4

        with ref(n_bhktransform.transform) as n_matrix44:
            n_matrix44.m_11 = -2.23517e-08
            n_matrix44.m_21 = 0.649519
            n_matrix44.m_31 = 0.375
            n_matrix44.m_12 = -0.375
            n_matrix44.m_22 = -0.32476
            n_matrix44.m_32 = 0.5625
            n_matrix44.m_13 = 0.649519
            n_matrix44.m_23 = -0.1875
            n_matrix44.m_33 = 0.324759
            n_matrix44.m_14 = 2.85714
            n_matrix44.m_24 = 2.85714
            n_matrix44.m_34 = 2.85714

    return n_bhktransform
Example #11
0
    def export_root_node(self, root_objects, filebase):
        """ Exports a nif's root node; use blender root if there is only one, else create a meta root """
        # TODO [collsion] detect root collision -> root collision node (can be mesh or empty)
        #     self.nif_export.collisionhelper.export_collision(b_obj, n_parent)
        #     return None  # done; stop here
        self.n_root = None
        # there is only one root object so that will be our final root
        if len(root_objects) == 1:
            b_obj = root_objects[0]
            self.export_node(b_obj, None)

        # there is more than one root object so we create a meta root
        else:
            NifLog.info("Created meta root because blender scene had multiple root objects")
            self.n_root = types.create_ninode()
            self.n_root.name = "Scene Root"
            for b_obj in root_objects:
                self.export_node(b_obj, self.n_root)

        # TODO [object] How dow we know we are selecting the right node in the case of multi-root?
        # making root block a fade node
        root_type = b_obj.niftools.rootnode
        if bpy.context.scene.niftools_scene.game in ('FALLOUT_3', 'SKYRIM') and root_type == 'BSFadeNode':
            NifLog.info("Making root block a BSFadeNode")
            fade_root_block = NifFormat.BSFadeNode().deepcopy(self.n_root)
            fade_root_block.replace_global_node(self.n_root, fade_root_block)
            self.n_root = fade_root_block

        # various extra datas
        object_property = ObjectDataProperty()
        object_property.export_bsxflags_upb(self.n_root, root_objects)
        object_property.export_inventory_marker(self.n_root, root_objects)
        object_property.export_weapon_location(self.n_root, b_obj)
        types.export_furniture_marker(self.n_root, filebase)
        return self.n_root
Example #12
0
def export(path) -> List[ShapeCollection]:
    data = NifFormat.Data()
    with open(path, 'rb') as f:
        data.inspect(f)
        data.read(f)

    objects = []
    for root in data.roots:
        name = root.name.decode('utf-8').lower()
        # For this project, only hair and head matters
        if 'hair' in name or 'head' in name:
            sys.stderr.write("> " + name + "\n")
        else:
            sys.stderr.write("  " + name + "\n")
            continue

        if isinstance(root, NifFormat.NiNode):
            objects.append(export_object(root))

        # Some NiTriShape's are a root object
        if isinstance(root, NifFormat.NiTriShape):
            coll = ShapeCollection(name)
            coll.shapes = [export_shape(root)]

            objects.append(coll)

    return objects
    def export_texturing_property(self,
                                  flags=0x0001,
                                  applymode=None,
                                  b_mat=None,
                                  b_obj=None):
        """Export texturing property."""

        self.determine_texture_types(b_obj, b_mat)

        texprop = NifFormat.NiTexturingProperty()

        texprop.flags = flags
        texprop.apply_mode = applymode
        texprop.texture_count = 7

        self.export_texture_shader_effect(texprop)
        self.export_nitextureprop_tex_descs(texprop)

        # search for duplicate
        for block in self.nif_export.dict_blocks:
            if isinstance(block, NifFormat.NiTexturingProperty
                          ) and block.get_hash() == texprop.get_hash():
                return block

        # no texturing property with given settings found, so use and register
        # the new one
        return texprop
    def export_bs_effect_shader_property(self, b_mat):
        bsshader = NifFormat.BSEffectShaderProperty()

        self.texturehelper.export_bs_effect_shader_prop_textures(bsshader)

        # Alpha
        # TODO [Shader] Alpha property
        # if b_mat.use_transparency:
        #     bsshader.alpha = (1 - b_mat.alpha)

        # clamp Mode
        bsshader.texture_clamp_mode = 65283

        # Emissive
        bsshader.emissive_color.r = b_mat.niftools.emissive_color.r
        bsshader.emissive_color.g = b_mat.niftools.emissive_color.g
        bsshader.emissive_color.b = b_mat.niftools.emissive_color.b
        bsshader.emissive_color.a = b_mat.niftools.emissive_alpha.v
        # TODO [shader] Expose a emission multiplier value
        # bsshader.emissive_multiple = b_mat.emit

        # Shader Flags
        BSShaderProperty.export_shader_flags(b_mat, bsshader)

        return bsshader
Example #15
0
def postprocessNif(filename):
    input_stream = open(filename, "rb")
    nifdata = NifFormat.Data()
    try:
        nifdata.read(input_stream)
    except Exception as e:
        # nif-read error, something wrong with NIF, delete...
        input_stream.close()
        os.remove(filename)
        s = "\n\npostprocessNif(): ERROR reading(" + filename + "); deleting file."
        #        print(s)
        error_list(s)
        return -1
    input_stream.close()
    try:
        nifdata = postprocess_nifdata(nifdata)
        output_stream = open(filename, "wb")
        nifdata.write(output_stream)
        output_stream.close()
        print "PostProcessing(" + filename + ") complete."
    except Exception as e:
        s = "\n\nERROR post-processing(" + filename + ")..."
        #        print(s)
        error_list(s)
    return 0
Example #16
0
def n_attach_bhkcollisionobject(n_ninode):
    '''Attaches a collision object to the NiNode'''
    
    n_bhkcollisionobject = NifFormat.bhkCollisionObject()
    n_ninode.collision_object = n_bhkcollisionobject #attach to ninode
    n_bhkcollisionobject.target = n_ninode
    return n_bhkcollisionobject
Example #17
0
def walk_nif(nif_path, use_stdout=True):
    if not path.exists(nif_path):
        exit("Path `{0}` not found.".format(nif_path))

    all_assets = []
    for stream, data in NifFormat.walkData(nif_path):
        try:
            if use_stdout:
                print(stream.name, sep='', end=', ', file=stdout, flush=True)
            data.read(stream)
            assets = []
            find_external_assets(data.roots, assets)
            assets = set(assets)  # remove duplicates
            assets_string = "{0}".format(
                b', '.join(assets).decode(encoding="ISO-8859-1"))
            all_assets.append(assets_string)
            if use_stdout:
                print(assets_string,
                      sep=', ',
                      end='\n',
                      file=stdout,
                      flush=True)
        except ValueError as ex:
            print("\n Error with {0}: {1}".format(stream.name, str(ex.args)),
                  sep='',
                  end='\n',
                  file=stdout,
                  flush=True)
        except Exception as ex:
            print(ex)
            raise
    return all_assets
Example #18
0
 def branchentry(self, branch):
     if isinstance(branch, NifFormat.bhkRigidBody):
         if isinstance(branch.shape, (NifFormat.bhkNiTriStripsShape,
                                      NifFormat.bhkPackedNiTriStripsShape)):
             colmopp = NifFormat.bhkMoppBvTreeShape()
             colmopp.material = branch.shape.material
             colmopp.unknown_8_bytes[0] = 160
             colmopp.unknown_8_bytes[1] = 13
             colmopp.unknown_8_bytes[2] = 75
             colmopp.unknown_8_bytes[3] = 1
             colmopp.unknown_8_bytes[4] = 192
             colmopp.unknown_8_bytes[5] = 207
             colmopp.unknown_8_bytes[6] = 144
             colmopp.unknown_8_bytes[7] = 11
             colmopp.unknown_float = 1.0
             if isinstance(branch.shape, NifFormat.bhkNiTriStripsShape):
                 branch.shape = branch.shape.get_interchangeable_packed_shape(
                 )
             colmopp.shape = branch.shape
             branch.shape = colmopp
             self.changed = True
             branch.shape.update_mopp()
             self.toaster.msg("collision set to MOPP")
         # Don't need to recurse further
         return False
     else:
         # recurse further
         return True
Example #19
0
 def run(self):
     # fo3 body path
     fo3_male = os.path.join(self.config.get("path", "fallout3"), "meshes",
                             "characters", "_male")
     nif_import = self.test(filename=os.path.join(fo3_male, "skeleton.nif"),
                            config=dict(IMPORT_ANIMATION=True))
     nif_export = self.test(filename='test/nif/fo3/_skeleton.nif',
                            config=dict(game='FALLOUT_3',
                                        EXPORT_SMOOTHOBJECTSEAMS=True,
                                        EXPORT_FLATTENSKIN=True),
                            selection=['Scene Root'])
     # open original nif to get rid of possible issues with scale
     # correction in nif_import
     data = NifFormat.Data()
     with open(os.path.join(fo3_male, "skeleton.nif")) as stream:
         data.read(stream)
     # compare NiNode transforms
     for n_imp_node in data.roots[0].tree():
         if not isinstance(n_imp_node, NifFormat.NiNode):
             continue
         for n_exp_node in nif_export.root_blocks[0].tree():
             if not isinstance(n_exp_node, NifFormat.NiNode):
                 continue
             if n_imp_node.name != n_exp_node.name:
                 continue
             # check that transforms are equal
             self.info("checking transform of %s" % n_imp_node.name)
             if (n_imp_node.get_transform() != n_exp_node.get_transform()):
                 raise ValueError("transforms are different\n%s\n%s\n" %
                                  (n_imp_node.get_transform(),
                                   n_exp_node.get_transform()))
    def import_egm_morphs(self, egmdata, b_obj, v_map, n_verts):
        """Import all EGM morphs as shape keys for blender object."""
        # XXX if there is an egm, the assumption is that there is only one
        # XXX mesh in the nif
        b_mesh = b_obj.data
        sym_morphs = [list(morph.get_relative_vertices())
                      for morph in egmdata.sym_morphs]
        asym_morphs = [list(morph.get_relative_vertices())
                      for morph in egmdata.asym_morphs]
        
        # insert base key at frame 1, using absolute keys
        sk_basis = b_obj.shape_key_add("Basis")
        b_mesh.shape_keys.use_relative = False

        morphs = ([(morph, "EGM SYM %i" % i)
                   for i, morph in enumerate(sym_morphs)]
                  + 
                  [(morph, "EGM ASYM %i" % i)
                   for i, morph in enumerate(asym_morphs)])

        for morphverts, keyname in morphs:
            #convert tuples into vector here so we can simply add in morph_mesh()
            morphvert_out = []
            for u in morphverts:
                v = NifFormat.Vector3()
                v.x, v.y, v.z = u
                morphvert_out.append(v)
            self.morph_mesh(b_mesh, n_verts, morphvert_out, v_map)
            shape_key = b_obj.shape_key_add(keyname, from_mix=False)
    def import_bone_bind(self, n_block, b_armature_data, n_armature, b_parent_bone=None):
        """Adds a bone to the armature in edit mode."""
        # check that n_block is indeed a bone
        if not self.is_bone(n_block):
            return None
        # bone name
        bone_name = block_store.import_name(n_block)
        # create a new bone
        b_edit_bone = b_armature_data.edit_bones.new(bone_name)
        # store nif block for access from object mode
        self.name_to_block[b_edit_bone.name] = n_block
        # get the nif bone's armature space matrix (under the hood all bone space matrixes are multiplied together)
        n_bind = math.nifformat_to_mathutils_matrix(self.bind_store.get(n_block, NifFormat.Matrix44()))
        # get transformation in blender's coordinate space
        b_bind = math.nif_bind_to_blender_bind(n_bind)

        # set the bone matrix - but set the tail first to prevent issues with zero-length bone
        b_edit_bone.tail = mathutils.Vector([0, 0, 1])
        b_edit_bone.matrix = b_bind
        # link to parent
        if b_parent_bone:
            b_edit_bone.parent = b_parent_bone
        # import and parent bone children
        for n_child in n_block.children:
            self.import_bone_bind(n_child, b_armature_data, n_armature, b_edit_bone)
Example #22
0
 def branchentry(self, branch):
     if isinstance(branch, NifFormat.bhkRigidBody):
         if isinstance(branch.shape, (NifFormat.bhkNiTriStripsShape,
                                      NifFormat.bhkPackedNiTriStripsShape)):
             colmopp = NifFormat.bhkMoppBvTreeShape()
             colmopp.material = branch.shape.material
             colmopp.unknown_8_bytes[0] = 160
             colmopp.unknown_8_bytes[1] = 13
             colmopp.unknown_8_bytes[2] = 75
             colmopp.unknown_8_bytes[3] = 1
             colmopp.unknown_8_bytes[4] = 192
             colmopp.unknown_8_bytes[5] = 207
             colmopp.unknown_8_bytes[6] = 144
             colmopp.unknown_8_bytes[7] = 11
             colmopp.unknown_float = 1.0
             if isinstance(branch.shape, NifFormat.bhkNiTriStripsShape):
                 branch.shape = branch.shape.get_interchangeable_packed_shape()
             colmopp.shape = branch.shape
             branch.shape = colmopp
             self.changed = True
             branch.shape.update_mopp()
             self.toaster.msg("collision set to MOPP")
         # Don't need to recurse further
         return False
     else:
         # recurse further
         return True
Example #23
0
def n_attach_material_prop(n_trishape):
    '''Attaches a NiMaterialProperty to a NiTrishape block property's array at pos[0]'''

    n_nimaterialprop = NifFormat.NiMaterialProperty()
    n_nimaterialprop.name = b'Material'

    with ref(n_nimaterialprop.ambient_color) as n_color3:
        n_color3.r = 1.0
        n_color3.g = 1.0
        n_color3.b = 1.0

    with ref(n_nimaterialprop.diffuse_color) as n_color3:
        n_color3.r = 1.0
        n_color3.g = 1.0
        n_color3.b = 1.0

    with ref(n_nimaterialprop.emissive_color) as n_color3:
        n_color3.r = 0.0
        n_color3.g = 0.0
        n_color3.b = 0.0

    with ref(n_nimaterialprop.specular_color) as n_color3:
        n_color3.r = 0.0
        n_color3.g = 0.0
        n_color3.b = 0.0

    n_nimaterialprop.glossiness = 12.5  # default nif.xml - 0.0, blender - 12.5
    n_nimaterialprop.alpha = 1.0  # default nif.xml - 0.0

    # add property to top of list
    n_trishape.properties.reverse()
    n_trishape.num_properties += 1
    n_trishape.properties.update_size()
    n_trishape.properties[-1] = n_nimaterialprop
    n_trishape.properties.reverse()
Example #24
0
    def import_bone_bind(self, n_block, n_bind_store, b_armature_data, n_armature, b_parent_bone=None):
        """Adds a bone to the armature in edit mode."""
        # check that n_block is indeed a bone
        if not self.is_bone(n_block):
            return None
        # bone name
        bone_name = block_store.import_name(n_block)
        # create a new bone
        b_edit_bone = b_armature_data.edit_bones.new(bone_name)
        # store nif block for access from object mode
        self.name_to_block[b_edit_bone.name] = n_block
        # get the nif bone's armature space matrix (under the hood all bone space matrixes are multiplied together)
        n_bind = mathutils.Matrix(n_bind_store.get(n_block, NifFormat.Matrix44()).as_list()).transposed()
        # get transformation in blender's coordinate space
        b_bind = math.nif_bind_to_blender_bind(n_bind)

        # the following is a workaround because blender can no longer set matrices to bones directly
        tail, roll = bpy.types.Bone.AxisRollFromMatrix(b_bind.to_3x3())
        b_edit_bone.head = b_bind.to_translation()
        b_edit_bone.tail = tail + b_edit_bone.head
        b_edit_bone.roll = roll
        # link to parent
        if b_parent_bone:
            b_edit_bone.parent = b_parent_bone
        # import and parent bone children
        for n_child in n_block.children:
            self.import_bone_bind(n_child, n_bind_store, b_armature_data, n_armature, b_edit_bone)
Example #25
0
def n_create_data():
    n_data = NifFormat.Data()
    n_data.version = 0x14000005
    n_data.user_version = 11
    n_data.user_version_2 = 11
    n_create_blocks(n_data)
    return n_data
Example #26
0
def mathutils_to_nifformat_matrix(b_matrix):
    """Convert a blender matrix to a NifFormat.Matrix44"""
    # transpose to swap columns for rows so we can use pyffi's set_rows() directly
    # instead of setting every single value manually
    n_matrix = NifFormat.Matrix44()
    n_matrix.set_rows(*b_matrix.transposed())
    return n_matrix
    def import_skin(ni_block, b_obj):
        """Import a NiSkinInstance and its contents as vertex groups"""
        skininst = ni_block.skin_instance
        if skininst:
            skindata = skininst.data
            bones = skininst.bones
            # the usual case
            if skindata.has_vertex_weights:
                bone_weights = skindata.bone_list
                for idx, n_bone in enumerate(bones):
                    # skip empty bones (see pyffi issue #3114079)
                    if not n_bone:
                        continue

                    vertex_weights = bone_weights[idx].vertex_weights
                    group_name = block_store.import_name(n_bone)
                    if group_name not in b_obj.vertex_groups:
                        v_group = b_obj.vertex_groups.new(name=group_name)

                    for skinWeight in vertex_weights:
                        vert = skinWeight.index
                        weight = skinWeight.weight
                        v_group.add([vert], weight, 'REPLACE')

            # WLP2 - hides the weights in the partition
            else:
                skin_partition = skininst.skin_partition
                for block in skin_partition.skin_partition_blocks:
                    # create all vgroups for this block's bones
                    block_bone_names = [block_store.import_name(bones[i]) for i in block.bones]
                    for group_name in block_bone_names:
                        b_obj.vertex_groups.new(name=group_name)

                    # go over each vert in this block
                    for vert, vertex_weights, bone_indices in zip(block.vertex_map, block.vertex_weights, block.bone_indices):

                        # assign this vert's 4 weights to its 4 vgroups (at max)
                        for w, b_i in zip(vertex_weights, bone_indices):
                            if w > 0:
                                group_name = block_bone_names[b_i]
                                v_group = b_obj.vertex_groups[group_name]
                                v_group.add([vert], w, 'REPLACE')

        # import body parts as face maps
        # get faces (triangles) as map of tuples to index
        tri_map = {frozenset(polygon.vertices): polygon.index for polygon in b_obj.data.polygons}
        if isinstance(skininst, NifFormat.BSDismemberSkinInstance):
            skinpart = ni_block.get_skin_partition()
            for bodypart, skinpartblock in zip(skininst.partitions, skinpart.skin_partition_blocks):
                bodypart_wrap = NifFormat.BSDismemberBodyPartType()
                bodypart_wrap.set_value(bodypart.body_part)
                group_name = bodypart_wrap.get_detail_display()

                # create face map if it did not exist yet
                if group_name not in b_obj.face_maps:
                    f_group = b_obj.face_maps.new(name=group_name)

                # add the triangles to the face map
                f_group.add([tri_map[frozenset(vertices)] for vertices in skinpartblock.get_mapped_triangles()])
Example #28
0
    def export_uv_controller(self, b_material, n_geom):
        """Export the material UV controller data."""

        # get fcurves - a bit more elaborate here so we can zip with the NiUVData later
        # nb. these are actually specific to the texture slot in blender
        # here we don't care and just take the first fcurve that matches
        fcurves = []
        for dp, ind in (("offset", 0), ("offset", 1), ("scale", 0), ("scale",
                                                                     1)):
            for fcu in b_material.animation_data.action.fcurves:
                if dp in fcu.data_path and fcu.array_index == ind:
                    fcurves.append(fcu)
                    break
            else:
                fcurves.append(None)

        # continue if at least one fcurve exists
        if not any(fcurves):
            return

        # get the uv curves and translate them into nif data
        n_uv_data = NifFormat.NiUVData()
        for fcu, n_uv_group in zip(fcurves, n_uv_data.uv_groups):
            if fcu:
                NifLog.debug(f"Exporting {fcu} as NiUVData")
                n_uv_group.num_keys = len(fcu.keyframe_points)
                n_uv_group.interpolation = NifFormat.KeyType.LINEAR_KEY
                n_uv_group.keys.update_size()
                for b_point, n_key in zip(fcu.keyframe_points,
                                          n_uv_group.keys):
                    # add each point of the curve
                    b_frame, b_value = b_point.co
                    if "offset" in fcu.data_path:
                        # offsets are negated in blender
                        b_value = -b_value
                    n_key.arg = n_uv_group.interpolation
                    n_key.time = b_frame / self.fps
                    n_key.value = b_value

        # if uv data is present then add the controller so it is exported
        if fcurves[0].keyframe_points:
            n_uv_ctrl = NifFormat.NiUVController()
            self.set_flags_and_timing(n_uv_ctrl, fcurves)
            n_uv_ctrl.data = n_uv_data
            # attach block to geometry
            n_geom.add_controller(n_uv_ctrl)
 def test_nonaccum_export(self):
     """Test the nonaccum xy export option."""
     # import a nif with animation
     dance = self.test(
         filename = 'test/nif/mw/dance.nif')
     # export as nif with animation, default
     self.test(
         filename='test/nif/ob/_testnonaccum_default.nif',
         config=dict(game='OBLIVION',
                     EXPORT_NONACCUM=0),
         selection=['Dance'],
         next_layer=False)
     # export as nif with animation, accum xy
     self.test(
         filename='test/nif/ob/_testnonaccum_accumxy.nif',
         config=dict(game='OBLIVION',
                     EXPORT_NONACCUM=1),
         selection=['Dance'],
         next_layer=False)
     # export as nif with animation, no accum
     self.test(
         filename='test/nif/ob/_testnonaccum_accumnone.nif',
         config=dict(game='OBLIVION',
                     EXPORT_NONACCUM=2),
         selection=['Dance'],
         next_layer=False)
     # check that these files are present, and check some of their properties
     with closing(open('test/nif/ob/_testnonaccum_default.nif')) as stream:
         self.info("Reading test/nif/ob/_testnonaccum_default.nif")
         nif_default = NifFormat.Data()
         nif_default.read(stream)
     with closing(open('test/nif/ob/_testnonaccum_accumxy.nif')) as stream:
         self.info("Reading test/nif/ob/_testnonaccum_accumxy.nif")
         nif_xy = NifFormat.Data()
         nif_xy.read(stream)
     with closing(open('test/nif/ob/_testnonaccum_accumnone.nif')) as stream:
         self.info("Reading test/nif/ob/_testnonaccum_accumnone.nif")
         nif_none = NifFormat.Data()
         nif_none.read(stream)
     # check root blocks
     assert(len(nif_default.roots) == 1)
     assert(len(nif_xy.roots) == 1)
     assert(len(nif_none.roots) == 1)
     assert(isinstance(nif_default.roots[0], NifFormat.NiNode))
     assert(isinstance(nif_xy.roots[0], NifFormat.NiNode))
     assert(isinstance(nif_none.roots[0], NifFormat.NiNode))
Example #30
0
 def export_material_uv_controller(self, b_material, n_geom):
     """Export the material UV controller data."""
     # get the material action
     b_action = b_material.action
     if not b_action:
         return
     # get the uv curves and translate them into nif data
     n_uvdata = NifFormat.NiUVData()
     n_times = []  # track all times (used later in start time and end time)
     b_channels = (Blender.Ipo.MA_OFSX, Blender.Ipo.MA_OFSY,
                   Blender.Ipo.MA_SIZEX, Blender.Ipo.MA_SIZEY)
     for b_channel, n_uvgroup in zip(b_channels, n_uvdata.uv_groups):
         b_curve = b_action[b_channel]
         if b_curve:
             self.info("Exporting %s as NiUVData" % b_curve)
             n_uvgroup.num_keys = len(b_curve.bezierPoints)
             n_uvgroup.interpolation = self.get_n_action_interpolation_from_b_action_interpolation(
                 b_curve.interpolation)
             n_uvgroup.keys.update_size()
             for b_point, n_key in zip(b_curve.bezierPoints,
                                       n_uvgroup.keys):
                 # add each point of the curve
                 b_time, b_value = b_point.pt
                 if b_channel in (Blender.Ipo.MA_OFSX, Blender.Ipo.MA_OFSY):
                     # offsets are negated in blender
                     b_value = -b_value
                 n_key.arg = n_uvgroup.interpolation
                 n_key.time = (b_time - 1) * self.context.scene.render.fps
                 n_key.value = b_value
                 # track time
                 n_times.append(n_key.time)
             # save extend mode to export later
             b_curve_extend = b_curve.extend
     # if uv data is present (we check this by checking if times were added)
     # then add the controller so it is exported
     if n_times:
         n_uvctrl = NifFormat.NiUVController()
         n_uvctrl.flags = 8  # active
         n_uvctrl.flags |= self.get_flags_from_extend(b_curve_extend)
         n_uvctrl.frequency = 1.0
         n_uvctrl.start_time = min(n_times)
         n_uvctrl.stop_time = max(n_times)
         n_uvctrl.data = n_uvdata
         # attach block to geometry
         n_geom.add_controller(n_uvctrl)
Example #31
0
def n_create_stencil_prop(n_trishapedata):
    n_nistencilproperty = NifFormat.NiStencilProperty()
    n_trishapedata.properties.reverse()  # ensure property is at top of list

    n_trishapedata.num_properties += 1
    n_trishapedata.properties.update_size()
    n_trishapedata.properties[-1] = n_nistencilproperty

    n_trishapedata.properties.reverse()
Example #32
0
def LoadNif( filename ):
    global Version, UserVersion
    try:
        print "Reading Nif: %s" % filename
        f = open( filename, "rb" )
        Version, UserVersion = NifFormat.getVersion( f )
        if Version >= 0:
                print "( Version 0x%08X )" % Version
                root_blocks = NifFormat.read(f, version = Version, user_version = UserVersion, verbose = 0)
                for block in root_blocks:
                    AddBlock( block )
        elif Version == -1:
            raise NIFImportError( "Unsupported NIF version." )
        else:
            raise NIFImportError( "Not a NIF file." )

    except NIFImportError, e: # in that case, we raise a menu instead of an exception
        print 'NIFImportError: ' + e.value
        return
Example #33
0
    def nif_explore(self):
        oldtime = time.time() #Start the clock :)
        """NifFormate.walkData searches the directory for files"""
        for stream, data in NifFormat.walkData(self.search_path):
            self.index += 1
            instance_count = 0;
            try:
                data.read(stream)
                
                for root in data.roots:
                    self.n_roots.append(root)
                    for block in root.tree():
                        self.n_blocks.append(block)
                        if isinstance(block, self.instance):
                            if stream.name.replace("\\","/") in self.files:
                                self.instance_count += 1
                                self.instance_cname = stream.name.replace("\\","/")
                            if self.property != None:
                                if getattr(block, self.property):
                                    print("%s found in %s" % (self.property, stream.name.replace("\\","/")))
                                    self.files.append(stream.name.replace("\\","/"))
                                else:
                                    print("Warning: No property %s found in %s" % (self.property, stream.name.replace("\\","/")))
                            else:  
                                print("Found instance in %s" % stream.name.replace("\\","/"))
                                self.files.append(stream.name.replace("\\","/"))
                self.fIndex += 1
            except Exception:
                print("Warning: read failed due to corrupt file",
                      ", corrupt format description or bug")

        for file in self.files:
            shutil.copy(file, self.result_path)
        if self.instance_count > 1:
            print("%s instance blocks in %s" % (self.instance_count, self.instance_cname))
        print("Counted %s objects in %ss" % (self.index, time.time()-oldtime))
        print("Output folder %s" % self.result_path)
Example #34
0
 def branchentry(self, branch):
     """Optimize a vertex based collision block:
       - remove duplicate vertices
       - rebuild triangle indice and welding info
       - update MOPP data if applicable.
     """
     if branch in self.optimized:
         # already optimized
         return False
     
     # TODO: other collision geometry types
     if (isinstance(branch, NifFormat.bhkMoppBvTreeShape)
         and isinstance(branch.shape, NifFormat.bhkPackedNiTriStripsShape)
         and isinstance(branch.shape.data,
                        NifFormat.hkPackedNiTriStripsData)):
         if branch.shape.data.num_vertices < 3:
             self.toaster.msg(_("less than 3 vertices: removing branch"))
             self.data.replace_global_node(branch, None)
             self.changed = True
             return False                
         self.optimize_mopp(branch)
         # we found a geometry to optimize
         self.optimized.append(branch)
         # we're going to change the data
         self.changed = True
         return False # don't recurese farther
     elif (isinstance(branch, NifFormat.bhkRigidBody)
           and isinstance(branch.shape, NifFormat.bhkNiTriStripsShape)):
         # convert to a packed shape
         new_shape = branch.shape.get_interchangeable_packed_shape()
         if new_shape.data.num_vertices < 3:
             self.data.replace_global_node(branch, None)
             self.toaster.msg(_("less than 3 vertices: removing branch"))
             self.optimized.append(branch)
         else:
             self.data.replace_global_node(branch.shape, new_shape)
             self.toaster.msg(_("collision packed"))
             # call branchentry again in order to create a mopp for it
             self.branchentry(branch)
         self.changed = True
         # don't recurse further
         return False
     elif (isinstance(branch, NifFormat.bhkRigidBody)
           and isinstance(branch.shape,
                          NifFormat.bhkPackedNiTriStripsShape)):
         # packed shape without mopp: add a mopp to it if it is static
         if any(sub_shape.layer != 1
                for sub_shape in branch.shape.sub_shapes):
             # no mopps for non-static objects
             return False
         mopp = NifFormat.bhkMoppBvTreeShape()
         shape = branch.shape # store reference before replacing
         self.data.replace_global_node(branch.shape, mopp)
         mopp.shape = shape
         mopp.material = shape.sub_shapes[0].material
         mopp.unknown_8_bytes[0] = 160
         mopp.unknown_8_bytes[1] = 13
         mopp.unknown_8_bytes[2] = 75
         mopp.unknown_8_bytes[3] = 1
         mopp.unknown_8_bytes[4] = 192
         mopp.unknown_8_bytes[5] = 207
         mopp.unknown_8_bytes[6] = 144
         mopp.unknown_8_bytes[7] = 11
         mopp.unknown_float = 1.0
         mopp.update_mopp_welding()
         self.toaster.msg(_("added mopp"))
         self.changed = True
         self.optimized.append(branch)
         return False
     #keep recursing
     return True