Python Skripte aus der Vorlesung
VL5 Animation - Springende Kugel
Code-Zwischenstand aus der Vorlesung vom 20.04.21
import bpy
import math
points = bpy.data.collections["points"].objects
sphere: bpy.types.Object = bpy.data.objects["Sphere"]
JUMP_HEIGHT = 5
FRAMES_PER_UNIT = 1.5
def get_distance(p1, p2):
return math.sqrt((p1[0] - p2[0]) ** 2 + (p1[1] - p2[1]) ** 2 + (p1[2] - p2[2]) ** 2)
def set_handle_types():
for c_curve in sphere.animation_data.action.fcurves:
for i, c_keyframe in enumerate(c_curve.keyframe_points):
if i % 2 == 0:
c_keyframe.handle_left_type = "VECTOR"
c_keyframe.handle_right_type = "VECTOR"
c_curve.update()
sphere.location = points[0].location
sphere.keyframe_insert(data_path="location", frame=0)
at_keyframe = 0
for i in range(1, len(points)):
distance_to_last_point = get_distance(points[i-1].location, points[i].location)
keyframe_loc = at_keyframe + distance_to_last_point * FRAMES_PER_UNIT
intermediate_keyframe_location = at_keyframe + (distance_to_last_point * FRAMES_PER_UNIT) / 2
at_keyframe = keyframe_loc
sphere.location = points[i].location
sphere.keyframe_insert(data_path="location", frame=keyframe_loc)
intermediate_point = points[i-1].location + points[i].location / 2
intermediate_point.z += JUMP_HEIGHT
sphere.location = intermediate_point
sphere.keyframe_insert(data_path="location", frame=intermediate_keyframe_location)
set_handle_types()
VL4 Meshgenerierung - Graßbüschel
Code-Zwischenstand aus der Vorlesung vom 14.04.21
import bpy
import bmesh
import math
import mathutils
import random
BLADES = 8
WIDTH_MAX = 0.6
WIDTH_MIN = 0.03
HEIGHT_MIN = 4
HEIGHT_MAX = 12
ROT_BASE_MIN = 3
ROT_BASE_MAX = 25
ROT_TIP_MIN = 30
ROT_TIP_MAX = 90
ROT_FALLOFF = 5
# Szene leeren
bpy.ops.object.select_all(action='SELECT') # selektiert alle Objekte
bpy.ops.object.delete(use_global=False, confirm=False) # löscht selektierte objekte
bpy.ops.outliner.orphans_purge() # löscht überbleibende Meshdaten etc.
# Mesh und Objekt erstellen
grass_mesh = bpy.data.meshes.new("grass_shrub_mesh")
grass_object = bpy.data.objects.new("grass shrub", grass_mesh)
# Mesh in aktuelle Collection der Szene verlinken
bpy.context.collection.objects.link(grass_object)
#
bm = bmesh.new()
bm.from_mesh(grass_mesh)
def map_range(v, from_min, from_max, to_min, to_max):
"""Bringt einen Wert v von einer Skala (from_min, from_max) auf eine neue Skala (to_min, to_max)"""
return to_min + (v - from_min) * (to_max - to_min) / (from_max - from_min)
for i in range(BLADES):
# Zufällige Werte für jedes Blatt generieren
c_height = random.randrange(HEIGHT_MIN, HEIGHT_MAX)
c_blade = []
c_rot_base = random.uniform(ROT_BASE_MIN, ROT_BASE_MAX)
c_rot_tip = random.uniform(ROT_TIP_MIN, ROT_TIP_MAX)
last_vert_1 = None
last_vert_2 = None
for i in range(c_height):
progress = i / c_height
v = math.pow(progress, 0.8)
pos_x = map_range(v, 0, 1, WIDTH_MAX, WIDTH_MIN)
vert_1 = bm.verts.new((-pos_x,0,i))
vert_2 = bm.verts.new((pos_x,0,i))
# Halm immer weiter biegen desto weiter oben wir uns im Mesh/Loop befinden
rot_angle = map_range(math.pow(progress, ROT_FALLOFF), 0, 1, c_rot_base, c_rot_tip)
rot_matrix = mathutils.Matrix.Rotation(math.radians(rot_angle), 4, 'X')
bmesh.ops.rotate(bm, cent=(0, 0, 0), matrix=rot_matrix, verts=[vert_1, vert_2])
# Generierung des Polygons in erster Stufe überspringen (weil bisher nur 2 Verices bestehen)
if i is not 0:
bm.faces.new((last_vert_1,last_vert_2,vert_2,vert_1))
# Vertices der Vertices-Liste des aktuellen Halms hinzufügen
c_blade.append(vert_1)
c_blade.append(vert_2)
# Letzte Vertices speichern, um sie für die generierung des nächsten Polygons zu verwenden
last_vert_1 = vert_1
last_vert_2 = vert_2
# Jeden Halm zufällig auf Z Achse rotieren
random_angle = random.randrange(0, 360)
rot_matrix_blade = mathutils.Matrix.Rotation(math.radians(random_angle), 4, 'Z')
# Dabei jeden Vertex des aktuellen Halms rotieren
for v in c_blade:
bmesh.ops.rotate(bm, cent=(0, 0, 0), matrix=rot_matrix_blade, verts=[v])
# BMesh auf Mesh anwenden und abschließen
bm.to_mesh(grass_mesh)
bm.free()
VL2 Turmgenerator
Code-Zwischenstand aus der Vorlesung am 23.03.21
import bpy
import typing
import math
class tower():
tower_radius: float = 3
tower_height: float = 8
roof_height: float = 2.5
roof_overhang: float = 0.5
windows_num_circular: int = 5
windows_num_vertical: int = 3
windows_size: float = 1
wall_thickness: float = 0.5
PI2 = math.pi * 2
def generate_windows(self) -> object:
windows = []
for i in range(self.windows_num_circular):
bpy.ops.mesh.primitive_cube_add(
scale=(self.windows_size, self.windows_size, self.windows_size * 1.5)
)
c_window = bpy.context.object
c_window.location = (
(math.cos(i / self.windows_num_circular * self.PI2) * self.tower_radius),
(math.sin(i / self.windows_num_circular * self.PI2) * self.tower_radius),
1
)
if i > 0:
c_window.rotation_euler.z = self.PI2 * i / self.windows_num_circular
windows.append(c_window)
for c_window in windows:
c_window.select_set(True)
bpy.ops.object.join()
window = bpy.context.object
modifier_array = window.modifiers.new("Window Array", "ARRAY")
modifier_array.use_relative_offset = False
modifier_array.use_constant_offset = True
modifier_array.count = self.windows_num_vertical
modifier_array.constant_offset_displace = (0, 0, 2.5)
bpy.context.object.display_type = 'WIRE'
return bpy.context.object
def create_roof_material(self) -> bpy.types.Material:
mat_roof: bpy.types.Material = bpy.data.materials.new("Roof Material")
mat_roof.use_nodes = True
nodes_roof: typing.List[bpy.types.Node] = mat_roof.node_tree.nodes
nodes_roof["Principled BSDF"].inputs[0].default_value = [0.103312, 0.236954, 0.177671, 1.000000]
return mat_roof
def create_tower_material(self) -> bpy.types.Material:
mat_tower: bpy.types.Material = bpy.data.materials.new("Tower Material")
mat_tower.use_nodes = True
nodes_tower: typing.List[bpy.types.Node] = mat_tower.node_tree.nodes
node_bricks: bpy.types.Node = nodes_tower.new("ShaderNodeTexBrick")
node_coords: bpy.types.Node = nodes_tower.new("ShaderNodeTexCoord")
node_bricks.inputs[1].default_value = (0.1,0.1,0.1,0.1)
node_bricks.inputs[4].default_value = 10
mat_tower.node_tree.links.new(node_bricks.outputs[0], nodes_tower["Principled BSDF"].inputs[0])
mat_tower.node_tree.links.new(node_coords.outputs[2], node_bricks.inputs[0])
return mat_tower
def generate_tower(self):
bpy.ops.mesh.primitive_cylinder_add(
location=(0, 0, self.tower_height / 2),
depth=self.tower_height,
radius=self.tower_radius)
tower_base = bpy.context.object
bpy.context.object.data.materials.append(self.create_tower_material())
bpy.ops.mesh.primitive_cone_add(
depth=self.roof_height,
location=(0, 0, self.tower_height + self.roof_height / 2),
radius1=self.tower_radius + self.roof_overhang
)
bpy.context.object.data.materials.append(self.create_roof_material())
windows = self.generate_windows()
modifier_solidify = tower_base.modifiers.new("Wall Thickness", "SOLIDIFY")
modifier_solidify.thickness = self.wall_thickness
modifier_bool = tower_base.modifiers.new("Window Bool", "BOOLEAN")
modifier_bool.object = windows
bpy.ops.object.select_all(action='SELECT') # selektiert alle Objekte
bpy.ops.object.delete(use_global=False, confirm=False) # löscht selektierte objekte
bpy.ops.outliner.orphans_purge() # löscht überbleibende Meshdaten etc.
t = tower()
t.generate_tower()