Example #1
0
class CrytekDaeExporter:
    def __init__(self, config):
        self._config = config
        self._doc = Document()
        self._m_exporter = export_materials.CrytekMaterialExporter(config)

    def export(self):
        self._prepare_for_export()

        root_element = self._doc.createElement('collada')
        root_element.setAttribute(
            "xmlns", "http://www.collada.org/2005/11/COLLADASchema")
        root_element.setAttribute("version", "1.4.1")
        self._doc.appendChild(root_element)
        self._create_file_header(root_element)

        if self._config.generate_materials:
            self._m_exporter.generate_materials()

        # Just here for future use:
        self._export_library_cameras(root_element)
        self._export_library_lights(root_element)
        ###

        self._export_library_images(root_element)
        self._export_library_effects(root_element)
        self._export_library_materials(root_element)
        self._export_library_geometries(root_element)

        utils.add_fakebones()
        try:
            self._export_library_controllers(root_element)
            self._export_library_animation_clips_and_animations(root_element)
            self._export_library_visual_scenes(root_element)
        except RuntimeError:
            pass
        finally:
            utils.remove_fakebones()

        self._export_scene(root_element)

        converter = RCInstance(self._config)
        converter.convert_dae(self._doc)

        write_scripts(self._config)

    def _prepare_for_export(self):
        utils.clean_file(self._config.export_selected_nodes)

        if self._config.apply_modifiers:
            utils.apply_modifiers(self._config.export_selected_nodes)

        if self._config.fix_weights:
            utils.fix_weights()

    def _create_file_header(self, parent_element):
        asset = self._doc.createElement('asset')
        parent_element.appendChild(asset)
        contributor = self._doc.createElement('contributor')
        asset.appendChild(contributor)
        author = self._doc.createElement('author')
        contributor.appendChild(author)
        author_name = self._doc.createTextNode('Blender User')
        author.appendChild(author_name)
        author_tool = self._doc.createElement('authoring_tool')
        author_name_text = self._doc.createTextNode('BCry v{}'.format(
            self._config.bcry_version))
        author_tool.appendChild(author_name_text)
        contributor.appendChild(author_tool)
        created = self._doc.createElement('created')
        created_value = self._doc.createTextNode(datetime.now().isoformat(' '))
        created.appendChild(created_value)
        asset.appendChild(created)
        modified = self._doc.createElement('modified')
        asset.appendChild(modified)
        unit = self._doc.createElement('unit')
        unit.setAttribute('name', 'meter')
        unit.setAttribute('meter', '1')
        asset.appendChild(unit)
        up_axis = self._doc.createElement('up_axis')
        z_up = self._doc.createTextNode('Z_UP')
        up_axis.appendChild(z_up)
        asset.appendChild(up_axis)

#------------------------------------------------------------------
# Library Cameras:
#------------------------------------------------------------------

    def _export_library_cameras(self, root_element):
        library_cameras = self._doc.createElement('library_cameras')
        root_element.appendChild(library_cameras)

#------------------------------------------------------------------
# Library Lights:
#------------------------------------------------------------------

    def _export_library_lights(self, root_element):
        library_lights = self._doc.createElement('library_lights')
        root_element.appendChild(library_lights)

#------------------------------------------------------------------
# Library Images:
#------------------------------------------------------------------

    def _export_library_images(self, parent_element):
        library_images = self._doc.createElement('library_images')
        self._m_exporter.export_library_images(library_images)
        parent_element.appendChild(library_images)

#--------------------------------------------------------------
# Library Effects:
#--------------------------------------------------------------

    def _export_library_effects(self, parent_element):
        library_effects = self._doc.createElement('library_effects')
        self._m_exporter.export_library_effects(library_effects)
        parent_element.appendChild(library_effects)

#------------------------------------------------------------------
# Library Materials:
#------------------------------------------------------------------

    def _export_library_materials(self, parent_element):
        library_materials = self._doc.createElement('library_materials')
        self._m_exporter.export_library_materials(library_materials)
        parent_element.appendChild(library_materials)

