Skip to content
Snippets Groups Projects
import_obj.py 51.5 KiB
Newer Older
# ##### BEGIN GPL LICENSE BLOCK #####
#
#  This program is free software; you can redistribute it and/or
#  modify it under the terms of the GNU General Public License
#  as published by the Free Software Foundation; either version 2
#  of the License, or (at your option) any later version.
#
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#
#  You should have received a copy of the GNU General Public License
#  along with this program; if not, write to the Free Software Foundation,
#  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# ##### END GPL LICENSE BLOCK #####

# <pep8 compliant>

# Script copyright (C) Campbell Barton
# Contributors: Campbell Barton, Jiri Hnidek, Paolo Ciccone

"""
This script imports a Wavefront OBJ files to Blender.

Usage:
Run this script from "File->Import" menu and then load the desired OBJ file.
Note, This loads mesh objects and materials only, nurbs and curves are not supported.

http://wiki.blender.org/index.php/Scripts/Manual/Import/wavefront_obj
"""

import os
import time
import bpy
import mathutils
from bpy_extras.io_utils import unpack_list
from bpy_extras.image_utils import load_image
from bpy_extras.wm_utils.progress_report import ProgressReport
    Returns 1 string representing the value for this line
    None will be returned if there's only 1 word
Campbell Barton's avatar
Campbell Barton committed
    length = len(line_split)
    if length == 1:
        return None

    elif length == 2:
        return line_split[1]

    elif length > 2:
        return b' '.join(line_split[1:])
def obj_image_load(context_imagepath_map, line, DIR, recursive, relpath):
    Mainly uses comprehensiveImageLoad
    But we try all space-separated items from current line when file is not found with last one
    (users keep generating/using image files with spaces in a format that does not support them, sigh...)
    Also tries to replace '_' with ' ' for Max's exporter replaces spaces with underscores.
    filepath_parts = line.split(b' ')
    image = None
    for i in range(-1, -len(filepath_parts), -1):
        imagepath = os.fsdecode(b" ".join(filepath_parts[i:]))
        image = context_imagepath_map.get(imagepath, ...)
        if image is ...:
            image = load_image(imagepath, DIR, recursive=recursive, relpath=relpath)
            if image is None and "_" in imagepath:
                image = load_image(imagepath.replace("_", " "), DIR, recursive=recursive, relpath=relpath)
            if image is not None:
                context_imagepath_map[imagepath] = image
                break;

    if image is None:
        imagepath = os.fsdecode(filepath_parts[-1])
        image = load_image(imagepath, DIR, recursive=recursive, place_holder=True, relpath=relpath)
        context_imagepath_map[imagepath] = image
                     material_libs, unique_materials,
                     use_image_search, float_func):
    Create all the used materials in this obj,
    assign colors and images to the materials from all referenced material libs
    from math import sqrt

Campbell Barton's avatar
Campbell Barton committed
    DIR = os.path.dirname(filepath)
    # Don't load the same image multiple times
    context_imagepath_map = {}

    nodal_material_wrap_map = {}
    def load_material_image(blender_material, mat_wrap, context_material_name, img_data, line, type):
        """
        Set textures defined in .mtl file.
        """
        map_options = {}

        curr_token = []
        for token in img_data[:-1]:
            if token.startswith(b'-') and token[1:].isalpha():
                if curr_token:
                    map_options[curr_token[0]] = curr_token[1:]
                curr_token[:] = []
            curr_token.append(token)
        if curr_token:
            map_options[curr_token[0]] = curr_token[1:]
        # Absolute path - c:\.. etc would work here
        image = obj_image_load(context_imagepath_map, line, DIR, use_image_search, relpath)
        map_offset = map_options.get(b'-o')
        map_scale = map_options.get(b'-s')

        def _generic_tex_set(nodetex, image, texcoords, translation, scale):
            nodetex.image = image
            nodetex.texcoords = texcoords
            if translation is not None:
                nodetex.translation = translation
            if scale is not None:
                nodetex.scale = scale

        # Adds textures for materials (rendering)
        if type == 'Kd':
            _generic_tex_set(mat_wrap.base_color_texture, image, 'UV', map_offset, map_scale)
            # XXX Not supported?
            print("WARNING, currently unsupported ambient texture, skipped.")
            _generic_tex_set(mat_wrap.specular_texture, image, 'UV', map_offset, map_scale)
            # XXX Not supported?
            print("WARNING, currently unsupported emit texture, skipped.")
            bump_mult = map_options.get(b'-bm')
            bump_mult = float(bump_mult[0]) if (bump_mult and len(bump_mult[0]) > 1) else 1.0
            mat_wrap.normalmap_strength_set(bump_mult)
            _generic_tex_set(mat_wrap.normalmap_texture, image, 'UV', map_offset, map_scale)
            _generic_tex_set(mat_wrap.transmission_texture, image, 'UV', map_offset, map_scale)
            # XXX Not supported?
            print("WARNING, currently unsupported displacement texture, skipped.")
            # ~ mat_wrap.bump_image_set(image)
            # ~ mat_wrap.bump_mapping_set(coords='UV', translation=map_offset, scale=map_scale)
            map_type = map_options.get(b'-type')
            if map_type and map_type != [b'sphere']:
                print("WARNING, unsupported reflection type '%s', defaulting to 'sphere'"
                      "" % ' '.join(i.decode() for i in map_type))

            _generic_tex_set(mat_wrap.base_color_texture, image, 'Reflection', map_offset, map_scale)
            mat_wrap.base_color_texture.projection = 'SPHERE'
            raise Exception("invalid type %r" % type)

    # Add an MTL with the same name as the obj if no MTLs are spesified.
    temp_mtl = os.path.splitext((os.path.basename(filepath)))[0] + ".mtl"
    if os.path.exists(os.path.join(DIR, temp_mtl)):
        material_libs.add(temp_mtl)
    # Create new materials
Campbell Barton's avatar
Campbell Barton committed
    for name in unique_materials:  # .keys()
Campbell Barton's avatar
Campbell Barton committed
        if name is not None:
            ma = unique_materials[name] = bpy.data.materials.new(name.decode('utf-8', "replace"))
            from bpy_extras import node_shader_utils
            ma_wrap = node_shader_utils.PrincipledBSDFWrapper(ma, is_readonly=False)
            nodal_material_wrap_map[ma] = ma_wrap
            ma_wrap.use_nodes = True
    for libname in sorted(material_libs):
Campbell Barton's avatar
Campbell Barton committed
        mtlpath = os.path.join(DIR, libname)
            print("\tMaterial not found MTL: %r" % mtlpath)
Loading
Loading full blame...