Skip to content

Commit 30a93ab

Browse files
committed
We have the convience factor of TEXTURE doing something. Bumps version, we're ready for public comment!
1 parent df7b69d commit 30a93ab

5 files changed

Lines changed: 81 additions & 22 deletions

File tree

io_xplane2blender/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
"name": "Import-Export: X-Plane (.obj)",
2626
"description": "Import and Export X-Plane objects/planes (.obj format)",
2727
"author": "Ted Greene, Ben Supnik",
28-
"version": (4, 1, 0),
28+
"version": (4, 2, 0),
2929
"blender": (2, 80, 0),
3030
"location": "File > Import/Export > X-Plane",
3131
"warning": "",

io_xplane2blender/importer/xplane_imp_cmd_builder.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -310,7 +310,6 @@ def chunk(it, size):
310310
self.datablock_info,
311311
mesh_src=me,
312312
)
313-
test_creation_helpers.set_material(ob, "Material")
314313

315314
return ob
316315
else:
@@ -427,6 +426,9 @@ def __init__(self, filepath: Path):
427426

428427
self.root_collection.xplane.is_exportable_collection = True
429428
self.vt_table = VTTable([], [])
429+
self.texture: Optional[Path] = None
430+
# self.texture_lit:Optional[Path] = Path()
431+
# self.texture_normal:Optiona[Path] = Path()
430432

431433
# Although we don't end up making this, it is useful for tree problems
432434
self.root_intermediate_datablock = IntermediateDatablock(
@@ -1128,13 +1130,19 @@ def fill_in_eulers(
11281130

11291131
if out_block.datablock_type == "EMPTY":
11301132
ob = test_creation_helpers.create_datablock_empty(
1131-
out_block.datablock_info
1133+
out_block.datablock_info,
1134+
empty_display_size=0.05,
11321135
)
11331136
elif out_block.datablock_type == "MESH":
11341137
try:
11351138
ob = out_block.build_mesh(self.vt_table)
11361139
except ValueError:
11371140
ob = None
1141+
else:
1142+
if self.texture:
1143+
test_creation_helpers.set_material(
1144+
ob, "Material", texture_image=self.texture
1145+
)
11381146

11391147
if ob:
11401148
ob.matrix_local = out_block.bake_matrix.copy()

io_xplane2blender/importer/xplane_imp_parser.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import pathlib
1212
import re
1313
from dataclasses import dataclass, field
14+
from pathlib import Path
1415
from pprint import pprint
1516
from typing import Any, Callable, Dict, List, Optional, Tuple, Union
1617

@@ -143,7 +144,17 @@ def scan_float(s_itr:iter)
143144
# def scan_(last=False, msg_missing=f"Could not convert parameter {lineno} _true, default=None)->value:
144145
# Throws parser error if needed
145146
# def _try to swallow all exceptions if the only thing that should happen is the line getting ignored on bad data. Otherwise we can go into more crazy exception hanlding cases
146-
if directive == "VT":
147+
if directive == "TEXTURE":
148+
try:
149+
texture_path = (filepath.parent / Path(components[0])).resolve()
150+
except IndexError:
151+
logger.warn(f"TEXTURE directive given but was empty")
152+
else:
153+
if texture_path.exists():
154+
builder.texture = texture_path
155+
else:
156+
logger.warn(f"'{str(texture_path)}' is not a real file")
157+
elif directive == "VT":
147158
components[:3] = vec_x_to_b(list(map(float, components[:3])))
148159
components[3:6] = vec_x_to_b(list(map(float, components[3:6])))
149160
components[6:8] = list(map(float, components[6:8]))

io_xplane2blender/tests/test_creation_helpers.py

Lines changed: 57 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import typing
1919
from collections import namedtuple
2020
from dataclasses import dataclass
21+
from pathlib import Path
2122
from typing import *
2223

2324
import bpy
@@ -455,6 +456,8 @@ def create_datablock_armature(
455456
def 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

649661
def create_material_default() -> bpy.types.Material:
@@ -664,6 +676,10 @@ def create_scene(name: str) -> bpy.types.Scene:
664676
def 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(
10271044
def 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

10441084
def set_parent(blender_object: bpy.types.Object, parent_info: ParentInfo) -> None:

io_xplane2blender/xplane_config.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
CURRENT_ADDON_VERSION: Tuple[int, int, int] = bl_info["version"]
1111

1212
# The current build type, must be a member of XPlane2BlenderVersion.BUILD_TYPE
13-
CURRENT_BUILD_TYPE = xplane_constants.BUILD_TYPE_BETA
13+
CURRENT_BUILD_TYPE = xplane_constants.BUILD_TYPE_ALPHA
1414

1515
# The current build type version, must be > 0
1616
# if not BUILD_TYPE_DEV or BULD_TYPE_LEGACY

0 commit comments

Comments
 (0)