1818import typing
1919from collections import namedtuple
2020from dataclasses import dataclass
21+ from pathlib import Path
2122from typing import *
2223
2324import bpy
@@ -455,6 +456,8 @@ def create_datablock_armature(
455456def create_datablock_empty (
456457 info : DatablockInfo ,
457458 scene : Optional [Union [bpy .types .Scene , str ]] = None ,
459+ empty_display_type : str = "PLAIN_AXES" ,
460+ empty_display_size = 1 ,
458461) -> bpy .types .Object :
459462 """
460463 Creates a datablock empty and links it to a scene and collection.
@@ -468,7 +471,8 @@ def create_datablock_empty(
468471 scene = bpy .context .scene
469472 set_collection (ob , info .collection )
470473
471- ob .empty_display_type = "PLAIN_AXES"
474+ ob .empty_display_type = empty_display_type
475+ ob .empty_display_size = empty_display_size
472476 ob .location = info .location
473477 ob .rotation_mode = info .rotation_mode
474478 set_rotation (ob , info .rotation , info .rotation_mode )
@@ -620,30 +624,38 @@ def create_datablock_light(
620624 return ob
621625
622626
623- def create_image_from_disk (
624- filename : str , filepath : str = "//tex/{}"
625- ) -> bpy .types .Image :
627+ def create_image_from_disk (filepath : Union [Path , str ]) -> bpy .types .Image :
626628 """
627629 Create an image from a .png file on disk.
630+
628631 Returns image or raises OSError
629632 """
630- assert os .path .splitext (filename )[1 ] == ".png"
633+ filepath = str (filepath )
634+ assert os .path .splitext (filepath )[1 ] in {".dds" , ".png" }
631635 # Load image file. Change here if the snippet folder is
632636 # not located in you home directory.
633- realpath = bpy .path .abspath (filepath .format (filename ))
637+ realpath = bpy .path .abspath (filepath .format (filepath ))
634638 try :
635- img = bpy .data .images .load (realpath )
639+ img = bpy .data .images .load (realpath , check_existing = True )
636640 img .filepath = bpy .path .relpath (realpath )
637641 return img
638642 except (RuntimeError , ValueError ): # Couldn't load or make relative path
639643 raise OSError ("Cannot load image %s" % realpath )
640644
641645
642- def create_material (material_name : str ):
646+ def create_material (
647+ material_name : str ,
648+ texture_image : Optional [Union [bpy .types .Image , Path , str ]] = None ,
649+ ):
650+ """
651+ Create a material and optionally give it a texture
652+ """
643653 try :
644- return bpy .data .materials [material_name ]
645- except :
646- return bpy .data .materials .new (material_name )
654+ mat = get_material (material_name )
655+ except KeyError :
656+ mat = bpy .data .materials .new (material_name )
657+
658+ return mat
647659
648660
649661def create_material_default () -> bpy .types .Material :
@@ -664,6 +676,10 @@ def create_scene(name: str) -> bpy.types.Scene:
664676def get_image (name : str ) -> Optional [bpy .types .Image ]:
665677 """
666678 New images will be created in //tex and will be a .png
679+
680+ TODO: This API is incomplete, what if None found? What should happen?
681+ Creating a new image is almost never what you want, unlike creating a bland cube
682+ I vote KeyError which makes this a useless wrapper
667683 """
668684 return bpy .data .images .get (name )
669685
@@ -915,7 +931,8 @@ def set_animation_data(
915931 else :
916932 dataref_prop .value = kf_info .dataref_value
917933 # Multiple assignment isn't harmful
918- dataref_prop .loop = kf_info .dataref_loop
934+ if kf_info .dataref_loop is not None :
935+ dataref_prop .loop = kf_info .dataref_loop
919936
920937 if not kf_info .location and not kf_info .rotation :
921938 continue
@@ -1027,18 +1044,41 @@ def set_manipulator_settings(
10271044def set_material (
10281045 blender_object : bpy .types .Object ,
10291046 material_name : str = "Material" ,
1030- material_props : Optional [Dict [str , Any ]] = None ,
1031- create_missing : bool = True ,
1047+ texture_image : Optional [Union [bpy .types .Image , Path , str ]] = None ,
10321048):
1049+ """
1050+ Sets blender_object's 1st material slot to 'material_name'.
1051+
1052+ Optionally a texture_image is used to set up a basic
1053+ shader with Base Color set to Image Texture
1054+
1055+ Raises OSError if texture_image is a path and not a real image
1056+ """
10331057
10341058 mat = create_material (material_name )
10351059 try :
10361060 blender_object .material_slots [0 ].material = mat
10371061 except IndexError :
10381062 blender_object .data .materials .append (mat )
1039- if material_props :
1040- for prop , value in material_props .items ():
1041- setattr (mat .xplane .manip , prop , value )
1063+
1064+ if texture_image :
1065+ mat .use_nodes = True
1066+ tex_node = mat .node_tree .nodes .new ("ShaderNodeTexImage" )
1067+ if isinstance (texture_image , bpy .types .Image ):
1068+ tex_node .image = texture_image
1069+ elif isinstance (texture_image , Path ) or texture_image .endswith (
1070+ (".png" , ".dds" )
1071+ ):
1072+ tex_node .image = create_image_from_disk (texture_image )
1073+ else :
1074+ tex_node .image = get_image (texture_image )
1075+
1076+ bsdf_node = mat .node_tree .nodes ["Principled BSDF" ]
1077+
1078+ # TODO: We should make it nice and move it so it isn't overlapping
1079+ mat .node_tree .links .new (
1080+ tex_node .outputs ["Color" ], bsdf_node .inputs ["Base Color" ]
1081+ )
10421082
10431083
10441084def set_parent (blender_object : bpy .types .Object , parent_info : ParentInfo ) -> None :
0 commit comments