Source code for blendify.renderables.mesh

from typing import Sequence

import bmesh
import bpy
import numpy as np

from .base import RenderableObject
from ..colors import VertexColors, UniformColors
from ..colors.base import ColorsList
from ..colors.texture import VertexUV, FacesUV, UVColors


[docs] class Mesh(RenderableObject): """Basic mesh, supports uniform (UniformColors), per-vertex (VertexColors) and texture (TextureColors and FileTextureColors) coloring with per-vertex uv (VertexUV) or per-face uv maps (FacesUV). Properties: emit_shadow (bool, optional): control whether mesh will emit shadow from any light source in the scene """ def __init__( self, vertices: np.ndarray, faces: np.ndarray, tag: str, faces_material: Sequence[Sequence[int]] = None, **kwargs ): """Creates Blender Object that represent given mesh Args: vertices (np.ndarray): mesh vertices faces (np.ndarray): mesh faces material (Union[Material, MaterialList]): Material instance or a list of Material instances colors (Union[Colors, ColorsList]): Colors instance or a list of Colors instances quaternion (Vector4d, optional): rotation applied to Blender object (default: None (identity)) translation (Vector3d, optional): translation applied to the Blender object (default: (0,0,0)) tag (str): name of the created object in Blender faces_material (np.ndarray, optional): for each face, the material index assigned to it """ obj = self._blender_create_object(vertices, faces, tag) self._faces_material = faces_material self._faces_count = len(faces) super().__init__(**kwargs, blender_object=obj, tag=tag) def _blender_create_object( self, vertices: np.ndarray, faces: np.ndarray, tag: str ) -> bpy.types.Object: """Creates mesh object in Blender Args: vertices (np.ndarray): mesh vertices faces (np.ndarray): mesh faces Returns: bpy.types.Object: Blender mesh """ mesh = bpy.data.meshes.new(name=tag) mesh.from_pydata(vertices.tolist(), [], faces.tolist()) obj = bpy.data.objects.new(tag, mesh) bpy.context.collection.objects.link(obj) self._blender_mesh = mesh return obj
[docs] def set_smooth(self, smooth: bool = True): """Turns smooth shading on and off based on the bool argument Args: smooth (bool, optional): If True shade smooth else shade flat (default: True) """ bpy.context.view_layer.objects.active = self._blender_object bpy.ops.object.mode_set(mode='EDIT') bm = bmesh.from_edit_mesh(self._blender_mesh) for face in bm.faces: face.smooth = smooth bpy.ops.object.mode_set(mode='OBJECT') if smooth: bpy.ops.object.shade_smooth() else: bpy.ops.object.shade_flat()
def _blender_set_colors( self, colors_list: ColorsList ): """Remembers current color properties, builds a color node for material, sets color information to mesh Args: colors_list (ColorsList): list of target colors """ for colors in colors_list: if isinstance(colors, VertexColors): bpy.context.view_layer.objects.active = self._blender_object bpy.ops.object.mode_set(mode='EDIT') bm = bmesh.from_edit_mesh(self._blender_mesh) color_layer = bm.loops.layers.color.new("color") for face in bm.faces: for loop in face.loops: loop[color_layer] = colors.vertex_colors[loop.vert.index] bpy.ops.object.mode_set(mode='OBJECT') self._blender_mesh.vertex_colors["color"].active_render = True elif isinstance(colors, UVColors): bpy.context.view_layer.objects.active = self._blender_object bpy.ops.object.mode_set(mode='EDIT') self._blender_mesh.uv_layers.new(name='NewUVMap') bm = bmesh.from_edit_mesh(self._blender_mesh) uv_layer = bm.loops.layers.uv.active uv_map = colors.uv_map if isinstance(uv_map, VertexUV): for face in bm.faces: for loop in face.loops: loop_uv = loop[uv_layer] loop_uv.uv = uv_map.data[loop.vert.index].tolist() elif isinstance(uv_map, FacesUV): for face in bm.faces: face_uv = uv_map.data[face.index] for loop, loop_uv_coords in zip(face.loops, face_uv): loop[uv_layer].uv = loop_uv_coords.tolist() else: raise NotImplementedError(f"Unknown UV map type: {uv_map.__class__.__name__}") bpy.ops.object.mode_set(mode='OBJECT') elif not isinstance(colors, UniformColors): raise NotImplementedError(f"Unknown Colors type {colors.__class__.__name__}") super()._blender_set_colors(colors_list)
[docs] def update_vertices( self, vertices: np.ndarray ): """Updates mesh vertices coordinates Args: vertices (np.ndarray): new vertex coordinates """ assert len(self._blender_mesh.vertices) == len(vertices), \ f"Number of vertices should be the same (expected {len(self._blender_mesh.vertices)}, got {len(vertices)})" for ind, vert in enumerate(self._blender_mesh.vertices): vert.co = vertices[ind] self._blender_mesh.update()
def _blender_assign_materials(self): super()._blender_assign_materials() assert self._faces_material is None or (len(self._faces_material) == self._faces_count), \ f"Number of material faces should be equal to the number of faces ({self._faces_count})" if not (len(self._material_instances) == 1 or self._faces_material is None): bpy.context.view_layer.objects.active = self._blender_object bpy.ops.object.mode_set(mode='EDIT') bpy.ops.mesh.select_mode(type='FACE') bpy.ops.mesh.select_all(action='DESELECT') bm = bmesh.from_edit_mesh(self._blender_mesh) for mat_ind in range(self._materials_count): for face in bm.faces: perface_mat_ind = self._faces_material[face.index] if perface_mat_ind == mat_ind: face.select = True self._blender_object.active_material_index = mat_ind bpy.ops.object.material_slot_assign() bpy.ops.mesh.select_all(action='DESELECT') # bmesh.update_edit_mesh(self._blender_object.data) bpy.ops.object.mode_set(mode='OBJECT')