Skip to content

Commit

Permalink
feat(MeshObject): Rework material manipulation
Browse files Browse the repository at this point in the history
  • Loading branch information
AndreyYashkin committed Apr 1, 2024
1 parent eaf6d86 commit 84fc5e7
Show file tree
Hide file tree
Showing 4 changed files with 109 additions and 14 deletions.
4 changes: 2 additions & 2 deletions blenderproc/api/material/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from blenderproc.python.material.MaterialLoaderUtility import add_alpha, add_alpha_channel_to_textures, \
add_alpha_texture_node, add_ambient_occlusion, add_base_color, add_bump, add_displacement, add_metal, \
add_normal, add_roughness, add_specular, change_to_texture_less_render, collect_all, connect_uv_maps, \
convert_to_materials, create_image_node, create, is_material_used, create_new_cc_material, \
create_procedural_texture, find_cc_material_by_name, create_material_from_texture
convert_to_material, convert_to_materials, create_image_node, create, is_material_used, \
create_new_cc_material, create_procedural_texture, find_cc_material_by_name, create_material_from_texture
from blenderproc.python.material.Dust import add_dust
8 changes: 7 additions & 1 deletion blenderproc/python/material/MaterialLoaderUtility.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,19 @@ def create(name: str) -> Material:
return Material(new_mat)


def convert_to_material(blender_material: bpy.types.Material) -> Material:
""" Converts the given blender material to bproc.Material
"""
return None if blender_material is None else Material(blender_material)


def convert_to_materials(blender_materials: List[Optional[bpy.types.Material]]) -> List[Optional[Material]]:
""" Converts the given list of blender materials to bproc.Material(s)
:param blender_materials: List of materials.
:return: The list of materials.
"""
return [(None if obj is None else Material(obj)) for obj in blender_materials]
return [convert_to_material(obj) for obj in blender_materials]


def find_cc_material_by_name(material_name: str, custom_properties: Dict[str, Any]) -> bpy.types.Material:
Expand Down
71 changes: 60 additions & 11 deletions blenderproc/python/types/MeshObjectUtility.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,39 +27,88 @@ class MeshObject(Entity):
Every instance of this class is a mesh which can be rendered in the scene. It can have multiple materials and
different configurations of vertices with faces and edges.
"""
def materials(self) -> int:
""" Returns the number of material slots of the object.
def get_materials(self) -> List[Optional[Material]]:
""" Returns the materials used by the mesh.
:return: The number of material slots.
"""
return len(self.blender_obj.material_slots)

def get_material_slot_link(self, index: int) -> str:
""" Returns whether object's or object's data material is used in the material slot.
:return: "DATA" if the material slot is linked to data material or "OBJECT" otherwise.
"""
return self.blender_obj.material_slots[index].link

def set_material_slot_link(self, index: int, link: str):
""" Sets whether object's or object's data material is used in the material slot.
Available: ["DATA", "OBJECT"]. Type: str
"""
self.blender_obj.material_slots[index].link = link

def get_material(self, index: int, link = "VISIBLE") -> Material:
""" Returns the material used by the object.
:link: The mode specifying whether to get material linked to the object or object's data.
Available: ["DATA", "OBJECT", "VISIBLE"]. Type: str
:return: A list of materials.
"""
link = link.upper()
if link == "VISIBLE" and self.get_material_slot_link(index) == "DATA":
link = "DATA"
link2get = "OBJECT" if link == "VISIBLE" else link

link2return = self.get_material_slot_link(index)
self.set_material_slot_link(index, link2get)
material = self.blender_obj.material_slots[index].material
self.set_material_slot_link(index, link2return)

# If there is no material in the `OBJECT` slot then the 'DATA' material is displayed.
if material is None and link == "VISIBLE":
return self.get_material(index, "DATA")
else:
return MaterialLoaderUtility.convert_to_material(material)

def get_materials(self, link = "VISIBLE") -> List[Optional[Material]]:
""" Returns the materials used by the object.
:link: The mode specifying whether to get materials linked to the object or object's data.
Available: ["DATA", "OBJECT", "VISIBLE"]. Type: str
:return: A list of materials.
"""
return MaterialLoaderUtility.convert_to_materials(self.blender_obj.data.materials)
return [self.get_material(index, link) for index in range(self.materials())]

def has_materials(self) -> bool:
"""
Returns True if the object has material slots. This does not necessarily mean any `Material` is assigned to it.
:return: True if the object has material slots.
"""
return len(self.blender_obj.data.materials) > 0
return self.materials() > 0

def set_material(self, index: int, material: Material):
""" Sets the given material at the given index of the objects material list.
def set_material(self, index: int, material: Material, link="DATA"):
""" Sets the given material at the given index of the object's material list.
:param index: The index to set the material to.
:param material: The material to set.
:link: The mode specifying whether to link material to the object or object's data.
Available: ["DATA", "OBJECT"]. Type: str
"""
self.blender_obj.data.materials[index] = material.blender_obj
keep_link = self.get_material_slot_link(index)
self.set_material_slot_link(index, link)
self.blender_obj.material_slots[index].material = None if material is None else material.blender_obj
self.set_material_slot_link(index, keep_link)

def add_material(self, material: Material):
""" Adds a new material to the object.
""" Adds a new material to the data.
:param material: The material to add.
"""
self.blender_obj.data.materials.append(material.blender_obj)

def new_material(self, name: str) -> Material:
""" Creates a new material and adds it to the object.
""" Creates a new material and adds it to the mesh.
:param name: The name of the new material.
"""
Expand All @@ -68,11 +117,11 @@ def new_material(self, name: str) -> Material:
return new_mat

def clear_materials(self):
""" Removes all materials from the object. """
""" Removes all materials from the data. """
self.blender_obj.data.materials.clear()

def replace_materials(self, material: bpy.types.Material):
""" Replaces all materials of the object with the given new material.
""" Replaces all materials of the mesh with the given new material.
:param material: A material that should exclusively be used as new material for the object.
"""
Expand Down
40 changes: 40 additions & 0 deletions tests/testMeshObject.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import blenderproc as bproc

import unittest

from blenderproc.python.tests.SilentMode import SilentMode
from blenderproc.python.types.MeshObjectUtility import create_primitive
from blenderproc.python.material import MaterialLoaderUtility


class UnitTestCheckUtility(unittest.TestCase):
def test_materials(self):
bproc.clean_up(True)

mat1 = MaterialLoaderUtility.create("mat1")
mat2 = MaterialLoaderUtility.create("mat2")

obj = create_primitive("CUBE")
self.assertTrue(obj.has_materials() == False)

obj.add_material(mat1)
self.assertTrue(obj.materials() == 1)
self.assertTrue(obj.get_material_slot_link(0) == "DATA")
self.assertTrue(obj.get_material(0, "DATA").get_name() == "mat1")
self.assertTrue(obj.get_material(0, "OBJECT") is None)
self.assertTrue(obj.get_material(0, "VISIBLE").get_name() == "mat1")

obj.set_material(0, mat2, "OBJECT")
self.assertTrue(obj.get_material(0, "DATA").get_name() == "mat1")
self.assertTrue(obj.get_material(0, "OBJECT").get_name() == "mat2")
self.assertTrue(obj.get_material(0, "VISIBLE").get_name() == "mat1")

obj.set_material_slot_link(0, "OBJECT")
self.assertTrue(obj.get_material(0, "DATA").get_name() == "mat1")
self.assertTrue(obj.get_material(0, "OBJECT").get_name() == "mat2")
self.assertTrue(obj.get_material(0, "VISIBLE").get_name() == "mat2")

obj.set_material(0, None, "OBJECT")
self.assertTrue(obj.get_material(0, "DATA").get_name() == "mat1")
self.assertTrue(obj.get_material(0, "OBJECT") is None)
self.assertTrue(obj.get_material(0, "VISIBLE").get_name() == "mat1")

0 comments on commit 84fc5e7

Please sign in to comment.