""" SURYA Blender Utilities Shared functions for all track animations """ import bpy import math from mathutils import Vector, Matrix, Euler import random # Color palette (RGB normalized 0-1) COLORS = { "skin": (0.957, 0.447, 0.714, 1.0), # #f472b6 Pink "u_saved_me": (0.133, 0.827, 0.933, 1.0), # #22d3ee Cyan "nothing": (0.4, 0.4, 0.4, 1.0), # #666666 Grey "natures_call": (0.369, 0.918, 0.831, 1.0), # #5eead4 Teal "idk": (0.957, 0.447, 0.714, 1.0), # #f472b6 Pink "with_u": (0.984, 0.749, 0.141, 1.0), # #fbbf24 Gold "hollow": (0.984, 0.749, 0.141, 1.0), # #fbbf24 Gold "intro": (0.102, 0.039, 0.180, 1.0), # #1a0a2e Deep purple "white": (1.0, 1.0, 1.0, 1.0), "black": (0.0, 0.0, 0.0, 1.0), } # Animation settings FPS = 30 DURATION_SECONDS = 25 TOTAL_FRAMES = FPS * DURATION_SECONDS # 750 frames def clear_scene(): """Remove all objects from the current scene.""" bpy.ops.object.select_all(action='SELECT') bpy.ops.object.delete(use_global=False) # Clear orphan data for block in bpy.data.meshes: if block.users == 0: bpy.data.meshes.remove(block) for block in bpy.data.materials: if block.users == 0: bpy.data.materials.remove(block) for block in bpy.data.curves: if block.users == 0: bpy.data.curves.remove(block) def create_scene(name): """Create a new scene with the given name.""" scene = bpy.data.scenes.new(name) bpy.context.window.scene = scene scene.frame_start = 1 scene.frame_end = TOTAL_FRAMES scene.render.fps = FPS return scene def setup_scene(background_color=(0.1, 0.04, 0.18, 1.0)): """Setup scene with camera and world settings.""" # Set world background world = bpy.context.scene.world if world is None: world = bpy.data.worlds.new("World") bpy.context.scene.world = world world.use_nodes = True bg_node = world.node_tree.nodes.get("Background") if bg_node: bg_node.inputs[0].default_value = background_color bg_node.inputs[1].default_value = 1.0 # Strength # Set render settings bpy.context.scene.render.resolution_x = 1920 bpy.context.scene.render.resolution_y = 1080 def create_camera(location=(0, -10, 5), rotation=(1.1, 0, 0)): """Create and setup camera.""" bpy.ops.object.camera_add(location=location) camera = bpy.context.active_object camera.rotation_euler = Euler(rotation, 'XYZ') bpy.context.scene.camera = camera return camera def create_emission_material(name, color, strength=2.0): """Create an emission material for glowing objects.""" mat = bpy.data.materials.new(name=name) mat.use_nodes = True nodes = mat.node_tree.nodes links = mat.node_tree.links # Clear default nodes nodes.clear() # Create emission shader emission = nodes.new('ShaderNodeEmission') emission.inputs[0].default_value = color emission.inputs[1].default_value = strength # Output output = nodes.new('ShaderNodeOutputMaterial') links.new(emission.outputs[0], output.inputs[0]) return mat def create_basic_material(name, color, metallic=0.0, roughness=0.5): """Create a basic PBR material.""" mat = bpy.data.materials.new(name=name) mat.use_nodes = True bsdf = mat.node_tree.nodes.get("Principled BSDF") if bsdf: bsdf.inputs["Base Color"].default_value = color bsdf.inputs["Metallic"].default_value = metallic bsdf.inputs["Roughness"].default_value = roughness return mat def create_sphere(location=(0, 0, 0), radius=1.0, segments=32, rings=16, name="Sphere"): """Create a UV sphere.""" bpy.ops.mesh.primitive_uv_sphere_add( radius=radius, segments=segments, ring_count=rings, location=location ) obj = bpy.context.active_object obj.name = name return obj def create_icosahedron(location=(0, 0, 0), radius=1.0, name="Icosahedron"): """Create an icosahedron.""" bpy.ops.mesh.primitive_ico_sphere_add( radius=radius, subdivisions=1, location=location ) obj = bpy.context.active_object obj.name = name return obj def create_curve_from_points(points, name="Curve", bevel_depth=0.02): """Create a curve from a list of 3D points.""" curve_data = bpy.data.curves.new(name=name, type='CURVE') curve_data.dimensions = '3D' curve_data.bevel_depth = bevel_depth curve_data.bevel_resolution = 4 spline = curve_data.splines.new('NURBS') spline.points.add(len(points) - 1) for i, point in enumerate(points): spline.points[i].co = (point[0], point[1], point[2], 1) spline.use_endpoint_u = True curve_obj = bpy.data.objects.new(name, curve_data) bpy.context.collection.objects.link(curve_obj) return curve_obj def keyframe_location(obj, frame, location): """Set a location keyframe.""" obj.location = location obj.keyframe_insert(data_path="location", frame=frame) def keyframe_scale(obj, frame, scale): """Set a scale keyframe.""" if isinstance(scale, (int, float)): scale = (scale, scale, scale) obj.scale = scale obj.keyframe_insert(data_path="scale", frame=frame) def keyframe_rotation(obj, frame, rotation): """Set a rotation keyframe (Euler).""" obj.rotation_euler = rotation obj.keyframe_insert(data_path="rotation_euler", frame=frame) def animate_camera_orbit(camera, center=(0, 0, 0), radius=10, height=5, start_frame=1, end_frame=750, revolutions=1): """Animate camera orbiting around a center point.""" for frame in range(start_frame, end_frame + 1): t = (frame - start_frame) / (end_frame - start_frame) angle = t * 2 * math.pi * revolutions x = center[0] + radius * math.cos(angle) y = center[1] + radius * math.sin(angle) z = center[2] + height camera.location = (x, y, z) camera.keyframe_insert(data_path="location", frame=frame) # Point at center direction = Vector(center) - Vector((x, y, z)) rot_quat = direction.to_track_quat('-Z', 'Y') camera.rotation_euler = rot_quat.to_euler() camera.keyframe_insert(data_path="rotation_euler", frame=frame) def create_star_field(count=200, radius=20, min_size=0.02, max_size=0.08): """Create a field of star particles.""" stars = [] for i in range(count): x = random.uniform(-radius, radius) y = random.uniform(-radius, radius) z = random.uniform(-radius/2, radius) size = random.uniform(min_size, max_size) bpy.ops.mesh.primitive_ico_sphere_add(radius=size, subdivisions=1, location=(x, y, z)) star = bpy.context.active_object star.name = f"Star_{i:03d}" # Add emission material mat = create_emission_material(f"StarMat_{i:03d}", COLORS["white"], strength=5.0) star.data.materials.append(mat) stars.append(star) return stars def smooth_interpolation(t): """Smooth step interpolation (ease in-out).""" return t * t * (3 - 2 * t) def ease_in_out(t): """Ease in-out cubic.""" if t < 0.5: return 4 * t * t * t else: return 1 - pow(-2 * t + 2, 3) / 2