Source code for goo.utils

from functools import reduce
from typing import Union

import bpy
import bmesh
from bpy.types import Modifier, ClothModifier
from mathutils import *


[docs] class BlenderObject: def __init__(self, obj: bpy.types.Object): self.obj = obj @property def name(self): return self.obj.name @name.setter def name(self, name): self.obj.name = name @property def loc(self): return self.obj.location @loc.setter def loc(self, loc): self.obj.location = loc
[docs] def hide(self): self.obj.hide_set(True)
# ----- CUSTOM PROPERTIES ----- def __setitem__(self, k: str, v: Union[float, list[float], int, list[int], str]): self.obj[k] = v def __contains__(self, k: str): return k in self.obj.keys() def __getitem__(self, k): return self.obj[k]
[docs] class Axis: def __init__(self, axis, start, end, world_matrix): """ :param axis: Vector of axis :param start: Vector of start endpoint in object space :param end: Vector of end endpoint in object space :param world_matrix: 4x4 matrix of object to world transformation :param local_coords: if coordinates given are in local space """ self._axis = axis self._start = start self._end = end self._matrix_world = world_matrix
[docs] def axis(self, local_coords=False): if local_coords: axis = self._axis.copy() axis.rotate(self._matrix_world.to_quaternion().inverted()) return axis return self._axis
[docs] def endpoints(self, local_coords=False): mat = self._matrix_world.inverted() if local_coords else Matrix.Identity(4) return [mat @ self._start, mat @ self._end]
[docs] def length(self, local_coords=False): start, end = self.endpoints(local_coords) return (end - start).length
# ----- BLENDER FUNCTIONS -----
[docs] def create_mesh( name, loc, mesh="icosphere", size=1.5, rotation=(0, 0, 0), scale=(1, 1, 1), subdivisions=2, **kwargs, ): bm = bmesh.new() if isinstance(rotation, tuple): rotation = Euler(rotation) elif isinstance(rotation, Quaternion): rotation = rotation.to_euler() match mesh: case "icosphere": bmesh.ops.create_icosphere( bm, subdivisions=subdivisions, radius=size, **kwargs ) # bmesh.ops.beautify_fill(bm, faces=bm.faces, edges=bm.edges) bmesh.ops.triangulate(bm, faces=bm.faces[:]) case "plane": bmesh.ops.create_grid( bm, x_segments=1, y_segments=1, size=size / 2, **kwargs ) case "monkey": bmesh.ops.create_monkey(bm, **kwargs) case "cube": bmesh.ops.create_cube(bm, size=size, **kwargs) # bmesh.ops.beautify_fill(bm, faces=bm.faces, edges=bm.edges) # bmesh.ops.triangulate(bm, faces=bm.faces[:]) case _: raise ValueError( """mesh must be one of "icosphere", "plane", "monkey" or "cube".""" ) me = bpy.data.meshes.new(f"{name}_mesh") bm.to_mesh(me) bm.free() obj = bpy.data.objects.new(name, me) obj.location = loc obj.rotation_euler = rotation obj.scale = scale return obj
[docs] def create_material(name, color): # Create a new material mat = bpy.data.materials.new(name=name) r, g, b = color mat.diffuse_color = (r, g, b, 1.0) # Set material properties to create a matte finish mat.metallic = 1.0 # No metallic reflection mat.roughness = 1.0 # Maximum roughness for a matte finish mat.specular_intensity = 1.0 # No specular reflection mat.use_nodes = False # Ensure nodes are not used return mat
# --- PHYSICS MODIFIER CONSTRUCTORS ---
[docs] class PhysicsConstructor: def __init__(self, *mod_contructors: "ModConstructor"): self.mod_constructors = [*mod_contructors] def __call__(self, bobj: BlenderObject): for mod_constructor in self.mod_constructors: mod_constructor().construct(bobj.obj)
[docs] class ModConstructor: name = "" type = ""
[docs] def construct(self, obj): mod = obj.modifiers.new(name=self.name, type=self.type) self.setup_mod(mod)
[docs] def setup_mod(self, mod: Modifier): pass
[docs] class ClothConstructor(ModConstructor): name = "Cloth" type = "CLOTH"
[docs] def setup_mod(self, mod: ClothModifier): stiffness = 1 pressure = 0.01 mod.settings.quality = 10 mod.settings.air_damping = 10 mod.settings.bending_model = "ANGULAR" mod.settings.mass = 1 mod.settings.time_scale = 1 mod.point_cache.frame_start = bpy.context.scene.frame_start mod.point_cache.frame_end = bpy.context.scene.frame_end # Cloth > Stiffness mod.settings.tension_stiffness = stiffness mod.settings.compression_stiffness = stiffness mod.settings.shear_stiffness = stiffness mod.settings.bending_stiffness = 1 # Cloth > Damping mod.settings.tension_damping = 50 mod.settings.compression_damping = 50 mod.settings.shear_damping = 50 mod.settings.bending_damping = 0.5 # Cloth > Pressure mod.settings.use_pressure = True mod.settings.uniform_pressure_force = pressure mod.settings.use_pressure_volume = True mod.settings.target_volume = 1 mod.settings.pressure_factor = 2 # mod.settings.fluid_density = 1.05 # Cloth > Collisions mod.collision_settings.collision_quality = 4 mod.collision_settings.use_collision = True mod.collision_settings.use_self_collision = True mod.collision_settings.self_friction = 0 mod.collision_settings.friction = 0 mod.collision_settings.self_distance_min = 0.01 mod.collision_settings.distance_min = 0.01 mod.collision_settings.self_impulse_clamp = 100 mod.collision_settings.impulse_clamp = 100 # Cloth > Field Weights mod.settings.effector_weights.gravity = 0
[docs] class SimpleClothConstructor(ClothConstructor):
[docs] def setup_mod(self, mod): super().setup_mod(mod) mod.settings.mass = 10
[docs] class YolkClothConstructor(ClothConstructor):
[docs] def setup_mod(self, mod: ClothModifier): stiffness = 5 pressure = 5.2 mod.settings.quality = 10 mod.settings.air_damping = 10 mod.settings.bending_model = "ANGULAR" mod.settings.mass = 2 mod.settings.time_scale = 1 # Cloth > Stiffness mod.settings.tension_stiffness = stiffness mod.settings.compression_stiffness = stiffness mod.settings.shear_stiffness = stiffness mod.settings.bending_stiffness = stiffness # Cloth > Damping mod.settings.tension_damping = 50 mod.settings.compression_damping = 50 mod.settings.shear_damping = 50 mod.settings.bending_damping = 0.5 # Cloth > Internal Springs mod.settings.use_internal_springs = True mod.settings.internal_spring_max_length = 1 mod.settings.internal_spring_max_diversion = 0.785398 mod.settings.internal_spring_normal_check = False mod.settings.internal_tension_stiffness = 10000 mod.settings.internal_compression_stiffness = 10000 mod.settings.internal_tension_stiffness_max = 10000 mod.settings.internal_compression_stiffness_max = 10000 # Cloth > Pressure mod.settings.use_pressure = True mod.settings.uniform_pressure_force = pressure mod.settings.use_pressure_volume = True mod.settings.target_volume = 1 mod.settings.pressure_factor = 2 mod.settings.fluid_density = 1.15 # Cloth > Collisions mod.collision_settings.collision_quality = 10 mod.collision_settings.use_collision = True mod.collision_settings.use_self_collision = True mod.collision_settings.self_friction = 0 mod.collision_settings.friction = 0 mod.collision_settings.self_distance_min = 0.005 mod.collision_settings.distance_min = 0.005 mod.collision_settings.self_impulse_clamp = 0
[docs] class CollisionConstructor(ModConstructor): name = "Collision" type = "COLLISION"
[docs] def setup_mod(self, mod: bpy.types.CollisionModifier): mod.settings.use_culling = True mod.settings.damping = 1 mod.settings.thickness_outer = 0.025 mod.settings.thickness_inner = 0.25 mod.settings.cloth_friction = 0 mod.settings.use_normal = False
[docs] class BoundaryCollisionConstructor(CollisionConstructor):
[docs] def setup_mod(self, mod: bpy.types.CollisionModifier): super(BoundaryCollisionConstructor, self).setup_mod(mod) mod.settings.use_culling = False
[docs] class SubsurfConstructor(ModConstructor): name = "Subdivision" type = "SUBSURF"
[docs] def setup_mod(self, mod: bpy.types.SubsurfModifier): mod.subdivision_type = "CATMULL_CLARK" mod.levels = 1 mod.render_levels = 1
# not stable
[docs] class RemeshConstructor(ModConstructor): name = "Remesh" type = "REMESH"
[docs] def setup_mod(self, mod: bpy.types.RemeshModifier): mod.mode = "VOXEL" mod.voxel_size = 0.75 mod.adaptivity = 0 mod.use_remove_disconnected = True mod.use_smooth_shade = True mod.show_in_editmode = True