예제 #1
0
    def generate_texture_unit(self, key, texture):
        """
        Generates a texture_unit of a pass.
        
        key: key of the texture in the material shader (not used, for normal if needed)
        texture: the material texture
        """
        src_dir = os.path.dirname(bpy.data.filepath)
        # For target path relative
        # dst_dir = os.path.dirname(self.target_path)
        dst_dir = src_dir
        filename = io_utils.path_reference(texture.image.filepath, src_dir, dst_dir, mode='RELATIVE', library=texture.image.library)
        # Do not use if target path relative
        # filename = repr(filepath)[1:-1]
        _, filename = split(filename)
        filename = self.change_ext(filename, texture.image)

        # special case of normal maps
        if key == "normalmap_texture":
            with self.w.iword('rtshader_system').embed():
                self.w.iword('lighting_stage normal_map').word(filename).nl()
                return

        with self.w.iword('texture_unit').embed():
            self.w.iword('texture').word(filename).nl()
            
            exmode = texture.extension
            if exmode in TEXTURE_ADDRESS_MODE:
                self.w.iword('tex_address_mode').word(TEXTURE_ADDRESS_MODE[exmode]).nl()

            if exmode == 'CLIP':
                r,g,b = texture.node_image.color_mapping.blend_color
                self.w.iword('tex_border_colour').round(r).round(g).round(b).nl()
            x,y = texture.scale[0:2]
            if x != 1 or y != 1:
                self.w.iword('scale').round(1.0 / x).round(1.0 / y).nl()
            
            if texture.texcoords == 'Reflection':
                if texture.projection == 'SPHERE':
                    self.w.iline('env_map spherical')
                elif texture.projection == 'FLAT':
                    self.w.iline('env_map planar')
                else: 
                    logger.warn('Texture: <%s> has a non-UV mapping type (%s) and not picked a proper projection type of: Sphere or Flat' % (texture.name, slot.mapping))
            
            x,y = texture.translation[0:2]
            if x or y:
                self.w.iword('scroll').round(x).round(y).nl()
            if texture.rotation.z:
                # Radians to degrees
                self.w.iword('rotate').round(math.degrees(texture.rotation.z), 2).nl()
 
            btype = 'modulate'
            if key == "emission_color_texture":
                btype = "add"
            
            self.w.iword('colour_op').word(btype).nl()
예제 #2
0
    def add_collada_image(self, img):

        #--- Add image to collada.images
        if img.name not in self.collada.images:

            #--- Copy image to a subdir in the export dir
            if self.copy_images:

                # Image source path and basename
                img_sourcepath = bpy.path.abspath(img.filepath)
                img_basename = bpy.path.basename(img_sourcepath)

                # Copy image to subdir in export dir
                export_filename = bpy.path.display_name_from_filepath(
                    self.export_path)
                subdir = export_filename + "_textures"

                img_destpath = os.path.join(self.export_dir, subdir,
                                            img_basename)
                copy_set = {(img_sourcepath, img_destpath)}
                io_utils.path_reference_copy(copy_set)

                # Relative path to the copied image
                path = io_utils.path_reference(img_destpath, self.source_dir,
                                               self.export_dir, 'RELATIVE')

            #--- Reference image where it is
            else:
                if self.use_relative_path:
                    path_mode = 'RELATIVE'
                else:
                    path_mode = 'ABSOLUTE'
                path = io_utils.path_reference(img.filepath, self.source_dir,
                                               self.export_dir, path_mode)

            #--- Create and add CImage object
            c_img = material.CImage(img.name, path)
            self.collada.images.append(c_img)

        #--- Image already added to collada.images
        else:
            c_img = self.collada.images[img.name]

        return c_img
 def add_collada_image(self, img):
     
     #--- Add image to collada.images
     if img.name not in self.collada.images:
         
         #--- Copy image to a subdir in the export dir
         if self.copy_images:
             
             # Image source path and basename
             img_sourcepath = bpy.path.abspath(img.filepath)
             img_basename = bpy.path.basename(img_sourcepath)
             
             # Copy image to subdir in export dir
             export_filename = bpy.path.display_name_from_filepath(self.export_path)
             #subdir = export_filename + "_textures"
             subdir = "textures"
             
             img_destpath = os.path.join(self.export_dir, subdir, img_basename)
             copy_set = {(img_sourcepath, img_destpath)}
             io_utils.path_reference_copy(copy_set)
             
             # Relative path to the copied image
             path = io_utils.path_reference(img_destpath, self.source_dir, self.export_dir, 'RELATIVE')
             
         #--- Reference image where it is
         else:
             if self.use_relative_path:
                 path_mode = 'RELATIVE'
             else:   
                 path_mode = 'ABSOLUTE'
             path = io_utils.path_reference(img.filepath, self.source_dir, self.export_dir, path_mode)
         
         #--- Create and add CImage object
         c_img = material.CImage(img.name, path)
         self.collada.images.append(c_img)
         
     #--- Image already added to collada.images
     else:
         c_img = self.collada.images[img.name]
     
     return c_img
