Skip to content
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,4 @@ doc/_build
*_wip**
tests/api/test_data/.temp**
*.zip
*.code-workspace
14 changes: 14 additions & 0 deletions phobos/blender/operators/editing.py
Original file line number Diff line number Diff line change
Expand Up @@ -1061,11 +1061,25 @@ class GenerateInertialObjectsOperator(Operator):
description="Clear existing inertial objects of selected links.",
)

_toggling : BoolProperty(
name="Ensure no infinite recursion when toggling visuals and collisions",
default=False,
description="Used to avoid infine recursion when toggling visuals/collisions",
)

def toggleVisual(self, context):
if self._toggling:
return
self._toggling = True
self.collisions = not bool(self.visuals)
self._toggling = False

def toggleCollision(self, context):
if self._toggling:
return
self._toggling = True
self.visuals = not bool(self.collisions)
self._toggling = False

visuals : BoolProperty(
name="visual",
Expand Down
30 changes: 30 additions & 0 deletions phobos/blender/utils/blender.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,36 @@ def getBlenderVersion():
return bpy.app.version[0] * 100 + bpy.app.version[1]


def blenderVersionIsAtLeast(required_version: tuple) -> bool:
"""
Check if the current Blender version is greater than or equal to a given version.

Args:
required_version (tuple): Version you want to check against.
Example: (4, 0) or (3, 6, 2)

Returns:
bool: True if the current Blender version >= required_version, False otherwise.
"""
current_version = bpy.app.version

for blender_v, required_v in zip(current_version, required_version):
if blender_v > required_v:
log(f"Current version {current_version} > {required_version}", "DEBUG")
return True
if blender_v < required_v:
log(f"Current version {current_version} < {required_version}", "DEBUG")
return False

# If we get here, all compared numbers are equal
if len(current_version) >= len(required_version):
log(f"Current version {current_version} >= {required_version}", "DEBUG")
return True # current_version is equal or has more patch info
else:
log(f"Could not parse version provided to blenderVersionIsAtLeast: {required_version}", "ERROR")
return False


def getPhobosPreferences():
"""TODO Missing documentation"""
return bpy.context.preferences.addons["phobos"].preferences
Expand Down
23 changes: 18 additions & 5 deletions phobos/io/representation.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
from ..utils.transform import inv
from ..utils.xml import read_relative_filename
from .. import defs as phobos_defs
from ..blender.utils.blender import blenderVersionIsAtLeast

MESH_INFO_KEYS = ["vertex_normals", "texture_coords", "vertices", "faces"]
MESH_DATA_TYPES = ["trimesh.base.Trimesh", "trimesh.scene.scene.Scene", "file_obj", "file_stl", "file_dae", "file_iv"]
Expand Down Expand Up @@ -811,7 +812,7 @@ def load_mesh(self, reload=False):
break
if BPY_AVAILABLE and self.mesh_object is None:
# A new file handler API was introduced in Blender 4.1
b41Import = bpy.app.version[0] == 4 and bpy.app.version[1] >= 1
b41Import = blenderVersionIsAtLeast((4,1))
bpy.ops.object.select_all(action='DESELECT')
if self.input_type == "file_stl":
if b41Import:
Expand Down Expand Up @@ -1029,7 +1030,7 @@ def provide_mesh_file(self, targetpath, rel_mesh_pathes=None, format=None, throw
else:
log.warn(f"Couldn't provide mesh {self.unique_name} to {targetpath}, because this mesh can't be exported as bobj")
return
elif format.lower() == "bobj" and (not self._info_in_sync and "texture_coords" in self._mesh_information):
elif format.lower() == "bobj" and (not self._info_in_sync and self._mesh_information is not None and "texture_coords" in self._mesh_information):
if throw_on_invalid_bobj:
raise IOError(
f"Couldn't provide mesh {self.unique_name} to {targetpath}, because this mesh has been edited and thus the textures might have get mixed up.")
Expand All @@ -1051,7 +1052,7 @@ def provide_mesh_file(self, targetpath, rel_mesh_pathes=None, format=None, throw
return
# export for blender
# A new file handler API was introduced in Blender 4.1
b41Export = bpy.app.version[0] == 4 and bpy.app.version[1] >= 1
b41Export = blenderVersionIsAtLeast((4,1))
if BPY_AVAILABLE and isinstance(self.mesh_object, bpy.types.Mesh):
from ..blender.utils import blender as bUtils
objname = "tmp_export_"+self.unique_name
Expand All @@ -1063,26 +1064,38 @@ def provide_mesh_file(self, targetpath, rel_mesh_pathes=None, format=None, throw
if format.lower() == 'obj':
axis_forward = bpy.context.preferences.addons["phobos"].preferences.obj_axis_forward
axis_up = bpy.context.preferences.addons["phobos"].preferences.obj_axis_up
# If user wants to export texture, we also export .mtl file associated to the .obj file
export_texture = bpy.context.scene.phobosexportsettings.exportTextures
# Adapt export path type for the .mtl files to also use relative path
path_mode_relative = "AUTO"
if "relative" in getattr(bpy.context.scene.phobosexportsettings, "urdfOutputPathtype"):
path_mode_relative = "RELATIVE"
if b41Export:
# Adapt axis forward and UP to match the expected values
# E.g. "-X" becomes "NEGATIVE_X"
axis_forward = axis_forward.replace("-", "NEGATIVE_")
axis_up = axis_up.replace("-", "NEGATIVE_")
bpy.ops.wm.obj_export(
filepath=targetpath,
export_selected_objects=True,
export_normals=True,
export_materials=False,
export_materials=export_texture,
apply_modifiers=True,
forward_axis=axis_forward,
up_axis=axis_up,
path_mode=path_mode_relative,
)
else:
bpy.ops.export_scene.obj(
filepath=targetpath,
use_selection=True,
use_normals=True,
use_materials=False,
use_materials=export_texture,
use_mesh_modifiers=True,
use_blen_objects=False,
axis_forward=axis_forward,
axis_up=axis_up,
path_mode=path_mode_relative,
)
elif format.lower() == 'stl':
if b41Export:
Expand Down