Files
Pr3tz/pr3tz/__init__.py
T
Stefan Cepko ca146ea2c1
Build & Release Addon (Gitea) / build-and-release (push) Has been cancelled
Release 0.1
2026-06-05 04:45:35 -04:00

217 lines
7.6 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
pr3tz/__init__.py
-----------------
Blender add-on entry-point for the Pr3tz procedural animation system.
What this add-on does
---------------------
1. Registers a VIEW_3D N-panel ("Pr3tz") with:
- A per-scene playlist of KnotItem entries, each specifying topology,
geometry, animation rates, transition settings, and a material preset.
- Operators for adding, removing, reordering, and randomly generating
playlist entries.
- Global playback controls (speed, phase, reactivity) that can be
keyframed or driven for audio-reactive animation.
2. On every frame change (frame_change_post handler), procedurally rebuilds
a single reused NURBS curve object ("AnimKnot") in-place using the
parametric torus-knot equations — no bpy.ops, no edit-mode, no depsgraph
races.
3. Interpolates geometry and cross-fades shader materials between adjacent
playlist entries during configurable transition windows.
4. Provides a catalogue of 20 built-in shader presets (GLOSS_BLUE, NEON_GLOW,
METALLIC, GLASS, HOLOGRAM, LAVA, IRIDESCENT, …) plus support for arbitrary
project materials.
Module layout
-------------
types.py — KnotConfig TypedDict (shared data contract)
constants.py — pure-data defaults (KNOT_CONFIGS, camera/light positions, …)
compat.py — one-shot compatibility patches (headless align_matrix fix)
materials.py — 20 shader builders + material cache + blend system
geometry.py — _make_torus_knot() (parametric NURBS, no bpy.context reads)
handler.py — @persistent frame-change handler + easing + playlist timing
properties.py — Blender PropertyGroups (KnotItem, KnotGlobalSettings, …)
operators.py — KNOT_OT_* operators
ui.py — KNOT_UL_* lists, draw_knot_properties(), KNOT_PT_Panel
scene_setup.py — setup_scene() (camera, light, world, render settings)
How to use
----------
Requires Blender 5.0 or later.
Option A Persistent add-on:
Zip the pr3tz/ folder (using package_addon.py) and install it from Preferences.
Alternatively, copy or symlink the pr3tz/ folder to Blender's addons directory
and enable it from Edit → Preferences → Add-ons.
The panel appears under the "Pr3tz" tab in the 3-D viewport N-panel.
Option B Run as a script (via bootstrap runner):
Open run_script.py from the workspace root in Blender's Text Editor,
and click Run Script (Alt+P).
Option C Command line:
blender.exe --python run_script.py
"""
if "bpy" in locals():
import importlib
importlib.reload(compat)
importlib.reload(constants)
importlib.reload(types)
importlib.reload(materials)
importlib.reload(geometry)
importlib.reload(handler)
importlib.reload(properties)
importlib.reload(operators)
importlib.reload(ui)
importlib.reload(scene_setup)
# bake_export may not exist from a prior session — import if needed
if "bake_export" in locals():
importlib.reload(bake_export)
else:
from . import bake_export
else:
import bpy
from . import compat, constants, types, materials, geometry, handler, properties, operators, ui, scene_setup, bake_export
from .compat import apply_compat_patches
from .properties import (
KnotAllowedMaterial,
KnotGlobalSettings,
KnotItem,
KnotGeneratorSettings,
)
from .operators import (
KNOT_OT_Add,
KNOT_OT_Remove,
KNOT_OT_Move,
KNOT_OT_Populate,
KNOT_OT_BakePreview,
KNOT_OT_SyncGeneratorMaterials,
KNOT_OT_FitTimeline,
KNOT_OT_FitPlaylist,
KNOT_OT_GenerateRandom,
)
from .bake_export import KNOT_OT_BakeExport
from .ui import (
KNOT_UL_List,
KNOT_UL_AllowedMaterialsList,
KNOT_PT_Panel,
)
from .geometry import _remove_existing_knot
from .handler import knot_frame_handler
from .scene_setup import setup_scene
from .constants import KNOT_MAT_NAME, SHADER_BLEND_MAT_NAME
# ---------------------------------------------------------------------------
# Add-on metadata
# ---------------------------------------------------------------------------
bl_info = {
"name": "Pr3tz",
"author": "knot_animation project",
"version": (2, 0, 0),
"blender": (5, 0, 0),
"location": "View3D > Sidebar > Pr3tz",
"description": "Procedural torus-knot animation with playlist, transitions, and 20 shader presets",
"category": "Animation",
}
# ---------------------------------------------------------------------------
# Registered Blender classes (order matters for PropertyGroup dependencies)
# ---------------------------------------------------------------------------
classes = (
KnotAllowedMaterial,
KnotGlobalSettings,
KnotItem,
KnotGeneratorSettings,
KNOT_UL_List,
KNOT_UL_AllowedMaterialsList,
KNOT_OT_SyncGeneratorMaterials,
KNOT_OT_Add,
KNOT_OT_Remove,
KNOT_OT_Move,
KNOT_OT_Populate,
KNOT_OT_BakePreview,
KNOT_OT_FitTimeline,
KNOT_OT_FitPlaylist,
KNOT_OT_GenerateRandom,
KNOT_OT_BakeExport,
KNOT_PT_Panel,
)
# ---------------------------------------------------------------------------
# Lifecycle
# ---------------------------------------------------------------------------
def register() -> None:
apply_compat_patches()
for cls in classes:
bpy.utils.register_class(cls)
bpy.types.Scene.knot_list = bpy.props.CollectionProperty(type=KnotItem)
bpy.types.Scene.knot_list_index = bpy.props.IntProperty(name="Index", default=0)
bpy.types.Scene.knot_generator = bpy.props.PointerProperty(type=KnotGeneratorSettings)
bpy.types.Scene.knot_globals = bpy.props.PointerProperty(type=KnotGlobalSettings)
# Register to frame_change_post so the handler fires *after* Blender has
# evaluated the new frame, preventing the freshly-built mesh from being
# overwritten by the depsgraph update.
handlers = bpy.app.handlers.frame_change_post
for h in list(handlers):
if getattr(h, "__name__", "") == knot_frame_handler.__name__:
handlers.remove(h)
handlers.append(knot_frame_handler)
print("[KnotScript] Handler and UI registered.")
def unregister() -> None:
for cls in reversed(classes):
bpy.utils.unregister_class(cls)
del bpy.types.Scene.knot_list
del bpy.types.Scene.knot_list_index
del bpy.types.Scene.knot_generator
del bpy.types.Scene.knot_globals
handlers = bpy.app.handlers.frame_change_post
for h in list(handlers):
if getattr(h, "__name__", "") == knot_frame_handler.__name__:
handlers.remove(h)
_remove_existing_knot()
# Purge all cached shader and blend materials on unregister to avoid
# data-block buildup on script reload. Only remove zero-user materials.
for mat in list(bpy.data.materials):
if (mat.name.startswith("KnotShader_")
or mat.name.startswith("KnotBlend_")
or mat.name.startswith("KnotItem_Preset_")
or mat.name in (KNOT_MAT_NAME, SHADER_BLEND_MAT_NAME)):
if mat.users == 0:
bpy.data.materials.remove(mat)
print("[KnotScript] Handler and UI unregistered.")
# ---------------------------------------------------------------------------
# Entry point (run as script or via --python flag)
# ---------------------------------------------------------------------------
if __name__ == "__main__":
register()
setup_scene()
# Generate the knot for the current frame immediately so the viewport is
# not empty when the script first runs.
knot_frame_handler(bpy.context.scene)
print("[KnotScript] Ready. Start scrubbing the timeline!")
print(" Press Space to play, or render with Ctrl+F12.")