예제 #4
0
def write_mtl(scene, filepath, path_mode, copy_set, mtl_dict):
    world = scene.world
    world_amb = Color((0.8, 0.8, 0.8))

    source_dir = os.path.dirname(bpy.data.filepath)
    dest_dir = os.path.dirname(filepath)

    with open(filepath, "w", encoding="utf8", newline="\n") as f:
        fw = f.write

        fw('# Blender MTL File: %r\n' % (os.path.basename(bpy.data.filepath) or "None"))
        fw('# Material Count: %i\n' % len(mtl_dict))

        mtl_dict_values = list(mtl_dict.values())
        mtl_dict_values.sort(key=lambda m: m[0])

        # Write material/image combinations we have used.
        # Using mtl_dict.values() directly gives un-predictable order.
        for mtl_mat_name, mat in mtl_dict_values:
            # Get the Blender data for the material and the image.
            # Having an image named None will make a bug, dont do it :)

            fw('\nnewmtl %s\n' % mtl_mat_name)  # Define a new material: matname_imgname

            mat_wrap = node_shader_utils.PrincipledBSDFWrapper(mat) if mat else None

            if mat_wrap:
                use_mirror = mat_wrap.metallic != 0.0
                use_transparency = mat_wrap.alpha != 1.0

                # XXX Totally empirical conversion, trying to adapt it
                #     (from 1.0 - 0.0 Principled BSDF range to 0.0 - 900.0 OBJ specular exponent range)...
                spec = (1.0 - mat_wrap.roughness) * 30
                spec *= spec
                fw('Ns %.6f\n' % spec)

                # Ambient
                if use_mirror:
                    fw('Ka %.6f %.6f %.6f\n' % (mat_wrap.metallic, mat_wrap.metallic, mat_wrap.metallic))
                else:
                    fw('Ka %.6f %.6f %.6f\n' % (1.0, 1.0, 1.0))
                fw('Kd %.6f %.6f %.6f\n' % mat_wrap.base_color[:3])  # Diffuse
                # XXX TODO Find a way to handle tint and diffuse color, in a consistent way with import...
                fw('Ks %.6f %.6f %.6f\n' % (mat_wrap.specular, mat_wrap.specular, mat_wrap.specular))  # Specular
                # Emission, not in original MTL standard but seems pretty common, see T45766.
                fw('Ke %.6f %.6f %.6f\n' % mat_wrap.emission_color[:3])
                fw('Ni %.6f\n' % mat_wrap.ior)  # Refraction index
                fw('d %.6f\n' % mat_wrap.alpha)  # Alpha (obj uses 'd' for dissolve)

                # See http://en.wikipedia.org/wiki/Wavefront_.obj_file for whole list of values...
                # Note that mapping is rather fuzzy sometimes, trying to do our best here.
                if mat_wrap.specular == 0:
                    fw('illum 1\n')  # no specular.
                elif use_mirror:
                    if use_transparency:
                        fw('illum 6\n')  # Reflection, Transparency, Ray trace
                    else:
                        fw('illum 3\n')  # Reflection and Ray trace
                elif use_transparency:
                    fw('illum 9\n')  # 'Glass' transparency and no Ray trace reflection... fuzzy matching, but...
                else:
                    fw('illum 2\n')  # light normally

                #### And now, the image textures...
                image_map = {
                        "map_Kd": "base_color_texture",
                        "map_Ka": None,  # ambient...
                        "map_Ks": "specular_texture",
                        "map_Ns": "roughness_texture",
                        "map_d": "alpha_texture",
                        "map_Tr": None,  # transmission roughness?
                        "map_Bump": "normalmap_texture",
                        "disp": None,  # displacement...
                        "refl": "metallic_texture",
                        "map_Ke": "emission_color_texture",
                        }

                for key, mat_wrap_key in sorted(image_map.items()):
                    if mat_wrap_key is None:
                        continue
                    tex_wrap = getattr(mat_wrap, mat_wrap_key, None)
                    if tex_wrap is None:
                        continue
                    image = tex_wrap.image
                    if image is None:
                        continue

                    filepath = io_utils.path_reference(image.filepath, source_dir, dest_dir,
                                                       path_mode, "", copy_set, image.library)
                    options = []
                    if key == "map_Bump":
                        if mat_wrap.normalmap_strength != 1.0:
                            options.append('-bm %.6f' % mat_wrap.normalmap_strength)
                    if tex_wrap.translation != Vector((0.0, 0.0, 0.0)):
                        options.append('-o %.6f %.6f %.6f' % tex_wrap.translation[:])
                    if tex_wrap.scale != Vector((1.0, 1.0, 1.0)):
                        options.append('-s %.6f %.6f %.6f' % tex_wrap.scale[:])
                    if options:
                        fw('%s %s %s\n' % (key, " ".join(options), repr(filepath)[1:-1]))
                    else:
                        fw('%s %s\n' % (key, repr(filepath)[1:-1]))

            else:
                # Write a dummy material here?
                fw('Ns 500\n')
                fw('Ka 0.8 0.8 0.8\n')
                fw('Kd 0.8 0.8 0.8\n')
                fw('Ks 0.8 0.8 0.8\n')
                fw('d 1\n')  # No alpha
                fw('illum 2\n')  # light normally
