1 Commits

Author SHA1 Message Date
Stefan Cepko 8292682ac2 Add example scripts for major functions 2026-06-05 05:36:50 -04:00
10 changed files with 763 additions and 2 deletions
+27
View File
@@ -106,6 +106,33 @@ blender --python run_script.py
---
## Programmatic Examples
The `examples/` directory contains 8 different Python scripts demonstrating how to interact with the Pr3tz animation engine programmatically:
1. [01_simple_torus_knot.py](examples/01_simple_torus_knot.py) — Demonstrates basic torus knot generation with customizable coprime topology parameters.
2. [02_multi_link_knot.py](examples/02_multi_link_knot.py) — Shows how to generate multi-link torus knots when $p$ and $q$ are not coprime, producing multiple separate interlocking curve loops.
3. [03_shape_types.py](examples/03_shape_types.py) — Shows generating and editing other shapes supported by the engine (Mobius Strip, 3D Lissajous, Spherical Spiral).
4. [04_animation_styles.py](examples/04_animation_styles.py) — Exercises the four different built-in animation modes (spin, revolution, vertical height breathing, and scale pulsation).
5. [05_custom_transitions.py](examples/05_custom_transitions.py) — Configures morph transitions and material cross-fading between completely different geometry types and shader presets.
6. [06_audio_reactive_simulation.py](examples/06_audio_reactive_simulation.py) — Simulates audio-reactive animation by programmatically keyframing global parameters and knot attenuverters.
7. [07_headless_render.py](examples/07_headless_render.py) — Sets up and renders a sequence of frames headlessly using Blender's background CLI.
8. [08_programmatic_bake_export.py](examples/08_programmatic_bake_export.py) — Invokes the bake/export operator programmatically to compile procedural curves into standalone mesh frames.
To run any example from the command line:
**PowerShell (Windows):**
```powershell
& "C:\Program Files\Blender Foundation\Blender 5.0\blender.exe" --python examples/01_simple_torus_knot.py
```
**CMD (Windows):**
```cmd
"C:\Program Files\Blender Foundation\Blender 5.0\blender.exe" --python examples/01_simple_torus_knot.py
```
---
## Baking for Render Farms
The **Bake & Export** button (bottom of the Pr3tz panel) converts the procedural animation into a standalone `.blend`:
+72
View File
@@ -0,0 +1,72 @@
import sys
import os
import bpy
# 1. Resolve path to include workspace root so pr3tz can be imported
dir_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
if dir_path not in sys.path:
sys.path.append(dir_path)
import pr3tz
# 2. Register pr3tz addon
try:
if "pr3tz" in sys.modules:
import importlib
importlib.reload(pr3tz)
else:
import pr3tz
pr3tz.register()
print("[Pr3tz] Registered successfully!")
except Exception as e:
print(f"[Pr3tz] Failed to register: {e}")
sys.exit(1)
# 3. Configure the scene (camera, lights, timeline, etc.)
pr3tz.setup_scene()
# 4. Clear existing playlist
scene = bpy.context.scene
scene.knot_list.clear()
# 5. Add a new knot configuration
item = scene.knot_list.add()
item.name = "Trefoil Knot"
item.shape_type = 'TORUS_KNOT'
# Topology (coprime p and q)
item.torus_p = 2 # Revolutions around the torus axis
item.torus_q = 3 # Spins around the torus tube
# Dimensions
item.mode = 'MAJOR_MINOR'
item.torus_R = 2.0 # Distance from center to tube center
item.torus_r = 0.8 # Radius of the tube
# Geometry
item.geo_bDepth = 0.08 # Bevel depth (thickness of the curve tube)
# Material settings
item.material_mode = 'PRESET'
item.shader_id = 'GLOSS_BLUE'
item.preset_color = (0.2, 0.6, 1.0)
item.preset_roughness = 0.1
item.preset_metallic = 0.2
# 6. Fit timeline so the knot plays
scene.knot_globals.frames_per_knot = 120
bpy.ops.knot.fit_timeline()
# 7. Force instant geometry generation for current frame
pr3tz.knot_frame_handler(scene)
print("\n=====================================================================")
print("Example 1: Simple Torus Knot Loaded!")
print("=====================================================================")
print("Generated a 3D Trefoil Knot (p=2, q=3) using the 'GLOSS_BLUE' preset.")
print("Interact with it in Blender:")
print(" - Open the 'Pr3tz' N-Panel in the 3D Viewport sidebar.")
print(" - Change 'Revolutions (p)' or 'Spins (q)' to see the knot shape morph.")
print(" - Modify the 'Bevel Depth' to change its thickness.")
print("=====================================================================\n")
+70
View File
@@ -0,0 +1,70 @@
import sys
import os
import bpy
# 1. Resolve path to include workspace root so pr3tz can be imported
dir_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
if dir_path not in sys.path:
sys.path.append(dir_path)
import pr3tz
# 2. Register pr3tz addon
try:
if "pr3tz" in sys.modules:
import importlib
importlib.reload(pr3tz)
else:
import pr3tz
pr3tz.register()
print("[Pr3tz] Registered successfully!")
except Exception as e:
print(f"[Pr3tz] Failed to register: {e}")
sys.exit(1)
# 3. Configure the scene (camera, lights, timeline, etc.)
pr3tz.setup_scene()
# 4. Clear existing playlist
scene = bpy.context.scene
scene.knot_list.clear()
# 5. Add a multi-link knot configuration
item = scene.knot_list.add()
item.name = "Interlocking Solomon Link"
item.shape_type = 'TORUS_KNOT'
# Topology with GCD > 1
item.torus_p = 4 # 4 revolutions
item.torus_q = 6 # 6 spins. gcd(4, 6) = 2 separate interlocking links
item.multiple_links = True # Enable rendering of all links as separate splines
# Dimensions & Geometry
item.torus_R = 2.5
item.torus_r = 1.0
item.geo_bDepth = 0.05
# Use a bright emissive neon color
item.material_mode = 'PRESET'
item.shader_id = 'NEON_GLOW'
item.preset_color = (0.0, 1.0, 0.9) # Cyan
item.preset_emission_strength = 3.0
# 6. Fit timeline so the knot plays
scene.knot_globals.frames_per_knot = 120
bpy.ops.knot.fit_timeline()
# 7. Force instant geometry generation for current frame
pr3tz.knot_frame_handler(scene)
print("\n=====================================================================")
print("Example 2: Multi-Link Knot Loaded!")
print("=====================================================================")
print("Generated a 3D Solomon Link (p=4, q=6) with multiple_links=True.")
print("This renders as 2 distinct interlocking/nested splines.")
print("Interact with it in Blender:")
print(" - Open the 'Pr3tz' N-Panel.")
print(" - Toggle 'Multiple Links' off/on to see the difference between")
print(" rendering a single loop vs. all mathematical links.")
print("=================================================================\n")
+89
View File
@@ -0,0 +1,89 @@
import sys
import os
import bpy
# 1. Resolve path to include workspace root so pr3tz can be imported
dir_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
if dir_path not in sys.path:
sys.path.append(dir_path)
import pr3tz
# 2. Register pr3tz addon
try:
if "pr3tz" in sys.modules:
import importlib
importlib.reload(pr3tz)
else:
import pr3tz
pr3tz.register()
print("[Pr3tz] Registered successfully!")
except Exception as e:
print(f"[Pr3tz] Failed to register: {e}")
sys.exit(1)
# 3. Configure the scene (camera, lights, timeline, etc.)
pr3tz.setup_scene()
# 4. Clear existing playlist
scene = bpy.context.scene
scene.knot_list.clear()
# ── 1. Mobius Strip Winding ──
mobius_item = scene.knot_list.add()
mobius_item.name = "Mobius Strip Boundary"
mobius_item.shape_type = 'MOBIUS'
mobius_item.mobius_twists = 3 # Odd twist count creates single continuous loop
mobius_item.mobius_width = 1.2
mobius_item.torus_R = 2.5 # Uses major radius for ring dimension
mobius_item.geo_bDepth = 0.04
mobius_item.material_mode = 'PRESET'
mobius_item.shader_id = 'METALLIC'
mobius_item.preset_color = (1.0, 0.78, 0.28) # Gold
# ── 2. Lissajous 3D Curve ──
liss_item = scene.knot_list.add()
liss_item.name = "3D Lissajous Knot"
liss_item.shape_type = 'LISSAJOUS'
liss_item.liss_kx = 3 # X frequency
liss_item.liss_ky = 2 # Y frequency
liss_item.liss_kz = 5 # Z frequency
liss_item.liss_amp = 2.5
liss_item.geo_bDepth = 0.06
liss_item.material_mode = 'PRESET'
liss_item.shader_id = 'LAVA'
liss_item.preset_color = (1.0, 0.1, 0.0)
# ── 3. Spherical Spiral (Loxodrome) ──
spiral_item = scene.knot_list.add()
spiral_item.name = "Spherical Spiral"
spiral_item.shape_type = 'SPIRAL'
spiral_item.spiral_turns = 16
spiral_item.spiral_R = 2.2
# Open spiral (not cyclic)
spiral_item.geo_bDepth = 0.05
spiral_item.material_mode = 'PRESET'
spiral_item.shader_id = 'IRIDESCENT'
# 5. Set globals and fit timeline
glob = scene.knot_globals
glob.frames_per_knot = 100
bpy.ops.knot.fit_timeline()
# 6. Force instant geometry generation for current frame
pr3tz.knot_frame_handler(scene)
print("\n=====================================================================")
print("Example 3: Shape Types Playlist Loaded!")
print("=====================================================================")
print("Created a playlist containing:")
print(" 1. Mobius Strip Winding (Gold Metallic)")
print(" 2. 3D Lissajous Curve (Emissive Lava)")
print(" 3. Spherical Spiral (Iridescent Rainbow)")
print("=====================================================================")
print("Interact with it in Blender:")
print(" - Play the animation (Space) or scrub the timeline.")
print(" - Each shape will display in sequence.")
print(" - Adjust specific shape properties in the N-Panel sidebar.")
print("=====================================================================\n")
+100
View File
@@ -0,0 +1,100 @@
import sys
import os
import bpy
# 1. Resolve path to include workspace root so pr3tz can be imported
dir_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
if dir_path not in sys.path:
sys.path.append(dir_path)
import pr3tz
# 2. Register pr3tz addon
try:
if "pr3tz" in sys.modules:
import importlib
importlib.reload(pr3tz)
else:
import pr3tz
pr3tz.register()
print("[Pr3tz] Registered successfully!")
except Exception as e:
print(f"[Pr3tz] Failed to register: {e}")
sys.exit(1)
# 3. Configure the scene (camera, lights, timeline, etc.)
pr3tz.setup_scene()
# 4. Clear existing playlist
scene = bpy.context.scene
scene.knot_list.clear()
# ── Style 1: Tube Spin Winding ──
# Animates the knot twisting around its own tube geometry.
item_spin = scene.knot_list.add()
item_spin.name = "Tube Spin Animation"
item_spin.shape_type = 'TORUS_KNOT'
item_spin.torus_p = 3
item_spin.torus_q = 5
item_spin.spin_phase_rate = 0.05 # Radians spin rate per frame
item_spin.material_mode = 'PRESET'
item_spin.shader_id = 'CARBON_FIBER'
item_spin.preset_color = (0.3, 0.3, 0.3)
# ── Style 2: Orbital Revolution ──
# Animates the entire knot revolving/orbiting around the core torus axis.
item_orbit = scene.knot_list.add()
item_orbit.name = "Orbital Orbit Animation"
item_orbit.shape_type = 'TORUS_KNOT'
item_orbit.torus_p = 3
item_orbit.torus_q = 5
item_orbit.rev_phase_rate = 0.03 # Radians revolution rate per frame
item_orbit.material_mode = 'PRESET'
item_orbit.shader_id = 'GLASS'
item_orbit.preset_color = (0.7, 0.9, 1.0)
# ── Style 3: Vertical Height Breathing ──
# Vertically stretches and squashes the knot structure.
item_breath = scene.knot_list.add()
item_breath.name = "Height Breathing Pulse"
item_breath.shape_type = 'TORUS_KNOT'
item_breath.torus_p = 3
item_breath.torus_q = 5
item_breath.height_rate = 0.06 # Sine frequency for height pulse
item_breath.material_mode = 'PRESET'
item_breath.shader_id = 'PLASMA_GLOW'
item_breath.preset_color = (1.0, 0.1, 0.7)
# ── Style 4: Uniform Scale Pulsation ──
# Animates the overall scale of the knot using a sine multiplier.
item_scale = scene.knot_list.add()
item_scale.name = "Scale Pulsation"
item_scale.shape_type = 'TORUS_KNOT'
item_scale.torus_p = 3
item_scale.torus_q = 5
item_scale.scale_amplitude = 0.25 # Scale factor oscillation amplitude (+/- 25%)
item_scale.scale_rate = 0.08 # Frequency of scale pulsation
item_scale.material_mode = 'PRESET'
item_scale.shader_id = 'ZEBRA_STRIPES'
# 5. Fit timeline
scene.knot_globals.frames_per_knot = 150
bpy.ops.knot.fit_timeline()
# 6. Force instant geometry generation for current frame
pr3tz.knot_frame_handler(scene)
print("\n=====================================================================")
print("Example 4: Animation Styles Playlist Loaded!")
print("=====================================================================")
print("Created 4 separate entries showing off isolated animation rates:")
print(" 1. Tube Spin (Carbon Fiber) — spin_phase_rate=0.05")
print(" 2. Orbital Orbit (Glass) — rev_phase_rate=0.03")
print(" 3. Height Breathing (Plasma) — height_rate=0.06")
print(" 4. Scale Pulsation (Zebra) — scale_amplitude=0.25, scale_rate=0.08")
print("=====================================================================")
print("Interact with it in Blender:")
print(" - Play the animation (Space) to see how the rates animate.")
print(" - Adjust the global 'Reactivity' slider to scale these speeds.")
print("=====================================================================\n")
+104
View File
@@ -0,0 +1,104 @@
import sys
import os
import bpy
# 1. Resolve path to include workspace root so pr3tz can be imported
dir_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
if dir_path not in sys.path:
sys.path.append(dir_path)
import pr3tz
# 2. Register pr3tz addon
try:
if "pr3tz" in sys.modules:
import importlib
importlib.reload(pr3tz)
else:
import pr3tz
pr3tz.register()
print("[Pr3tz] Registered successfully!")
except Exception as e:
print(f"[Pr3tz] Failed to register: {e}")
sys.exit(1)
# 3. Configure the scene (camera, lights, timeline, etc.)
pr3tz.setup_scene()
# 4. Clear existing playlist
scene = bpy.context.scene
scene.knot_list.clear()
# ── Knot 1: Trefoil (Torus Knot) with Lava Preset ──
item_a = scene.knot_list.add()
item_a.name = "Trefoil Knot"
item_a.shape_type = 'TORUS_KNOT'
item_a.torus_p = 2
item_a.torus_q = 3
item_a.torus_R = 2.0
item_a.torus_r = 0.8
item_a.geo_bDepth = 0.05
item_a.material_mode = 'PRESET'
item_a.shader_id = 'LAVA'
item_a.preset_color = (1.0, 0.2, 0.0)
item_a.cycle_rate = 1.0
# ── Knot 2: 3D Lissajous with Glass Preset (Morphed using QUAD_IN_OUT) ──
# Triggers a transition morph when transitioning from item_a to item_b
item_b = scene.knot_list.add()
item_b.name = "Lissajous Morph Target"
item_b.shape_type = 'LISSAJOUS'
item_b.liss_kx = 3
item_b.liss_ky = 2
item_b.liss_kz = 4
item_b.liss_amp = 2.2
item_b.geo_bDepth = 0.08
item_b.material_mode = 'PRESET'
item_b.shader_id = 'GLASS'
item_b.preset_color = (0.3, 0.8, 1.0)
item_b.preset_roughness = 0.0
item_b.cycle_rate = 1.0
item_b.transition_frames = 60 # Morphs over 60 frames
item_b.transition_easing = 'QUAD_IN_OUT' # Smooth ease-in, ease-out curve
# ── Knot 3: Mobius Loop with Lichen Preset (Morphed using SMOOTHSTEP) ──
item_c = scene.knot_list.add()
item_c.name = "Mobius Morph Target"
item_c.shape_type = 'MOBIUS'
item_c.mobius_twists = 1
item_c.mobius_width = 1.6
item_c.torus_R = 2.4
item_c.geo_bDepth = 0.04
item_c.material_mode = 'PRESET'
item_c.shader_id = 'COPPER_PATINA'
item_c.preset_color = (0.1, 0.7, 0.5)
item_c.cycle_rate = 1.0
item_c.transition_frames = 40 # Morphs over 40 frames
item_c.transition_easing = 'SMOOTHSTEP' # S-curve ease
# 5. Set up playlist blend materials.
# MUST call this whenever modifying transitions or colors programmatically,
# so the shader node trees are rebuilt.
pr3tz.materials.prewarm_materials_and_blends(scene)
# 6. Fit timeline
scene.knot_globals.frames_per_knot = 120
bpy.ops.knot.fit_timeline()
# 7. Force instant geometry generation for current frame
pr3tz.knot_frame_handler(scene)
print("\n=====================================================================")
print("Example 5: Morph Transitions Playlist Loaded!")
print("=====================================================================")
print("Created 3 distinct shapes with custom transition settings:")
print(" - Trefoil Knot (Lava Preset)")
print(" - Lissajous (Glass Preset) morphs from Trefoil over 60 frames (Quad In/Out)")
print(" - Mobius Loop (Copper Patina) morphs from Lissajous over 40 frames (Smoothstep)")
print("=====================================================================")
print("Interact with it in Blender:")
print(" - Play the animation (Space).")
print(" - Watch how the geometries blend smoothly between shapes.")
print(" - Observe the material node network transition from Lava to Glass/Copper.")
print("=====================================================================\n")
+122
View File
@@ -0,0 +1,122 @@
import sys
import os
import math
import random
import bpy
# 1. Resolve path to include workspace root so pr3tz can be imported
dir_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
if dir_path not in sys.path:
sys.path.append(dir_path)
import pr3tz
# 2. Register pr3tz addon
try:
if "pr3tz" in sys.modules:
import importlib
importlib.reload(pr3tz)
else:
import pr3tz
pr3tz.register()
print("[Pr3tz] Registered successfully!")
except Exception as e:
print(f"[Pr3tz] Failed to register: {e}")
sys.exit(1)
# 3. Configure the scene (camera, lights, timeline, etc.)
pr3tz.setup_scene()
# 4. Clear existing playlist
scene = bpy.context.scene
scene.knot_list.clear()
# 5. Add a base knot configuration
item = scene.knot_list.add()
item.name = "Audio Reactive Knot"
item.shape_type = 'TORUS_KNOT'
item.torus_p = 3
item.torus_q = 7
item.torus_R = 2.0
item.torus_r = 0.8
item.geo_bDepth = 0.05
# We will modulate minor radius and bevel depth using attenuverters.
# These variables will act as targets for our keyframed modulators.
item.mod_torus_r = 0.0 # Starts flat
item.mod_geo_bDepth = 0.0
item.material_mode = 'PRESET'
item.shader_id = 'NEON_GLOW'
item.preset_color = (1.0, 0.0, 0.5) # Magenta
item.preset_emission_strength = 2.0
# 6. Fit timeline
scene.frame_start = 1
scene.frame_end = 240
scene.render.fps = 24
# Set up prewarmed materials
pr3tz.materials.prewarm_materials_and_blends(scene)
# 7. Generate a simulated audio track (bass peaks on beats, high-hat noise)
# We will write keyframes to global properties to drive the animation.
glob = scene.knot_globals
# Clear any existing animation on globals
if glob.animation_data:
glob.animation_data.clear()
if item.animation_data:
item.animation_data.clear()
print("Simulating audio analysis and writing keyframes...")
# Loop over the frames and compute simulated audio values
for frame in range(scene.frame_start, scene.frame_end + 1):
# Beat occurs every 24 frames (1 second at 24fps)
beat_timer = (frame - 1) % 24
# Bass amplitude: sharp spike decaying exponentially
bass_amp = math.exp(-beat_timer * 0.15) if beat_timer < 20 else 0.0
# High frequency noise (high-hats/snares)
random.seed(frame) # Deterministic per frame
treble_amp = 0.15 * random.random() if (frame % 8) < 3 else 0.02
# Total reactivity scales the spin/rates
glob.reactivity_factor = 1.0 + bass_amp * 4.0
glob.keyframe_insert(data_path="reactivity_factor", frame=frame)
# Bevel depth thickness spikes with the bass beat
glob.global_master_thickness = 1.0 + bass_amp * 0.8
glob.keyframe_insert(data_path="global_master_thickness", frame=frame)
# Emission spikes on treble highlights
glob.global_emission_multiplier = 1.0 + treble_amp * 20.0
glob.keyframe_insert(data_path="global_emission_multiplier", frame=frame)
# Turbulence (noise) peaks on beats
glob.global_turbulence = bass_amp * 0.25
glob.keyframe_insert(data_path="global_turbulence", frame=frame)
# Hue shifts continuously, jumping slightly on beats
glob.global_hue_shift = (frame * 0.002 + bass_amp * 0.05) % 1.0
glob.keyframe_insert(data_path="global_hue_shift", frame=frame)
# 8. Force instant geometry generation for current frame
pr3tz.knot_frame_handler(scene)
print("\n=====================================================================")
print("Example 6: Audio-Reactive Simulation Loaded!")
print("=====================================================================")
print("Generated a 240-frame animation simulating audio beat sync:")
print(" - 'reactivity_factor' & 'global_turbulence' spike on bass beats (every 24 frames).")
print(" - 'global_master_thickness' swells on beats.")
print(" - 'global_emission_multiplier' flashes on high-frequency treble ticks.")
print(" - 'global_hue_shift' rotates colors over time.")
print("=====================================================================")
print("Interact with it in Blender:")
print(" - Press Space to play.")
print(" - Look at the Graph Editor to see the custom audio curves keyframed.")
print("=====================================================================\n")
+93
View File
@@ -0,0 +1,93 @@
import sys
import os
import bpy
# 1. Resolve path to include workspace root so pr3tz can be imported
dir_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
if dir_path not in sys.path:
sys.path.append(dir_path)
import pr3tz
# 2. Register pr3tz addon
try:
if "pr3tz" in sys.modules:
import importlib
importlib.reload(pr3tz)
else:
import pr3tz
pr3tz.register()
print("[Pr3tz] Registered successfully!")
except Exception as e:
print(f"[Pr3tz] Failed to register: {e}")
sys.exit(1)
# 3. Configure the scene (camera, lights, timeline, etc.)
pr3tz.setup_scene()
# 4. Clear existing playlist
scene = bpy.context.scene
scene.knot_list.clear()
# 5. Add a simple spinning knot configuration
item = scene.knot_list.add()
item.name = "Render Example Knot"
item.shape_type = 'TORUS_KNOT'
item.torus_p = 2
item.torus_q = 5
item.torus_R = 2.0
item.torus_r = 0.8
item.geo_bDepth = 0.06
item.spin_phase_rate = 0.02
item.material_mode = 'PRESET'
item.shader_id = 'NEON_GLOW'
item.preset_color = (0.0, 0.5, 1.0) # Neon blue
item.preset_emission_strength = 2.0
pr3tz.materials.prewarm_materials_and_blends(scene)
# 6. Configure rendering for a super-fast headless export
scene.render.engine = 'CYCLES'
scene.cycles.samples = 4 # Very low samples for quick testing
scene.cycles.use_denoising = False
# Render size (small for fast test rendering)
scene.render.resolution_x = 480
scene.render.resolution_y = 270
scene.render.resolution_percentage = 100
# Setup Output directory
output_dir = os.path.join(dir_path, "renders")
if not os.path.exists(output_dir):
os.makedirs(output_dir)
# Set base filename
scene.render.filepath = os.path.join(output_dir, "frame_")
scene.render.image_settings.file_format = 'PNG'
scene.render.image_settings.color_mode = 'RGBA'
# 7. Render a short range (10 frames)
start_frame = 1
end_frame = 10
print(f"\nHeadless Render Started. Rendering frames {start_frame} to {end_frame}...")
for f in range(start_frame, end_frame + 1):
print(f"Rendering frame {f}/{end_frame}...")
scene.frame_set(f) # Triggers knot_frame_handler automatically
# We construct the filepath for this frame manually to save it
scene.render.filepath = os.path.join(output_dir, f"frame_{f:04d}.png")
# Execute render
bpy.ops.render.render(write_still=True)
print(f"\nRendering complete! Output frames saved to: {output_dir}")
print("\n=====================================================================")
print("Example 7: Headless Render Script Completed!")
print("=====================================================================")
print("You can run this script from your terminal headlessly using:")
print(" blender -b -P examples/07_headless_render.py")
print("=====================================================================\n")
+84
View File
@@ -0,0 +1,84 @@
import sys
import os
import bpy
# 1. Resolve path to include workspace root so pr3tz can be imported
dir_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
if dir_path not in sys.path:
sys.path.append(dir_path)
import pr3tz
# 2. Register pr3tz addon
try:
if "pr3tz" in sys.modules:
import importlib
importlib.reload(pr3tz)
else:
import pr3tz
pr3tz.register()
print("[Pr3tz] Registered successfully!")
except Exception as e:
print(f"[Pr3tz] Failed to register: {e}")
sys.exit(1)
# 3. Configure the scene (camera, lights, timeline, etc.)
pr3tz.setup_scene()
# 4. Clear existing playlist and populate with a couple of animated shapes
scene = bpy.context.scene
scene.knot_list.clear()
item_1 = scene.knot_list.add()
item_1.name = "Bake Target A"
item_1.shape_type = 'TORUS_KNOT'
item_1.torus_p = 2
item_1.torus_q = 3
item_1.spin_phase_rate = 0.05
item_1.material_mode = 'PRESET'
item_1.shader_id = 'LAVA'
item_2 = scene.knot_list.add()
item_2.name = "Bake Target B"
item_2.shape_type = 'LISSAJOUS'
item_2.liss_kx = 3
item_2.liss_ky = 2
item_2.liss_amp = 2.0
item_2.material_mode = 'PRESET'
item_2.shader_id = 'GLASS'
item_2.transition_frames = 20
pr3tz.materials.prewarm_materials_and_blends(scene)
# 5. Set a short frame range for rapid baking
scene.frame_start = 1
scene.frame_end = 40
scene.render.fps = 24
# Setup target filepath for the baked blend file
baked_dir = os.path.join(dir_path, "baked")
if not os.path.exists(baked_dir):
os.makedirs(baked_dir)
output_blend = os.path.join(baked_dir, "baked_knot_animation.blend")
# 6. Execute programmatic bake and export
print(f"\nStarting programmatic bake & export to: {output_blend}...")
# Run the operator directly, passing parameters.
# Since it inherits from ExportHelper, it expects filepath to be passed.
bpy.ops.knot.bake_export(
filepath=output_blend,
use_render_resolution=False, # Set to False to bake quickly at preview res
pack_textures=True,
split_export=False
)
print(f"\nBake completed! stand-alone .blend file saved at: {output_blend}")
print("\n=====================================================================")
print("Example 8: Programmatic Bake & Export Script Completed!")
print("=====================================================================")
print("The procedural knot was successfully baked to standalone meshes.")
print("The output .blend file requires no addons and can be rendered")
print("on any computer or render farm (e.g. SheepIt).")
print("=====================================================================\n")
+2 -2
View File
@@ -149,8 +149,8 @@ class KNOT_OT_BakePreview(bpy.types.Operator):
glob = context.scene.knot_globals
_make_torus_knot(
config,
resolution=glob.resolution,
bevel_resolution=glob.bevel_resolution,
resolution=glob.preview_resolution,
bevel_resolution=glob.preview_bevel_resolution,
knot_scale=glob.knot_scale,
scene=context.scene,
)