#------------------------------------------------------------------
# Library Geometries:
#------------------------------------------------------------------

    def _export_library_geometries(self, parent_element):
        libgeo = self._doc.createElement("library_geometries")
        parent_element.appendChild(libgeo)
        for group in utils.get_mesh_export_nodes(
                self._config.export_selected_nodes):
            for object_ in group.objects:
                if object_.type != 'MESH':
                    continue

                bmesh_, layer_state, scene_layer = utils.get_bmesh(object_)
                geometry_node = self._doc.createElement("geometry")
                geometry_name = utils.get_geometry_name(group, object_)
                geometry_node.setAttribute("id", geometry_name)
                mesh_node = self._doc.createElement("mesh")

                print()
                bcPrint('"{}" object is being processed...'.format(
                    object_.name))

                start_time = clock()
                self._write_positions(bmesh_, mesh_node, geometry_name)
                bcPrint('Positions have been writed {:.4f} seconds.'.format(
                    clock() - start_time))

                start_time = clock()
                self._write_normals(object_, bmesh_, mesh_node, geometry_name)
                bcPrint('Normals have been writed {:.4f} seconds.'.format(
                    clock() - start_time))

                start_time = clock()
                self._write_uvs(object_, bmesh_, mesh_node, geometry_name)
                bcPrint(
                    'UVs have been writed {:.4f} seconds.'.format(clock() -
                                                                  start_time))

                start_time = clock()
                self._write_vertex_colors(object_, bmesh_, mesh_node,
                                          geometry_name)
                bcPrint(
                    'Vertex colors have been writed {:.4f} seconds.'.format(
                        clock() - start_time))

                start_time = clock()
                self._write_vertices(mesh_node, geometry_name)
                bcPrint('Vertices have been writed {:.4f} seconds.'.format(
                    clock() - start_time))

                start_time = clock()
                self._write_triangle_list(object_, bmesh_, mesh_node,
                                          geometry_name)
                bcPrint(
                    'Triangle list have been writed {:.4f} seconds.'.format(
                        clock() - start_time))

                extra = self._create_double_sided_extra("MAYA")
                mesh_node.appendChild(extra)
                geometry_node.appendChild(mesh_node)
                libgeo.appendChild(geometry_node)

                utils.clear_bmesh(object_, layer_state, scene_layer)
                bcPrint('"{}" object has been processed for "{}" node.'.format(
                    object_.name, group.name))

    def _write_positions(self, bmesh_, mesh_node, geometry_name):
        float_positions = []
        for vertex in bmesh_.verts:
            float_positions.extend(vertex.co)

        id_ = "{!s}-pos".format(geometry_name)
        source = utils.write_source(id_, "float", float_positions, "XYZ")
        mesh_node.appendChild(source)

    def _write_normals(self, object_, bmesh_, mesh_node, geometry_name):
        split_angle = 0
        use_edge_angle = False
        use_edge_sharp = False

        if object_.data.use_auto_smooth:
            use_edge_angle = True
            use_edge_sharp = True
            split_angle = object_.data.auto_smooth_angle
        else:
            for modifier in object_.modifiers:
                if modifier.type == 'EDGE_SPLIT' and modifier.show_viewport:
                    use_edge_angle = modifier.use_edge_angle
                    use_edge_sharp = modifier.use_edge_sharp
                    split_angle = modifier.split_angle

        float_normals = None
        if self._config.custom_normals:
            float_normals = utils.get_custom_normals(bmesh_, use_edge_angle,
                                                     split_angle)
        else:
            float_normals = utils.get_normal_array(bmesh_, use_edge_angle,
                                                   use_edge_sharp, split_angle)

        id_ = "{!s}-normal".format(geometry_name)
        source = utils.write_source(id_, "float", float_normals, "XYZ")
        mesh_node.appendChild(source)

    def _write_uvs(self, object_, bmesh_, mesh_node, geometry_name):
        uv_layer = bmesh_.loops.layers.uv.active
        if object_.data.uv_layers.active is None:
            bcPrint(
                "{} object has no a UV map, creating a default UV...".format(
                    object_.name))
            uv_layer = bmesh_.loops.layers.uv.new()

        float_uvs = []

        for face in bmesh_.faces:
            for loop in face.loops:
                float_uvs.extend(loop[uv_layer].uv)

        id_ = "{!s}-uvs".format(geometry_name)
        source = utils.write_source(id_, "float", float_uvs, "ST")
        mesh_node.appendChild(source)

    def _write_vertex_colors(self, object_, bmesh_, mesh_node, geometry_name):
        float_colors = []
        alpha_found = False

        color_layer = bmesh_.loops.layers.color.active
        if object_.data.vertex_colors:
            for vert in bmesh_.verts:
                loop = vert.link_loops[0]
                float_colors.extend(loop[color_layer])

        if float_colors:
            id_ = "{!s}-vcol".format(geometry_name)
            params = ("RGBA" if alpha_found else "RGB")
            source = utils.write_source(id_, "float", float_colors, params)
            mesh_node.appendChild(source)

    def _write_vertices(self, mesh_node, geometry_name):
        vertices = self._doc.createElement("vertices")
        vertices.setAttribute("id", "{}-vtx".format(geometry_name))
        input = utils.write_input(geometry_name, None, "pos", "POSITION")
        vertices.appendChild(input)
        mesh_node.appendChild(vertices)

    def _write_triangle_list(self, object_, bmesh_, mesh_node, geometry_name):
        tessfaces = utils.get_tessfaces(bmesh_)
        current_material_index = 0
        for material, materialname in self._m_exporter.get_materials_for_object(
                object_).items():
            triangles = ''
            triangle_count = 0
            normal_uv_index = 0
            for face in bmesh_.faces:
                norm_uv_indices = {}

                for index in range(0, len(face.verts)):
                    norm_uv_indices[str(
                        face.verts[index].index)] = normal_uv_index + index

                if face.material_index == current_material_index:
                    for tessface in tessfaces[face.index]:
                        triangle_count += 1
                        for vert in tessface:
                            normal_uv = norm_uv_indices[str(vert)]
                            dae_vertex = self._write_vertex_data(
                                vert, normal_uv, normal_uv,
                                object_.data.vertex_colors)
                            triangles = join(triangles, dae_vertex)

                normal_uv_index += len(face.verts)

            current_material_index += 1

            if triangle_count == 0:
                continue

            triangle_list = self._doc.createElement('triangles')
            triangle_list.setAttribute('material', materialname)
            triangle_list.setAttribute('count', str(triangle_count))

            inputs = []
            inputs.append(utils.write_input(geometry_name, 0, 'vtx', 'VERTEX'))
            inputs.append(
                utils.write_input(geometry_name, 1, 'normal', 'NORMAL'))
            inputs.append(
                utils.write_input(geometry_name, 2, 'uvs', 'TEXCOORD'))
            if object_.data.vertex_colors:
                inputs.append(
                    utils.write_input(geometry_name, 3, 'vcol', 'COLOR'))

            for input in inputs:
                triangle_list.appendChild(input)

            p = self._doc.createElement('p')
            p_text = self._doc.createTextNode(triangles)
            p.appendChild(p_text)

            triangle_list.appendChild(p)
            mesh_node.appendChild(triangle_list)

    def _write_vertex_data(self, vert, normal, uv, vertex_colors):
        if vertex_colors:
            return "{:d} {:d} {:d} {:d} ".format(vert, normal, uv, vert)
        else:
            return "{:d} {:d} {:d} ".format(vert, normal, uv)

    def _create_double_sided_extra(self, profile):
        extra = self._doc.createElement("extra")
        technique = self._doc.createElement("technique")
        technique.setAttribute("profile", profile)
        double_sided = self._doc.createElement("double_sided")
        double_sided_value = self._doc.createTextNode("1")
        double_sided.appendChild(double_sided_value)
        technique.appendChild(double_sided)
        extra.appendChild(technique)

        return extra

# -------------------------------------------------------------------------
# Library Controllers: --> Skeleton Armature and List of Bone Names
#                      --> Skin Geometry, Weights, Transform Matrices
# -------------------------------------------------------------------------

    def _export_library_controllers(self, parent_element):
        library_node = self._doc.createElement("library_controllers")

        ALLOWED_NODE_TYPES = ('chr', 'skin')
        for group in utils.get_mesh_export_nodes(
                self._config.export_selected_nodes):
            node_type = utils.get_node_type(group)
            if node_type in ALLOWED_NODE_TYPES:
                for object_ in group.objects:
                    if not utils.is_bone_geometry(object_):
                        armature = utils.get_armature_for_object(object_)
                        if armature is not None:
                            self._process_bones(library_node, group, object_,
                                                armature)

        parent_element.appendChild(library_node)

    def _process_bones(self, parent_node, group, object_, armature):
        id_ = "{!s}_{!s}".format(armature.name, object_.name)

        controller_node = self._doc.createElement("controller")
        parent_node.appendChild(controller_node)
        controller_node.setAttribute("id", id_)

        skin_node = self._doc.createElement("skin")
        skin_node.setAttribute(
            "source", "#{!s}".format(utils.get_geometry_name(group, object_)))
        controller_node.appendChild(skin_node)

        bind_shape_matrix = self._doc.createElement("bind_shape_matrix")
        utils.write_matrix(Matrix(), bind_shape_matrix)
        skin_node.appendChild(bind_shape_matrix)

        self._process_bone_joints(object_, armature, skin_node, group)
        self._process_bone_matrices(object_, armature, skin_node)
        self._process_bone_weights(object_, armature, skin_node)

        joints = self._doc.createElement("joints")
        input = utils.write_input(id_, None, "joints", "JOINT")
        joints.appendChild(input)
        input = utils.write_input(id_, None, "matrices", "INV_BIND_MATRIX")
        joints.appendChild(input)
        skin_node.appendChild(joints)

    def _process_bone_joints(self, object_, armature, skin_node, group):

        bones = utils.get_bones(armature)
        id_ = "{!s}_{!s}-joints".format(armature.name, object_.name)
        bone_names = []
        for bone in bones:
            props_name = self._create_properties_name(bone, group)
            bone_name = "{!s}{!s}".format(bone.name, props_name)
            bone_names.append(bone_name)
        source = utils.write_source(id_, "IDREF", bone_names, [])
        skin_node.appendChild(source)

    def _process_bone_matrices(self, object_, armature, skin_node):

        bones = utils.get_bones(armature)
        bone_matrices = []
        for bone in armature.pose.bones:

            bone_matrix = utils.transform_bone_matrix(bone)
            bone_matrices.extend(utils.matrix_to_array(bone_matrix))

        id_ = "{!s}_{!s}-matrices".format(armature.name, object_.name)
        source = utils.write_source(id_, "float4x4", bone_matrices, [])
        skin_node.appendChild(source)

    def _process_bone_weights(self, object_, armature, skin_node):

        bones = utils.get_bones(armature)
        group_weights = []
        vw = ""
        vertex_groups_lengths = ""
        vertex_count = 0
        bone_list = {}

        for bone_id, bone in enumerate(bones):
            bone_list[bone.name] = bone_id

        for vertex in object_.data.vertices:
            vertex_group_count = 0
            for group in vertex.groups:
                group_name = object_.vertex_groups[group.group].name
                if (group.weight == 0 or group_name not in bone_list):
                    continue
                if vertex_group_count == 8:
                    bcPrint("Too many bone references in {}:{} vertex group".
                            format(object_.name, group_name))
                    continue
                group_weights.append(group.weight)
                vw = "{}{} {} ".format(vw, bone_list[group_name], vertex_count)
                vertex_count += 1
                vertex_group_count += 1

            vertex_groups_lengths = "{}{} ".format(vertex_groups_lengths,
                                                   vertex_group_count)

        id_ = "{!s}_{!s}-weights".format(armature.name, object_.name)
        source = utils.write_source(id_, "float", group_weights, [])
        skin_node.appendChild(source)

        vertex_weights = self._doc.createElement("vertex_weights")
        vertex_weights.setAttribute("count", str(len(object_.data.vertices)))

        id_ = "{!s}_{!s}".format(armature.name, object_.name)
        input = utils.write_input(id_, 0, "joints", "JOINT")
        vertex_weights.appendChild(input)
        input = utils.write_input(id_, 1, "weights", "WEIGHT")
        vertex_weights.appendChild(input)

        vcount = self._doc.createElement("vcount")
        vcount_text = self._doc.createTextNode(vertex_groups_lengths)
        vcount.appendChild(vcount_text)
        vertex_weights.appendChild(vcount)

        v = self._doc.createElement("v")
        v_text = self._doc.createTextNode(vw)
        v.appendChild(v_text)
        vertex_weights.appendChild(v)

        skin_node.appendChild(vertex_weights)