def export_bm(context, bmx_filepath, prefs_fncg, opts_exportMode, opts_exportTarget):
    # ============================================ alloc a temp folder
    utils_tempFolderObj = tempfile.TemporaryDirectory()
    utils_tempFolder = utils_tempFolderObj.name
    utils_tempTextureFolder = os.path.join(utils_tempFolder, "Texture")
    os.makedirs(utils_tempTextureFolder)
    
    # ============================================ 
    # find export target. 
    # do not need check them validation in there. 
    # just collect them.
    if opts_exportMode== "COLLECTION":
        objectList = opts_exportTarget.objects
    else:
        objectList = [opts_exportTarget, ]

    # try get fncg collection
    # fncg stands with forced non-component group
    try:
        object_fncgCollection = bpy.data.collections[prefs_fncg]
    except:
        object_fncgCollection = None
   
    # ============================================ export
    with open(os.path.join(utils_tempFolder, "index.bm"), "wb") as finfo:
        UTILS_file_io.write_uint32(finfo, UTILS_constants.bmfile_currentVersion)
        
        # ====================== export object
        meshSet = set()
        meshList = []
        meshCount = 0        
        with open(os.path.join(utils_tempFolder, "object.bm"), "wb") as fobject:
            for obj in objectList:
                # only export mesh object
                if obj.type != 'MESH':
                    continue

                # clean no mesh object
                object_blenderMesh = obj.data
                if object_blenderMesh is None:
                    continue

                # check component
                if (object_fncgCollection is not None) and (obj.name in object_fncgCollection.objects):
                    # it should be set as normal object forcely
                    object_isComponent = False
                else:
                    # check isComponent normally
                    object_isComponent = UTILS_functions.is_component(obj.name)

                # triangle first and then group
                if not object_isComponent:
                    if object_blenderMesh not in meshSet:
                        _mesh_triangulate(object_blenderMesh)
                        meshSet.add(object_blenderMesh)
                        meshList.append(object_blenderMesh)
                        object_meshIndex = meshCount
                        meshCount += 1
                    else:
                        object_meshIndex = meshList.index(object_blenderMesh)
                else:
                    object_meshIndex = UTILS_functions.get_component_id(obj.name)

                # get visibility
                object_isHidden = not obj.visible_get()

                # try get grouping data
                object_groupList = UTILS_virtools_prop.get_virtools_group_data(obj)

                # =======================
                # write to files
                # write finfo first
                UTILS_file_io.write_string(finfo, obj.name)
                UTILS_file_io.write_uint8(finfo, UTILS_constants.BmfileInfoType.OBJECT)
                UTILS_file_io.write_uint64(finfo, fobject.tell())

                # write fobject
                UTILS_file_io.write_bool(fobject, object_isComponent)
                UTILS_file_io.write_bool(fobject, object_isHidden)
                UTILS_file_io.write_world_matrix(fobject, obj.matrix_world)
                UTILS_file_io.write_uint32(fobject, len(object_groupList))
                for item in object_groupList:
                    UTILS_file_io.write_string(fobject, item)
                UTILS_file_io.write_uint32(fobject, object_meshIndex)

        # ====================== export mesh
        materialSet = set()
        materialList = []
        with open(os.path.join(utils_tempFolder, "mesh.bm"), "wb") as fmesh:
            for mesh in meshList:
                # split normals
                mesh.calc_normals_split()

                # write finfo first
                UTILS_file_io.write_string(finfo, mesh.name)
                UTILS_file_io.write_uint8(finfo, UTILS_constants.BmfileInfoType.MESH)
                UTILS_file_io.write_uint64(finfo, fmesh.tell())

                # write fmesh
                # vertices
                mesh_vecList = mesh.vertices[:]
                UTILS_file_io.write_uint32(fmesh, len(mesh_vecList))
                for vec in mesh_vecList:
                    #swap yz
                    UTILS_file_io.write_3vector(fmesh,vec.co[0],vec.co[2],vec.co[1])

                # uv
                mesh_faceIndexPairs = [(face, index) for index, face in enumerate(mesh.polygons)]
                UTILS_file_io.write_uint32(fmesh, len(mesh_faceIndexPairs) * 3)
                if mesh.uv_layers.active is not None:
                    uv_layer = mesh.uv_layers.active.data[:]
                    for f, f_index in mesh_faceIndexPairs:
                        # it should be triangle face, otherwise throw a error
                        if (f.loop_total != 3):
                            raise Exception("Not a triangle", f.poly.loop_total)

                        for loop_index in range(f.loop_start, f.loop_start + f.loop_total):
                            uv = uv_layer[loop_index].uv
                            # reverse v
                            UTILS_file_io.write_2vector(fmesh, uv[0], -uv[1])
                else:
                    # no uv data. write garbage
                    for i in range(len(mesh_faceIndexPairs) * 3):
                        UTILS_file_io.write_2vector(fmesh, 0.0, 0.0)

                # normals
                UTILS_file_io.write_uint32(fmesh, len(mesh_faceIndexPairs) * 3)
                for f, f_index in mesh_faceIndexPairs:
                    # no need to check triangle again
                    for loop_index in range(f.loop_start, f.loop_start + f.loop_total):
                        nml = mesh.loops[loop_index].normal
                        # swap yz
                        UTILS_file_io.write_3vector(fmesh, nml[0], nml[2], nml[1])

                # face
                # get material first
                mesh_usedBlenderMtl = mesh.materials[:]
                mesh_noMaterial = len(mesh_usedBlenderMtl) == 0
                for mat in mesh_usedBlenderMtl:
                    if mat not in materialSet:
                        materialSet.add(mat)
                        materialList.append(mat)

                UTILS_file_io.write_uint32(fmesh, len(mesh_faceIndexPairs))
                mesh_vtIndex = []
                mesh_vnIndex = []
                mesh_vIndex = []
                for f, f_index in mesh_faceIndexPairs:
                    # confirm material use
                    if mesh_noMaterial:
                        mesh_materialIndex = 0
                    else:
                        mesh_materialIndex = materialList.index(mesh_usedBlenderMtl[f.material_index])

                    # export face
                    mesh_vtIndex.clear()
                    mesh_vnIndex.clear()
                    mesh_vIndex.clear()

                    counter = 0
                    for loop_index in range(f.loop_start, f.loop_start + f.loop_total):
                        mesh_vIndex.append(mesh.loops[loop_index].vertex_index)
                        mesh_vnIndex.append(f_index * 3 + counter)
                        mesh_vtIndex.append(f_index * 3 + counter)
                        counter += 1
                    # reverse vertices sort
                    UTILS_file_io.write_face(fmesh,
                    mesh_vIndex[2], mesh_vtIndex[2], mesh_vnIndex[2],
                    mesh_vIndex[1], mesh_vtIndex[1], mesh_vnIndex[1],
                    mesh_vIndex[0], mesh_vtIndex[0], mesh_vnIndex[0])

                    # set used material
                    UTILS_file_io.write_bool(fmesh, not mesh_noMaterial)
                    UTILS_file_io.write_uint32(fmesh, mesh_materialIndex)

                # free splited normals
                mesh.free_normals_split()

        # ====================== export material
        textureSet = set()
        textureList = []
        textureCount = 0        
        with open(os.path.join(utils_tempFolder, "material.bm"), "wb") as fmaterial:
            for material in materialList:
                # write finfo first
                UTILS_file_io.write_string(finfo, material.name)
                UTILS_file_io.write_uint8(finfo, UTILS_constants.BmfileInfoType.MATERIAL)
                UTILS_file_io.write_uint64(finfo, fmaterial.tell())

                # try get original written data
                (material_colAmbient, material_colDiffuse, material_colSpecular, material_colEmissive, material_specularPower,
                material_alphaTest, material_alphaBlend, material_zBuffer, material_twoSided,
                material_texture) = UTILS_virtools_prop.get_virtools_material_data(material)

                # only try get from Principled BSDF when we couldn't get from virtools_material props
                if material_texture is None:
                    # get node
                    mat_wrap = node_shader_utils.PrincipledBSDFWrapper(material)
                    # check existence of Principled BSDF
                    if mat_wrap:
                        # we trying get texture data from Principled BSDF
                        # because bpy.types.Material.virtools_material now can provide
                        # Virtools material data stablely, so i annotate following code
                        # only keep texture data
                        '''
                        use_mirror = mat_wrap.metallic != 0.0
                        if use_mirror:
                            material_colAmbient = _set_value_when_none(material_colAmbient, (mat_wrap.metallic, mat_wrap.metallic, mat_wrap.metallic))
                        else:
                            material_colAmbient = _set_value_when_none(material_colAmbient, (1.0, 1.0, 1.0))
                        material_colDiffuse = _set_value_when_none(material_colDiffuse, (mat_wrap.base_color[0], mat_wrap.base_color[1], mat_wrap.base_color[2]))
                        material_colSpecular = _set_value_when_none(material_colSpecular, (mat_wrap.specular, mat_wrap.specular, mat_wrap.specular))
                        material_colEmissive = _set_value_when_none(material_colEmissive, mat_wrap.emission_color[:3])
                        material_specularPower = _set_value_when_none(material_specularPower, 0.0)
                        '''

                        # confirm texture
                        tex_wrap = getattr(mat_wrap, "base_color_texture", None)
                        if tex_wrap:
                            image = tex_wrap.image
                            if image:
                                material_texture = image
                                

                # check texture index
                if material_texture is None:
                    material_useTexture = False
                    material_textureIndex = 0
                else:
                    # add into texture list
                    if material_texture not in textureSet:
                        textureSet.add(material_texture)
                        textureList.append(material_texture)
                        textureIndex = textureCount
                        textureCount += 1
                    else:
                        textureIndex = textureList.index(material_texture)

                    material_useTexture = True
                    material_textureIndex = textureIndex

                UTILS_file_io.write_color(fmaterial, material_colAmbient)
                UTILS_file_io.write_color(fmaterial, material_colDiffuse)
                UTILS_file_io.write_color(fmaterial, material_colSpecular)
                UTILS_file_io.write_color(fmaterial, material_colEmissive)
                UTILS_file_io.write_float(fmaterial, material_specularPower)
                UTILS_file_io.write_bool(fmaterial, material_alphaTest)
                UTILS_file_io.write_bool(fmaterial, material_alphaBlend)
                UTILS_file_io.write_bool(fmaterial, material_zBuffer)
                UTILS_file_io.write_bool(fmaterial, material_twoSided)
                UTILS_file_io.write_bool(fmaterial, material_useTexture)
                UTILS_file_io.write_uint32(fmaterial, material_textureIndex)
            

        # ====================== export texture
        texture_blenderFilePath = os.path.dirname(bpy.data.filepath)
        texture_existedTextureFilepath = set()        
        with open(os.path.join(utils_tempFolder, "texture.bm"), "wb") as ftexture:
            for texture in textureList:
                # write finfo first
                UTILS_file_io.write_string(finfo, texture.name)
                UTILS_file_io.write_uint8(finfo, UTILS_constants.BmfileInfoType.TEXTURE)
                UTILS_file_io.write_uint64(finfo, ftexture.tell())

                # confirm whether it is internal texture
                # get absolute texture path
                texture_filepath = io_utils.path_reference(texture.filepath, texture_blenderFilePath, utils_tempTextureFolder,
                                                            'ABSOLUTE', "", None, texture.library)
                # get file name and write it
                texture_filename = os.path.basename(texture_filepath)
                UTILS_file_io.write_string(ftexture, texture_filename)

                if (_is_external_texture(texture_filename)):
                    # write directly, use Ballance texture
                    UTILS_file_io.write_bool(ftexture, True)
                else:
                    # copy internal texture, if this file is copied, do not copy it again
                    UTILS_file_io.write_bool(ftexture, False)
                    if texture_filename not in texture_existedTextureFilepath:
                        shutil.copy(texture_filepath, os.path.join(utils_tempTextureFolder, texture_filename))
                        texture_existedTextureFilepath.add(texture_filename)


    # ============================================ 
    # save zip and clean up folder
    UTILS_zip_helper.compress(utils_tempFolder, bmx_filepath)
    utils_tempFolderObj.cleanup()
def copy_image(image, context):
    base_src = os.path.dirname(bpy.data.filepath)
    filepath_full = bpy.path.abspath(image.filepath, library=image.library)
    image_src = path_reference(filepath_full, base_src, context.base_url, 'COPY', "textures", context.copy_set, image.library)
    # Stats are written when files have been copied (see Context.finalize)
    return image_src