Skip to content

Instantly share code, notes, and snippets.

@ylegall
Created January 20, 2021 14:15
Show Gist options
  • Save ylegall/2a873a27201fb3034cba12b5c7924e17 to your computer and use it in GitHub Desktop.
Save ylegall/2a873a27201fb3034cba12b5c7924e17 to your computer and use it in GitHub Desktop.
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