def animate_2d_automata(rule, nb_frames: 10, use_grease_pencil=True): nb_rows = 10 nb_cols = 10 gol = Automaton2D(nb_rows, nb_cols, rule, seed=11) FRAMES_SPACING = 1 bpy.context.scene.frame_start = 0 bpy.context.scene.frame_end = nb_frames * FRAMES_SPACING if use_grease_pencil: gp_layer = init_grease_pencil(clear_layer=True) else: obj_size = 0.7 subdivisions = 2 scale_factor = 0.2 init_mat_color = (0.7, 0.1, 0.1) # obj_generator = lambda idx: automata_blender_utils.icosphere_generator(obj_size, subdivisions, idx[0], idx[1], 0) obj_generator = lambda idx: automata_blender_utils.cube_generator( obj_size, idx[0], idx[1], 0) obj_updater = lambda obj, grid, idx: automata_blender_utils.object_updater_hide( obj, grid[idx]) # obj_updater = lambda obj, grid, idx: automata_blender_utils.object_updater_scale(obj, grid[idx], # scale_factor=scale_factor) # obj_updater = lambda obj, grid, idx: automata_blender_utils.object_updater_color_vector( # obj, grid[:, idx[0], idx[1]]) delete_all() obj_grid = automata_blender_utils.create_grid(gol, obj_generator) # automata_blender_utils.init_materials(obj_grid, init_mat_color) gol.update() for frame in range(nb_frames): if use_grease_pencil: gp_frame = gp_layer.frames.new(frame * FRAMES_SPACING) for idx, val in np.ndenumerate(gol.grid): if val: draw_square(gp_frame, (idx[0], idx[1], 0), 1) else: bpy.context.scene.frame_set(frame) automata_blender_utils.update_grid(obj_grid, gol, obj_updater) gol.update()
def main(): NUM_FRAMES_CHANGE = 5 NUM_FRAMES = 160 PATH_DURATION = 100 MIRROR_FRAME = 80 bpy.context.scene.frame_start = 0 bpy.context.scene.frame_end = NUM_FRAMES assert (NUM_FRAMES % NUM_FRAMES_CHANGE == 0) torus_major_segments = 70 torus_minor_segments = 25 torus_major_radius = 1 torus_minor_radius = 0.25 seed = 10 obj_size = 0.015 move_factor = 2 delete_all() automata_blender_utils.cube_generator(obj_size, 0, 0, 0) cube = bpy.context.view_layer.objects.active print("-----------------------") print("Start creation process") # create Donut bpy.ops.mesh.primitive_torus_add(location=(0, 0, 0), major_radius=torus_major_radius, minor_radius=torus_minor_radius, abso_major_rad=1., abso_minor_rad=1., major_segments=torus_major_segments, minor_segments=torus_minor_segments) torus = bpy.context.view_layer.objects.active # duplicate object by donut faces cube.select = True torus.select = True bpy.ops.object.parent_set(type='VERTEX') bpy.context.object.dupli_type = 'FACES' bpy.ops.object.duplicates_make_real() obj_updater = lambda obj, grid, idx: object_updater_move(obj, grid[idx]) # create GOL data rule_gol = { 'neighbours_count_born': 3, 'neighbours_maxcount_survive': 3, 'neighbours_mincount_survive': 2, } gol = Automaton2D(nb_rows=torus_major_segments, nb_cols=torus_minor_segments, config=rule_gol, seed=seed) gol.update() # update gol to start with a cleaner grid gol_grid_cache = [] # create GOL object grid by selected all previously created objects objs = [ obj for name, obj in bpy.context.scene.objects.items() if name.startswith('Cube.') ] assert len(objs) == gol.grid.shape[0] * gol.grid.shape[1] #set_gol_to_glider(gol) # Add camera path and follow path action bpy.ops.curve.primitive_bezier_circle_add(radius=torus_major_radius, location=(0, 0, 0)) cube.select = False torus.select = False bpy.data.objects['BezierCircle'].select = False #bpy.data.objects['BezierCircle'].data.path_duration = PATH_DURATION camera = bpy.data.objects['Camera'] camera.select = True camera.location = (0, 0, 0) camera.rotation_euler = (0, 0, 0) camera.constraints.new(type='FOLLOW_PATH') follow_path = camera.constraints['Follow Path'] follow_path.target = bpy.data.objects["BezierCircle"] follow_path.forward_axis = 'TRACK_NEGATIVE_Z' follow_path.up_axis = 'UP_Y' follow_path.use_curve_follow = True # Animate path. Doesn't seem to work because of wrong context #bpy.ops.constraint.followpath_path_animate(constraint="Follow Path", owner='OBJECT') camera.rotation_euler = (0, -0.25, 0) # Set objects on and off location by translating across object axes obj_grid = [] trans_local_adjust = Vector((0.0, 0.0, obj_size + obj_size / 10)) trans_local_move = Vector((0.0, 0.0, -obj_size * move_factor)) for obj in objs: trans_world = obj.matrix_world.to_3x3() * trans_local_adjust obj.matrix_world.translation += trans_world original_loc = obj.location.copy() trans_world = obj.matrix_world.to_3x3() * trans_local_move obj.matrix_world.translation += trans_world translated_loc = obj.location.copy() obj_grid.append({ 'obj': obj, 'original_loc': original_loc, 'translated_loc': translated_loc }) obj_grid = np.array(obj_grid).reshape(gol.grid.shape) for frame in range(0, NUM_FRAMES + 1): if frame % 10 == 0: print("Updating frame {}".format(frame)) bpy.context.scene.frame_set(frame) # When reaching final frame, clear handlers if (frame % NUM_FRAMES_CHANGE) == 0: if frame < MIRROR_FRAME: gol_grid_cache.append(gol.grid) elif frame > MIRROR_FRAME: gol.grid = gol_grid_cache.pop() automata_blender_utils.update_grid(obj_grid, gol, obj_updater) if frame >= NUM_FRAMES: bpy.app.handlers.frame_change_pre.clear() bpy.context.scene.frame_set(0) print("-----------------------")