Created
February 11, 2021 19:38
-
-
Save ylegall/155db9496e43369ce4f752303b16a413 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import bpy | |
import bmesh | |
import random | |
from mathutils import Vector, noise, Matrix | |
from math import sin, cos, tau, pi, modf | |
from utils.interpolation import * | |
from utils.math import * | |
from utils.color import * | |
from easing_functions import QuadEaseOut | |
# https://sinestesia.co/blog/tutorials/python-cube-matrices/ | |
frame_start = 1 | |
total_frames = 16 * 30 | |
bpy.context.scene.render.fps = 30 | |
bpy.context.scene.frame_start = frame_start | |
bpy.context.scene.frame_end = total_frames | |
random.seed(7) | |
size = 2 | |
max_levels = 2 | |
padding = 0.961 | |
colors1 = [0xF6E8F3, 0xF6E8F3, 0xeebe96, 0xef709d, 0x4b4e6d, 0x66DAB0] | |
colors2 = [0xe0fbfc, 0xd9cc98, 0x293241, 0x3d5a80, 0xe0fbfc, 0xee6c4d] | |
material_assignments = [random.randrange(len(colors1)) for i in range(8**max_levels)] | |
# splits = [random.uniform(0.1, 0.9) for _ in range(8**max_levels)] | |
splits = {} | |
for i in range(8**max_levels): | |
split_values = [random.uniform(0.15, 0.85) for _ in range(6)] | |
split_values += split_values[:1] | |
splits[i] = split_values | |
def setup(): | |
col = bpy.data.collections.get('generated') | |
if not col: | |
col = bpy.data.collections.new('generated') | |
bpy.context.scene.collection.children.link(col) | |
cubes_obj = bpy.data.objects.new('cubes', bpy.data.meshes.new('cube-mesh')) | |
col.objects.link(cubes_obj) | |
bpy.ops.mesh.primitive_cube_add(size=2.0) | |
base_cube = bpy.context.object | |
base_cube.name = 'base_cube' | |
base_cube.hide_render = True | |
base_cube.hide_viewport = True | |
col.objects.link(base_cube) | |
bpy.context.scene.collection.objects.unlink(base_cube) | |
for i, color in enumerate(colors1): | |
material = bpy.data.materials.new(f'mat-{i}') | |
material.diffuse_color = hex_to_rgb(color) | |
material.roughness = 0.35 | |
material.specular_intensity = 0.7 | |
def get_cube_center(start: Vector, end: Vector) -> Matrix: | |
return Matrix.Translation(start.lerp(end, 0.5)) | |
def get_cube_scale(start: Vector, end: Vector) -> Matrix: | |
scale_values = ((end - start) * 0.5) * padding | |
return scale_matrix_from_vec3(scale_values) | |
def get_noise_value(split_id: int, t: float) -> float: | |
tf, ti = modf(t * 6) | |
t1 = (ti + QuadEaseOut().ease(smoothstep(0.15, 0.65, tf))) / 6 | |
# noise_pos1 = Vector((axis * 3, axis * 5, axis * 7)) | |
# noise_pos2 = polar(tau * t1, 1.3) | |
# return 0.5 + 0.3 * noise.noise(noise_pos1 + noise_pos2, noise_basis='BLENDER') | |
return mix_values(splits[split_id], t1) | |
# return 0.5 | |
def update_mesh_recursive( | |
t: float, | |
start_coord: Vector, | |
end_coord: Vector, | |
axis: int, | |
level: int, | |
seed: [int], | |
transforms | |
): | |
if level >= max_levels: | |
translation = get_cube_center(start_coord, end_coord) | |
scale_matrix = get_cube_scale(start_coord, end_coord) | |
transforms.append(translation @ scale_matrix) | |
else: | |
start = start_coord[axis] | |
end = end_coord[axis] | |
# split_pct = get_noise_value(axis + level * 3, t) | |
split_pct = get_noise_value(seed[0], t) | |
split = mix(start, end, split_pct) | |
seed[0] += 1 | |
new_start = start_coord.copy() | |
new_start[axis] = split | |
new_end = end_coord.copy() | |
new_end[axis] = split | |
new_level = level + 1 if axis == 2 else level | |
new_axis = (axis + 1) % 3 | |
update_mesh_recursive(t, start_coord.copy(), new_end, new_axis, new_level, seed, transforms) | |
update_mesh_recursive(t, new_start, end_coord.copy(), new_axis, new_level, seed, transforms) | |
# update_mesh_recursive(t, start_coord.copy(), new_end, new_axis, new_level, seed * 2 + 1, transforms) | |
# update_mesh_recursive(t, new_start, end_coord.copy(), new_axis, new_level, seed * 2 + 2, transforms) | |
def update_mesh(t: float) -> bpy.types.Mesh: | |
transforms = [] | |
start_coord = Vector((-size, -size, -size)) | |
end_coord = Vector((size, size, size)) | |
update_mesh_recursive(t, start_coord, end_coord, 0, 0, [0], transforms) | |
bm = bmesh.new() | |
base_cube = bpy.data.objects.get('base_cube') | |
new_mesh = bpy.data.meshes.new('cube-mesh') | |
for i in range(len(colors1)): | |
new_mesh.materials.append(bpy.data.materials.get(f'mat-{i}')) | |
for i, tx in enumerate(transforms): | |
cube_mesh = base_cube.data.copy() | |
cube_mesh.transform(tx) | |
for face in cube_mesh.polygons: | |
face.material_index = material_assignments[i] | |
face.use_smooth = True | |
bm.from_mesh(cube_mesh) | |
bpy.data.meshes.remove(cube_mesh) | |
bmesh.ops.bevel(bm, | |
geom=bm.edges, | |
offset=0.037, | |
offset_type='OFFSET', | |
segments=3, | |
profile=0.5, | |
affect='EDGES', | |
material=-1 | |
) | |
bm.to_mesh(new_mesh) | |
bm.free() | |
return new_mesh | |
def get_cubes_rotation(t: float): | |
p2 = pi / 2 | |
t1 = t * 6 | |
rx = p2 * smoothstep(0.8, 1.0, t1) | |
ry = p2 * smoothstep(1.8, 2.0, t1) | |
rz = p2 * smoothstep(2.8, 3.0, t1) | |
ry = ry - p2 * smoothstep(5.8, 6.0, t1) | |
rx = rx - p2 * smoothstep(3.8, 4.0, t1) | |
rz = rz - p2 * smoothstep(4.8, 5.0, t1) | |
return rx, ry, rz | |
def update_material_colors(t: float): | |
t1 = (t * 4) % 1.0 | |
pct = smoothstep(0.1, 0.3, t1) - smoothstep(0.6, 0.9, t1) | |
for i in range(len(colors1)): | |
mat = bpy.data.materials.get(f'mat-{i}') | |
mat.diffuse_color = mix_rgb(hex_to_rgb(colors1[i]), hex_to_rgb(colors2[i]), pct) | |
def frame_update(scene): | |
frame = scene.frame_current | |
t = (((frame - 1) % total_frames) + 1) / float(total_frames) | |
update_material_colors(t) | |
new_mesh = update_mesh(t) | |
cubes_obj = bpy.data.objects.get('cubes') | |
old_mesh = cubes_obj.data | |
cubes_obj.data = new_mesh | |
if old_mesh: | |
bpy.data.meshes.remove(old_mesh, do_unlink=True) | |
cubes_obj.rotation_euler = get_cubes_rotation(t) | |
setup() | |
bpy.app.handlers.frame_change_pre.clear() | |
bpy.app.handlers.frame_change_pre.append(frame_update) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment