Created
January 20, 2021 14:15
-
-
Save ylegall/2a873a27201fb3034cba12b5c7924e17 to your computer and use it in GitHub Desktop.
blender python script for https://www.instagram.com/p/CKOr41rnpft/?utm_source=ig_web_copy_link
This file contains hidden or 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 | |
from mathutils import Matrix, Vector, noise, kdtree | |
from math import sin, cos, tau, pi, sqrt | |
import random | |
from utils import * | |
from enum import Enum | |
from easing_functions import * | |
frame_start = 1 | |
total_frames = 345 | |
bpy.context.scene.render.fps = 30 | |
bpy.context.scene.frame_start = frame_start | |
bpy.context.scene.frame_end = total_frames | |
random.seed(1) | |
rows = 18 | |
cols = 8 | |
tile_size = 1.8 | |
tile_width = sqrt(3.0) * tile_size | |
tile_height = 2.0 * tile_size | |
max_height = rows * tile_height * 0.75 | |
print(f'max height: {max_height}') | |
normal_color = hex_to_srgb(0x1a1829) + (1.0,) | |
colors = [ | |
hex_to_srgb(0xe94f37) + (1.0,), | |
hex_to_srgb(0xF1EAC9) + (1.0,), | |
hex_to_srgb(0x87f6fe) + (1.0,), | |
hex_to_srgb(0x6883ba) + (1.0,), | |
] | |
def hex_verts(t: float, row: int, col: int): | |
c_y = row * tile_height * 0.75 + (t * max_height) | |
c_y %= max_height | |
c_x = (row % 2) * tile_width * 0.5 + col * tile_width | |
return [ | |
(c_x - 0.5 * tile_width, c_y + 0.25 * tile_height, 0.0), | |
(c_x, c_y + 0.5 * tile_height, 0.0), | |
(c_x + 0.5 * tile_width, c_y + 0.25 * tile_height, 0.0), | |
(c_x + 0.5 * tile_width, c_y - 0.25 * tile_height, 0.0), | |
(c_x, c_y - 0.5 * tile_height, 0.0), | |
(c_x - 0.5 * tile_width, c_y - 0.25 * tile_height, 0.0), | |
] | |
# base_tiles = [hex_verts(row, col) for row in range(rows) for col in range(cols)] | |
num_tiles = rows * cols | |
material_offsets = [random.random() for i in range(num_tiles)] | |
tile_color_offsets = [random.random() for _ in range(num_tiles)] | |
def centroid(verts): | |
total = Vector((0.0, 0.0, 0.0)) | |
for vert in verts: | |
total += Vector(vert) | |
return total / len(verts) | |
def offset_vertex(t: float, x: float, y: float, z: float): | |
magnitude = smoothstep(0.20, 0.75 * tile_height * rows, y) | |
x_offset = magnitude * (0.6 * sin(tau * (t + y/20.0))) | |
noise_angle = 2 * tau * t | |
noise_radius = 1.0 | |
noise_scale = 0.5 | |
noise_pos = noise_scale * (Vector((x, y, z)) + noise_radius * Vector((cos(noise_angle), sin(noise_angle), 0.0))) | |
noise_offset = 0.65 * noise.noise_vector(noise_pos, noise_basis='BLENDER') | |
noise_offset *= magnitude | |
return x + noise_offset.x + x_offset, y + noise_offset.y, z | |
def extrude_tile(t: float, tile_index: int, face: bmesh.types.BMFace, center: Vector, bm: bmesh.types.BMesh): | |
magnitude = smoothstep(0.20, 0.75 * tile_height * rows, center.y) | |
noise_angle = 2 * tau * t | |
noise_radius = 1.0 | |
noise_scale = 0.3 | |
noise_pos = noise_scale * (center + noise_radius * Vector((cos(noise_angle), sin(noise_angle), 0.0))) | |
noise_height = 0.5 + 0.5 * noise.noise(noise_pos, noise_basis='BLENDER') | |
wave_height = 0.3 * sin(2 * tau * (-t + center.y / 40)) | |
height = 1.0 + wave_height + magnitude * noise_height | |
bmesh.ops.translate(bm, vec=Vector((0.0, 0.0, height)), verts=face.verts) | |
for vert in face.verts: | |
vert.co = vert.co.lerp(center, magnitude * 0.45) | |
def mesh_geometry(t: float): | |
base_tiles = [hex_verts(t, row, col) for row in range(rows) for col in range(cols)] | |
all_verts = [v for tile in base_tiles for v in tile] | |
for idx, vert in enumerate(all_verts): | |
all_verts[idx] = offset_vertex(t, *vert) | |
faces = [[j for j in range(i, i+6)] for i in range(0, len(all_verts), 6)] | |
mesh = bpy.data.meshes.new('tiles') | |
mesh.from_pydata(vertices=all_verts, edges=[], faces=faces) | |
# set materials | |
metal_material = bpy.data.materials.get('metal') | |
#light_material = bpy.data.materials.get('light') | |
mesh.materials.append(metal_material) | |
#mesh.materials.append(light_material) | |
color_layer = mesh.vertex_colors.get('rand_color') or mesh.vertex_colors.new(name='rand_color') | |
for i, poly in enumerate(mesh.polygons): | |
#poly.material_index = 0 if material_offsets[i] < 0.9 else 1 | |
# rgba = [random.random() for _ in range(3)] + [1.0] | |
c1 = mix_colors(colors, tile_color_offsets[i]) | |
for idx in poly.loop_indices: | |
color_layer.data[idx].color = mix_rgb(normal_color, c1, smoothstep(0.20, 0.75 * tile_height * rows, poly.center.y)) | |
poly.material_index = 0 | |
bm = bmesh.new() | |
bm.from_mesh(mesh) | |
original_faces = bm.faces[:] | |
for i, face in enumerate(original_faces): | |
face.normal_update() | |
# center = centroid(face.verts) | |
center = face.calc_center_bounds() | |
ext = bmesh.ops.extrude_discrete_faces(bm, faces=[face]) | |
new_faces = ext["faces"] | |
for new_face in new_faces: | |
new_face.normal_update() | |
norm = new_face.normal.normalized() | |
if abs(norm.z) > 0.9: | |
extrude_tile(t, i, new_face, center, bm) | |
geom = bm.verts[:] + bm.edges[:] + bm.faces[:] | |
bmesh.ops.bevel( | |
bm, | |
geom=geom, | |
affect='EDGES', | |
offset_type='OFFSET', | |
offset=0.06, | |
segments=3, | |
profile=0.5, | |
material=-1 | |
) | |
for face in bm.faces: | |
face.smooth = True | |
bm.to_mesh(mesh) | |
bm.free() | |
return mesh | |
def setup(): | |
collection = bpy.data.collections.get('generated') | |
if not collection: | |
collection = bpy.data.collections.new('generated') | |
bpy.context.scene.collection.children.link(collection) | |
tiles_obj = bpy.data.objects.get('tiles') | |
if tiles_obj: | |
mesh = mesh_geometry(0.0) | |
old_mesh = tiles_obj.data | |
tiles_obj.data = mesh | |
bpy.data.meshes.remove(old_mesh, do_unlink=True) | |
else: | |
mesh = mesh_geometry(0.0) | |
tiles_obj = bpy.data.objects.new('tiles', mesh) | |
collection.objects.link(tiles_obj) | |
def frame_update(scene): | |
frame = scene.frame_current | |
t = (((frame - 1) % total_frames) + 1) / float(total_frames) | |
obj = bpy.data.objects.get('tiles') | |
new_mesh = mesh_geometry(t) | |
old_mesh = obj.data | |
obj.data = new_mesh | |
bpy.data.meshes.remove(old_mesh, do_unlink=True) | |
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