# -----------------------------------------------------------------------------
# Library Animation and Clips: --> Animations, F-Curves
# -----------------------------------------------------------------------------

    def _export_library_animation_clips_and_animations(self, parent_element):
        libanmcl = self._doc.createElement("library_animation_clips")
        libanm = self._doc.createElement("library_animations")
        parent_element.appendChild(libanmcl)
        parent_element.appendChild(libanm)


# ---------------------------------------------------------------------
# Library Visual Scene: --> Skeleton and _Phys bones, Bone
#       Transformations, and Instance URL (_boneGeometry) and extras.
# ---------------------------------------------------------------------

    def _export_library_visual_scenes(self, parent_element):
        current_element = self._doc.createElement("library_visual_scenes")
        visual_scene = self._doc.createElement("visual_scene")
        visual_scene.setAttribute("id", "scene")
        visual_scene.setAttribute("name", "scene")
        current_element.appendChild(visual_scene)
        parent_element.appendChild(current_element)

        if utils.get_mesh_export_nodes(self._config.export_selected_nodes):
            if utils.are_duplicate_nodes():
                message = "Duplicate Node Names"
                bpy.ops.screen.display_error('INVOKE_DEFAULT', message=message)

            for group in utils.get_mesh_export_nodes(
                    self._config.export_selected_nodes):
                self._write_export_node(group, visual_scene)
        else:
            pass  # TODO: Handle No Export Nodes Error

    def _write_export_node(self, group, visual_scene):
        if not self._config.export_for_lumberyard:
            node_name = "CryExportNode_{}".format(utils.get_node_name(group))
            node = self._doc.createElement("node")
            node.setAttribute("id", node_name)
            node.setIdAttribute("id")
        else:
            node_name = "{}".format(utils.get_node_name(group))
            node = self._doc.createElement("node")
            node.setAttribute("id", node_name)
            node.setAttribute("LumberyardExportNode", "1")
            node.setIdAttribute("id")

        root_objects = []
        for object_ in group.objects:
            if utils.is_visual_scene_node_writed(object_, group):
                root_objects.append(object_)

        node = self._write_visual_scene_node(root_objects, node, group)

        extra = self._create_cryengine_extra(group)
        node.appendChild(extra)
        visual_scene.appendChild(node)

    def _write_visual_scene_node(self, objects, parent_node, group):
        for object_ in objects:
            if object_.type == "MESH" and not utils.is_fakebone(object_) \
                    and not utils.is_lod_geometry(object_):
                prop_name = object_.name
                node_type = utils.get_node_type(group)
                if node_type in ('chr', 'skin'):
                    prop_name = join(
                        object_.name,
                        self._create_properties_name(object_, group))
                node = self._doc.createElement("node")
                node.setAttribute("id", prop_name)
                node.setAttribute("name", prop_name)
                node.setIdAttribute("id")

                self._write_transforms(object_, node)

                ALLOWED_NODE_TYPES = ('cgf', 'cga', 'chr', 'skin')
                if node_type in ALLOWED_NODE_TYPES:
                    instance = self._create_instance(group, object_)
                    if instance is not None:
                        node.appendChild(instance)

                udp_extra = self._create_user_defined_property(object_)
                if udp_extra is not None:
                    node.appendChild(udp_extra)

                parent_node.appendChild(node)

                if utils.is_has_lod(object_):
                    sub_node = node
                    for lod in utils.get_lod_geometries(object_):
                        sub_node = self._write_lods(lod, sub_node, group)

                if node_type in ('chr', 'skin') and object_.parent \
                        and object_.parent.type == "ARMATURE":
                    armature = object_.parent
                    self._write_bone_list([utils.get_root_bone(armature)],
                                          object_, parent_node, group)

                    armature_physic = utils.get_armature_physic(armature)
                    if armature_physic:
                        self._write_bone_list(
                            [utils.get_root_bone(armature_physic)],
                            armature_physic, parent_node, group)

        return parent_node

    def _write_lods(self, object_, parent_node, group):
        #prop_name = object_.name
        prop_name = utils.changed_lod_name(object_.name)
        node_type = utils.get_node_type(group)
        if node_type in ('chr', 'ckin'):
            prop_name = join(object_.name,
                             self._create_properties_name(object_, group))
        node = self._doc.createElement("node")
        node.setAttribute("id", prop_name)
        node.setAttribute("name", prop_name)
        node.setIdAttribute("id")

        self._write_transforms(object_, node)

        ALLOWED_NODE_TYPES = ('cgf', 'cga', 'chr', 'skin')
        if utils.get_node_type(group) in ALLOWED_NODE_TYPES:
            instance = self._create_instance(group, object_)
            if instance is not None:
                node.appendChild(instance)

        udp_extra = self._create_user_defined_property(object_)
        if udp_extra is not None:
            node.appendChild(udp_extra)

        parent_node.appendChild(node)

        return node

    def _write_bone_list(self, bones, object_, parent_node, group):
        scene = bpy.context.scene
        bone_names = []

        for bone in bones:
            props_name = self._create_properties_name(bone, group)
            props_ik = self._create_ik_properties(bone, object_)
            bone_name = join(bone.name, props_name, props_ik)
            bone_names.append(bone_name)

            node = self._doc.createElement("node")
            node.setAttribute("id", bone_name)
            node.setAttribute("name", bone_name)
            node.setIdAttribute("id")

            fakebone = utils.get_fakebone(bone.name)
            if fakebone is not None:
                self._write_transforms(fakebone, node)

                bone_geometry = utils.get_bone_geometry(bone)
                if bone_geometry is not None:
                    geo_name = utils.get_geometry_name(group, bone_geometry)
                    instance = self._create_bone_instance(
                        bone_geometry, geo_name)
                    node.appendChild(instance)

                    extra = self._create_physic_proxy_for_bone(
                        object_.parent, bone)
                    if extra is not None:
                        node.appendChild(extra)

            elif utils.is_physic_bone(bone):
                bone_geometry = utils.get_bone_geometry(bone)
                if fakebone is not None:
                    self._write_transforms(fakebone, node)

            parent_node.appendChild(node)

            if bone.children:
                self._write_bone_list(bone.children, object_, node, group)

    def _create_bone_instance(self, bone_geometry, geometry_name):
        instance = None

        instance = self._doc.createElement("instance_geometry")
        instance.setAttribute("url", "#{}".format(geometry_name))
        bm = self._doc.createElement("bind_material")
        tc = self._doc.createElement("technique_common")

        for mat in bone_geometry.material_slots:
            im = self._doc.createElement("instance_material")
            im.setAttribute("symbol", mat.name)
            im.setAttribute("target", "#{}".format(mat.name))
            bvi = self._doc.createElement("bind_vertex_input")
            bvi.setAttribute("semantic", "UVMap")
            bvi.setAttribute("input_semantic", "TEXCOORD")
            bvi.setAttribute("input_set", "0")
            im.appendChild(bvi)
            tc.appendChild(im)

        bm.appendChild(tc)
        instance.appendChild(bm)

        return instance

    def _create_physic_proxy_for_bone(self, object_, bone):
        extra = None
        try:
            bonePhys = object_.pose.bones[bone.name]['phys_proxy']
            bcPrint(bone.name + " physic proxy is " + bonePhys)

            extra = self._doc.createElement("extra")
            techcry = self._doc.createElement("technique")
            techcry.setAttribute("profile", "CryEngine")
            prop2 = self._doc.createElement("properties")

            cryprops = self._doc.createTextNode(bonePhys)
            prop2.appendChild(cryprops)
            techcry.appendChild(prop2)
            extra.appendChild(techcry)
        except:
            pass

        return extra

    def _write_transforms(self, object_, node):
        trans = self._create_translation_node(object_)
        rotx, roty, rotz = self._create_rotation_node(object_)
        scale = self._create_scale_node(object_)

        node.appendChild(trans)
        node.appendChild(rotx)
        node.appendChild(roty)
        node.appendChild(rotz)
        node.appendChild(scale)

    def _create_translation_node(self, object_):
        trans = self._doc.createElement("translate")
        trans.setAttribute("sid", "translation")
        trans_text = self._doc.createTextNode(
            "{:f} {:f} {:f}".format(*object_.location))
        trans.appendChild(trans_text)

        return trans

    def _create_rotation_node(self, object_):
        rotz = self._write_rotation("z", "0 0 1 {:f}",
                                    object_.rotation_euler[2])
        roty = self._write_rotation("y", "0 1 0 {:f}",
                                    object_.rotation_euler[1])
        rotx = self._write_rotation("x", "1 0 0 {:f}",
                                    object_.rotation_euler[0])

        return rotz, roty, rotx

    def _write_rotation(self, axis, textFormat, rotation):
        rot = self._doc.createElement("rotate")
        rot.setAttribute("sid", "rotation_{}".format(axis))
        rot_text = self._doc.createTextNode(
            textFormat.format(rotation * utils.to_degrees))
        rot.appendChild(rot_text)

        return rot

    def _create_scale_node(self, object_):
        scale = self._doc.createElement("scale")
        scale.setAttribute("sid", "scale")
        scale_text = self._doc.createTextNode(
            utils.floats_to_string(object_.scale, " ", "%s"))
        scale.appendChild(scale_text)

        return scale

    def _create_instance(self, group, object_):
        armature = utils.get_armature_for_object(object_)
        node_type = utils.get_node_type(group)
        instance = None
        if armature and node_type in ('chr', 'skin'):
            instance = self._doc.createElement("instance_controller")
            # This binds the mesh object to the armature in control of it
            instance.setAttribute(
                "url", "#{!s}_{!s}".format(armature.name, object_.name))
        elif object_.name[:6] != "_joint" and object_.type == "MESH":
            instance = self._doc.createElement("instance_geometry")
            instance.setAttribute(
                "url", "#{!s}".format(utils.get_geometry_name(group, object_)))

        if instance is not None:
            bind_material = self._create_bind_material(object_)
            instance.appendChild(bind_material)
            return instance

    def _create_bind_material(self, object_):
        bind_material = self._doc.createElement('bind_material')
        technique_common = self._doc.createElement('technique_common')

        for material, materialname in self._m_exporter.get_materials_for_object(
                object_).items():
            instance_material = self._doc.createElement('instance_material')
            instance_material.setAttribute('symbol', materialname)
            instance_material.setAttribute('target',
                                           '#{!s}'.format(materialname))

            technique_common.appendChild(instance_material)

        bind_material.appendChild(technique_common)

        return bind_material

    def _create_cryengine_extra(self, node):
        extra = self._doc.createElement("extra")
        technique = self._doc.createElement("technique")
        technique.setAttribute("profile", "CryEngine")
        properties = self._doc.createElement("properties")

        ALLOWED_NODE_TYPES = ("cgf", "cga", "chr", "skin")

        if utils.is_export_node(node):
            node_type = utils.get_node_type(node)
            if node_type in ALLOWED_NODE_TYPES:
                prop = self._doc.createTextNode(
                    "fileType={}".format(node_type))
                properties.appendChild(prop)
            if not self._config.merge_all_nodes:
                prop = self._doc.createTextNode("DoNotMerge")
                properties.appendChild(prop)

            prop = self._doc.createTextNode("UseCustomNormals")
            properties.appendChild(prop)

            if self._config.vcloth_pre_process and node_type == 'skin':
                prop = self._doc.createTextNode("VClothPreProcess")
                properties.appendChild(prop)

            prop = self._doc.createTextNode("CustomExportPath=")
            properties.appendChild(prop)
        else:
            if not node.rna_type.id_data.items():
                return

        technique.appendChild(properties)

        if (node.name[:6] == "_joint"):
            helper = self._create_helper_joint(node)
            technique.appendChild(helper)

        extra.appendChild(technique)
        extra.appendChild(self._create_xsi_profile(node))

        return extra

    def _create_xsi_profile(self, node):
        technique_xsi = self._doc.createElement("technique")
        technique_xsi.setAttribute("profile", "XSI")

        xsi_custom_p_set = self._doc.createElement("XSI_CustomPSet")
        xsi_custom_p_set.setAttribute("name", "ExportProperties")

        propagation = self._doc.createElement("propagation")
        propagation.appendChild(self._doc.createTextNode("NODE"))
        xsi_custom_p_set.appendChild(propagation)

        type_node = self._doc.createElement("type")
        type_node.appendChild(
            self._doc.createTextNode("CryExportNodeProperties"))
        xsi_custom_p_set.appendChild(type_node)

        xsi_parameter = self._doc.createElement("XSI_Parameter")
        xsi_parameter.setAttribute("id", "FileType")
        xsi_parameter.setAttribute("type", "Integer")
        xsi_parameter.setAttribute("value", utils.get_xsi_filetype_value(node))
        xsi_custom_p_set.appendChild(xsi_parameter)

        xsi_parameter = self._doc.createElement("XSI_Parameter")
        xsi_parameter.setAttribute("id", "Filename")
        xsi_parameter.setAttribute("type", "Text")
        xsi_parameter.setAttribute("value", utils.get_node_name(node))
        xsi_custom_p_set.appendChild(xsi_parameter)

        xsi_parameter = self._doc.createElement("XSI_Parameter")
        xsi_parameter.setAttribute("id", "Exportable")
        xsi_parameter.setAttribute("type", "Boolean")
        xsi_parameter.setAttribute("value", "1")
        xsi_custom_p_set.appendChild(xsi_parameter)

        xsi_parameter = self._doc.createElement("XSI_Parameter")
        xsi_parameter.setAttribute("id", "MergeObjects")
        xsi_parameter.setAttribute("type", "Boolean")
        xsi_parameter.setAttribute("value",
                                   str(int(self._config.merge_all_nodes)))
        xsi_custom_p_set.appendChild(xsi_parameter)

        technique_xsi.appendChild(xsi_custom_p_set)

        return technique_xsi

    def _create_user_defined_property(self, object_):
        udp_buffer = None
        for prop in object_.rna_type.id_data.items():
            if prop:
                prop_name = prop[0]
                if udp.is_user_defined_property(prop_name):
                    udp_buffer = "\n"

                    if isinstance(prop[1], str):
                        udp_buffer += "{!s}\n".format(prop[1])
                    else:
                        udp_buffer += "{!s}={!s}\n".format(prop[0], prop[1])

        if udp_buffer:
            extra = self._doc.createElement("extra")
            technique = self._doc.createElemen("technique")
            technique.setAttribute("profile", "CryEngine")
            properties = self._doc.createElement("properties")
            properties._doc.createTextNode(udp_buffer)
            technique.appendChild(properties)
            extra.appendChild(technique)

            return extra
        else:
            return None

    def _create_helper_joint(self, object_):
        x1, y1, z1, x2, y2, z2 = utils.get_bounding_box(object_)

        min = self._doc.createElement("bound_box_min")
        min_text = self._doc.createTextNode("{:f} {:f} {:f}".format(
            x1, y1, z1))
        min.appendChild(min_text)

        max = self._doc.createElement("bound_box_max")
        max_text = self._doc.createTextNode("{:f} {:f} {:f}".format(
            x2, y2, z2))
        max.appendChild(max_text)

        joint = self._doc.createElement("helper")
        joint.setAttribute("type", "dummy")
        joint.appendChild(min)
        joint.appendChild(max)

        return joint

    def _create_properties_name(self, bone, group):
        bone_name = bone.name.replace("__", "*")
        node_name = utils.get_node_name(group)
        props_name = '%{!s}%--PRprops_name={!s}'.format(node_name, bone_name)

        return props_name

    def _create_ik_properties(self, bone, object_):
        props = ""
        if utils.is_physic_bone(bone):

            armature_object = bpy.data.objects[object_.name[:-5]]
            pose_bone = armature_object.pose.bones[bone.name[:-5]]

            xIK, yIK, zIK = udp.get_bone_ik_max_min(pose_bone)

            damping, spring, spring_tension = udp.get_bone_ik_properties(
                pose_bone)

            props = join(xIK, '_xdamping={}'.format(damping[1]),
                         '_xspringangle={}'.format(spring[1]),
                         '_xspringtension={}'.format(spring_tension[1]), yIK,
                         '_ydamping={}'.format(damping[0]),
                         '_yspringangle={}'.format(spring[0]),
                         '_yspringtension={}'.format(spring_tension[0]), zIK,
                         '_zdamping={}'.format(damping[2]),
                         '_zspringangle={}'.format(spring[2]),
                         '_zspringtension={}'.format(spring_tension[2]))

        return props

    def _export_scene(self, parent_element):
        scene = self._doc.createElement("scene")
        instance_visual_scene = self._doc.createElement(
            "instance_visual_scene")
        instance_visual_scene.setAttribute("url", "#scene")
        scene.appendChild(instance_visual_scene)
        parent_element.appendChild(scene)