Created
November 10, 2022 13:14
-
-
Save blender8r/92a5b9fa3d5f1b9fdfe5e0039fe4766b to your computer and use it in GitHub Desktop.
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 | |
| from math import tau, pi, sin, cos, ceil | |
| import random | |
| import time | |
| t = time.perf_counter() | |
| num_points = 48 | |
| center = (0.0, 0.0, 0.0) | |
| num_rings = 3 | |
| num_leading = 2 | |
| num_trailing = 1 | |
| steps_per_ring = 6 | |
| num_peaks = num_rings + num_leading + num_trailing | |
| total_num_rings = num_peaks * steps_per_ring | |
| wave_compression = 0.3 | |
| taper_strength = 0.6 | |
| optimize_threshold = 0.025 | |
| sharpness = 4.0 | |
| strength_min = 0.0 | |
| strength_max = 0.75 | |
| angle = pi + (pi/2) | |
| angle_step = (tau * num_peaks) / total_num_rings | |
| peak_color = (1.0, 1.0, 1.0, 1.0) #RGBA | |
| start_frame = 1 | |
| duration = 100 | |
| fade_duration = 100 | |
| speed = 0.14 | |
| fps = 24 | |
| rate = speed / fps | |
| def get_frame(frames, frame_num): | |
| '''Checks for the existence of a given frame''' | |
| frame = None | |
| for item in frames: | |
| if item.frame_number == frame_num: | |
| frame = item | |
| break | |
| return frame | |
| def norm(value, min, max): | |
| return (value - min) / (max - min) | |
| def remap(value, min1, max1, min2, max2): | |
| return (value-min1) / (max1-min1) * (max2-min2) + min2 | |
| def ellipse(gp_frame, center, radius_x, radius_z, num_points, line_thickness): | |
| gp_stroke = gp_frame.strokes.new() | |
| gp_stroke.line_width = line_thickness | |
| gp_stroke.use_cyclic = True | |
| gp_stroke.points.add(count=num_points) | |
| step = tau / num_points | |
| for i in range(num_points): | |
| angle = i * step | |
| x = center[0] + (radius_x * cos(angle)) | |
| y = 0 | |
| z = center[2] + (radius_z* sin(angle)) | |
| gp_stroke.points[i].co = (x, y, z) | |
| return gp_stroke | |
| def set_ring_color(stroke, color=(1.0, 1.0, 1.0, 1.0), strength=1.0): | |
| for p in stroke.points: | |
| p.strength = strength | |
| p.vertex_color = color | |
| def build_rings(gp_frame, center, size, num_rings, num_leading, num_trailing, ring_spacing, angle, opacity): | |
| final_rings = 0 | |
| for r in range(1, total_num_rings + 1): | |
| r_norm = norm(r, 0, total_num_rings) | |
| wc = r_norm ** wave_compression | |
| if size <= 0: | |
| break | |
| else: | |
| line_thickness = ceil(((ring_spacing * wc) / steps_per_ring) * 1000) | |
| strength = remap(sin(angle), -1.0, 1.0, strength_min, strength_max) | |
| if strength != 0: | |
| strength = strength ** sharpness | |
| # fade strength for leading edge | |
| if r < num_leading * steps_per_ring: | |
| strength = strength * (r_norm ** taper_strength) | |
| if r > (num_rings + num_trailing) * steps_per_ring: | |
| strength = strength * (1 - (r_norm ** (1 / taper_strength))) | |
| strength *= opacity | |
| if strength > optimize_threshold: | |
| circle = ellipse(gp_frame, center, size, size, num_points, line_thickness) | |
| set_ring_color(circle, peak_color, strength) | |
| final_rings += 1 | |
| size = size - ((ring_spacing * wc) / steps_per_ring) | |
| angle = angle + angle_step | |
| return final_rings | |
| def make_ripple(gp_layer, center, num_rings, num_leading, num_trailing, sharpness, start_frame, | |
| duration, fade_duration): | |
| ring_size = 0.0 | |
| ring_spacing = 0.03 | |
| opacity = 1.0 | |
| opacity_power = 2 | |
| rings_built = total_num_rings | |
| for frame in gp_layer.frames: | |
| gp_layer.frames.remove(frame) | |
| for f in range(start_frame, (start_frame + duration + fade_duration)): | |
| if f < bpy.context.scene.frame_end: | |
| if rings_built > 0: | |
| gp_frame = get_frame(gp_layer.frames, f) | |
| gp_frame = gp_layer.frames.new(f) | |
| if f >= start_frame + duration: | |
| f_norm = norm(f, (start_frame + duration), (start_frame + duration + fade_duration)) | |
| opacity = opacity * (1 - (f_norm ** opacity_power)) | |
| rings_built = build_rings(gp_frame, center, ring_size, num_rings, num_leading, num_trailing, | |
| ring_spacing, angle, opacity) | |
| if (f < start_frame + 5) and (rings_built == 0): | |
| rings_built = 1 | |
| ring_size += rate | |
| num_rings = 0 | |
| sel_obj = None | |
| sel_objs = bpy.context.selected_objects | |
| if sel_objs: | |
| sel_obj = sel_objs[0] | |
| if sel_obj and sel_obj.type=='GPENCIL': | |
| sel_obj.data.materials.clear() | |
| gpencil = sel_obj.data | |
| gpencil.stroke_thickness_space = 'WORLDSPACE' | |
| for layer in gpencil.layers: | |
| gpencil.layers.remove(layer) | |
| num_ripples = 100 | |
| random.seed(60) | |
| for r in range(num_ripples): | |
| x = random.uniform(-2.0, 2.0) | |
| z = random.uniform(-2.0, 2.0) | |
| start_frame = random.randrange(1, 500) | |
| duration = random.randrange(5, 20) | |
| fade_duration = random.randrange(30, 100) | |
| layer_name = "ripple%s" % str(r) | |
| gp_layer = gpencil.layers.new(layer_name, set_active=True) | |
| make_ripple(gp_layer, (x, 0.0, z), num_rings, num_leading, num_trailing, sharpness, start_frame, | |
| duration, fade_duration) | |
| for layer in gpencil.layers: | |
| for frame in layer.frames: | |
| num_rings += len(frame.strokes) | |
| print("Num rings: ", num_rings) | |
| print("Runtime: ", time.perf_counter() - t) | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment