Skip to content

Instantly share code, notes, and snippets.

@behreajj
Created May 20, 2021 19:50
Show Gist options
  • Save behreajj/a08c07d5eb3c41ec0ec2a6f67c0cc210 to your computer and use it in GitHub Desktop.
Save behreajj/a08c07d5eb3c41ec0ec2a6f67c0cc210 to your computer and use it in GitHub Desktop.
Distorted Suzanne with Shape Keys
import bpy
import bmesh
import math
from mathutils import noise, Quaternion, Vector
# Create a Suzanne (monkey head); subdivide.
bm = bmesh.new()
bmesh.ops.create_monkey(bm)
bmesh.ops.subdivide_edges(
bm,
edges=bm.edges,
cuts=2,
smooth=0.75,
smooth_falloff='SMOOTH',
use_grid_fill=True)
mesh_data = bpy.data.meshes.new("Suzanne")
bm.to_mesh(mesh_data)
bm.free()
# Append an orange material.
mat = bpy.data.materials.new(name="Material")
mat.diffuse_color = (1.0, 0.25, 0.0, 1.0)
mesh_data.materials.append(mat)
# Link object to scene.
suzanne = bpy.data.objects.new("Suzanne", mesh_data)
bpy.context.collection.objects.link(suzanne)
# Add subdivide modifier.
subdiv = suzanne.modifiers.new("Subsurf", 'SUBSURF')
subdiv.levels = 2
# Add shape key blocks.
basis_sk = suzanne.shape_key_add(name="Basis")
sphere_sk = suzanne.shape_key_add(name="Sphere")
offset_sk = suzanne.shape_key_add(name="Offset")
# Sphere shape key block.
for sk in sphere_sk.data:
sk.co = sk.co.normalized()
# Custom offset shape key block.
co_len = len(offset_sk.data)
half_len = co_len // 2
i_range = range(0, co_len, 1)
sk_data = offset_sk.data
i_to_prc = 1.0 / (co_len - 1.0)
pivot = Vector((0.0, 2.0, 0.0))
axis_central = Vector((0.0, 0.0, 1.0))
axis_left = Vector((0.0, 0.5, -1.0)).normalized()
axis_right = Vector((0.0, -1.0, 0.5)).normalized()
range_limit = 1.0 / 16.0
for i in i_range:
sk = sk_data[i]
sk_co = sk.co
i_prc = i * i_to_prc
theta = 0.0
if i % 2 != 0:
theta = i_prc * math.pi * range_limit
else:
theta = i_prc * -math.pi * range_limit
axis = axis_central
if sk_co.x > 0.0:
axis = axis_left
elif sk_co.x < -0.0:
axis = axis_right
q = Quaternion(axis, theta)
co = sk.co - pivot
co = q @ co
co = co + pivot
sk.co = co
# Cache scene frame start and end.
scene = bpy.context.scene
frame_start = scene.frame_start
frame_end = scene.frame_end
# Set animation at frame start.
scene.frame_set(frame_start)
sphere_sk.value = 0.0
offset_sk.value = 0.0
sphere_sk.keyframe_insert(data_path="value")
offset_sk.keyframe_insert(data_path="value")
# Set animation at frame end.
scene.frame_set(frame_end)
sphere_sk.value = 0.0
offset_sk.value = 0.0
sphere_sk.keyframe_insert(data_path="value")
offset_sk.keyframe_insert(data_path="value")
# Set animation at 1/4.
scene.frame_set(frame_start + (frame_end - frame_start) // 4)
sphere_sk.value = 1.0
offset_sk.value = 0.0
sphere_sk.keyframe_insert(data_path="value")
offset_sk.keyframe_insert(data_path="value")
# Set animation at 1/2.
scene.frame_set(frame_start + (frame_end - frame_start) // 2)
sphere_sk.value = 0.5
offset_sk.value = 0.5
sphere_sk.keyframe_insert(data_path="value")
offset_sk.keyframe_insert(data_path="value")
# Set animation at 3/4.
scene.frame_set(frame_start + (frame_end - frame_start) * 3 // 4)
sphere_sk.value = 0.0
offset_sk.value = 1.0
sphere_sk.keyframe_insert(data_path="value")
offset_sk.keyframe_insert(data_path="value")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment