Getting ready for deploy
This commit is contained in:
@@ -0,0 +1,151 @@
|
||||
# Pr3tz — Architecture Overview
|
||||
|
||||
This document describes the internal design of the add-on for contributors and developers who want to extend or debug it.
|
||||
|
||||
---
|
||||
|
||||
## High-Level Flow
|
||||
|
||||
```
|
||||
Blender Timeline scrub / playback
|
||||
│
|
||||
▼
|
||||
frame_change_post handler ── handler.py :: knot_frame_handler()
|
||||
│
|
||||
├── compute playlist position (which knot is active, transition blend weight)
|
||||
│
|
||||
├── geometry.py :: _make_torus_knot(config)
|
||||
│ └── writes NURBS control points into the shared "AnimKnot" object
|
||||
│
|
||||
└── materials.py :: apply shader / blend material to "AnimKnot"
|
||||
```
|
||||
|
||||
**Key design choice:** The handler never calls `bpy.ops` and never touches `bpy.context.space_data`. This makes it safe to run headlessly (command-line renders, bake loops) without crashes.
|
||||
|
||||
---
|
||||
|
||||
## Module Descriptions
|
||||
|
||||
### `compat.py`
|
||||
Applied once at addon load. Monkey-patches `align_matrix` in `add_curve_torus_knots` so it doesn't crash when there is no `space_data` (headless / baked renders).
|
||||
|
||||
### `constants.py`
|
||||
Pure Python — no `bpy` imports. Contains:
|
||||
- `KNOT_CONFIGS` — the default 10-entry demo playlist.
|
||||
- Scene defaults (camera position, light energy).
|
||||
- String constants for well-known object/material names.
|
||||
|
||||
### `types.py`
|
||||
A `TypedDict` called `KnotConfig` that documents every key the geometry and materials systems expect. Used for type-checking; not enforced at runtime.
|
||||
|
||||
### `properties.py`
|
||||
All Blender `PropertyGroup` subclasses:
|
||||
- `KnotAllowedMaterial` — an item in the generator's allowed-material list.
|
||||
- `KnotGlobalSettings` — scene-level animation controls.
|
||||
- `KnotItem` — one entry in the knot playlist. Includes `to_dict()` to serialize to a plain Python dict.
|
||||
- `KnotGeneratorSettings` — the random playlist generator's configuration.
|
||||
|
||||
### `geometry.py`
|
||||
`_make_torus_knot(config)` — the core parametric geometry function.
|
||||
- Accepts a `KnotConfig` dict.
|
||||
- Finds or creates the `AnimKnot` NURBS curve object.
|
||||
- Writes control points directly into `curve.splines[0].points`, bypassing `bpy.ops.curve.torus_knot_plus` for headless safety.
|
||||
- Returns immediately if the config would produce a degenerate knot.
|
||||
|
||||
### `materials.py`
|
||||
- 20 shader builder functions, each returning a `bpy.types.Material`.
|
||||
- A material **cache** (`KnotShader_<id>`) avoids rebuilding identical materials every frame.
|
||||
- **Preset materials per KnotItem** (`KnotItem_Preset_<uid>`) allow per-item color/roughness overrides without polluting the global cache.
|
||||
- **Blend materials** (`KnotBlend_<uid_a>_<uid_b>`) mix two adjacent presets during transition windows using a `MixShader` node driven by a `Value` node that the handler updates each frame.
|
||||
- `prewarm_materials_and_blends(scene)` — builds all materials upfront so the first frame has no stutter.
|
||||
- `prebuild_playlist_blend_materials(scene)` — called whenever the playlist changes (add/remove/reorder) to keep blend materials in sync.
|
||||
|
||||
### `handler.py`
|
||||
`knot_frame_handler(scene)` — the `@persistent` callback registered to `frame_change_post`.
|
||||
|
||||
Timeline math:
|
||||
```
|
||||
effective_frame = (current_frame × global_speed) + animation_phase
|
||||
|
||||
for each knot_i in playlist:
|
||||
duration_i = frames_per_knot × cycle_rate_i
|
||||
cumulative_start_i = sum(duration_j for j < i) + transition_frames_i/2
|
||||
|
||||
active_knot, blend_weight = resolve(effective_frame, playlist)
|
||||
```
|
||||
|
||||
During a transition window the handler interpolates geometry config dicts and sets the blend material's mix factor.
|
||||
|
||||
### `operators.py`
|
||||
All `KNOT_OT_*` operator classes:
|
||||
- `Add / Remove / Move` — playlist CRUD.
|
||||
- `Populate` — loads `KNOT_CONFIGS` into the playlist.
|
||||
- `BakePreview` — runs `_make_torus_knot` once for the selected item (useful while scrubbing).
|
||||
- `SyncGeneratorMaterials` — rebuilds the generator's allowed-materials list from live `bpy.data.materials`.
|
||||
- `GenerateRandom` — fills the playlist with randomly configured knots.
|
||||
- `FitTimeline / FitPlaylist` — synchronise frame range and `frames_per_knot`.
|
||||
|
||||
### `ui.py`
|
||||
- `KNOT_UL_List` — the playlist UIList.
|
||||
- `KNOT_UL_AllowedMaterialsList` — the generator's material filter UIList.
|
||||
- `draw_knot_properties(layout, item)` — renders the full KnotItem editor (topology, animation, material).
|
||||
- `KNOT_PT_Panel` — the root N-panel.
|
||||
|
||||
### `scene_setup.py`
|
||||
`setup_scene()` — called once when the add-on runs as a script. Creates or repositions the camera, area light, and sets sensible render defaults (Cycles, HDRI world, denoising).
|
||||
|
||||
### `bake_export.py`
|
||||
`KNOT_OT_BakeExport` — seven-phase bake operator. See [PARAMETERS.md](PARAMETERS.md#bake--export-options) for user-facing options.
|
||||
|
||||
`_get_action_fcurves(obj)` — walks the Blender 5 layered Action tree
|
||||
(`action → layers → strips → channelbag(slot) → fcurves`) to return an
|
||||
iterable of FCurves without touching the removed `action.fcurves` attribute.
|
||||
|
||||
Phases:
|
||||
1. **Bake geometry** — iterate every frame, convert NURBS → mesh, fingerprint for deduplication.
|
||||
2. **Visibility keyframes** — insert `hide_render` / `hide_viewport` with CONSTANT interpolation.
|
||||
3. **Camera shake bake** — convert procedural camera shake to explicit `delta_location` keyframes.
|
||||
4. **Cleanup** — remove the live `AnimKnot` NURBS object.
|
||||
5. **Pack** — make paths relative, pack external textures, purge zero-user data blocks (first chunk only).
|
||||
6. **Save copy** — `wm.save_as_mainfile(copy=True, compress=True)`.
|
||||
7. **Restore session** — remove all baked objects, re-register the handler, trigger one handler call to rebuild the live knot.
|
||||
|
||||
---
|
||||
|
||||
## Data Flow Diagram
|
||||
|
||||
```
|
||||
Scene properties (knot_list, knot_globals)
|
||||
│
|
||||
│ read by
|
||||
▼
|
||||
handler.py ──────────────────────────────────────────────────────────────────┐
|
||||
│ │
|
||||
│ _make_torus_knot(config) apply_material(knot_obj, material) │
|
||||
▼ ▼ │
|
||||
geometry.py materials.py │
|
||||
│ │ │
|
||||
└── writes into ──► "AnimKnot" ◄───┘ │
|
||||
(NURBS curve object in scene) │
|
||||
│
|
||||
bake_export.py ◄──────────────────────────────────────┘
|
||||
(iterates frames, calls frame_set which triggers handler,
|
||||
then converts knot_obj to mesh)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Adding a New Shader Preset
|
||||
|
||||
1. Open `materials.py`.
|
||||
2. Add a builder function following the pattern of existing builders (e.g. `_build_gloss_blue`). The function receives `color`, `roughness`, `metallic`, `emission_strength` keyword arguments.
|
||||
3. Add the new ID string to `SHADER_IDS` in `constants.py` **and** `materials.py`'s dispatch dict.
|
||||
4. The EnumProperty in `KnotItem` is auto-generated from `SHADER_IDS`, so the UI will pick it up automatically.
|
||||
|
||||
## Adding a New KnotItem Property
|
||||
|
||||
1. Add the `bpy.props.*` declaration to `KnotItem` in `properties.py`.
|
||||
2. Add the corresponding key to `KnotItem.to_dict()`.
|
||||
3. If the property should affect geometry, read it in `geometry.py :: _make_torus_knot()`.
|
||||
4. If it should be randomisable, add `r_<name>`, `min_<name>`, `max_<name>` entries to `KnotGeneratorSettings` in `properties.py` and wire them in `operators.py :: KNOT_OT_GenerateRandom.execute()`.
|
||||
5. Add UI for it in `ui.py :: draw_knot_properties()`.
|
||||
@@ -0,0 +1,124 @@
|
||||
# Pr3tz — Full Parameter Reference
|
||||
|
||||
This document describes every configurable parameter available in the Pr3tz add-on.
|
||||
|
||||
---
|
||||
|
||||
## Global Settings (`KnotGlobalSettings`)
|
||||
|
||||
Accessed via the **"Global Settings"** section at the top of the Pr3tz panel.
|
||||
These settings apply to the entire animation.
|
||||
|
||||
| Property | Type | Default | Description |
|
||||
|---|---|---|---|
|
||||
| `frames_per_knot` | int | 12 | Base display duration (in frames) for each playlist entry. Each knot's effective duration is `frames_per_knot × cycle_rate`. |
|
||||
| `resolution` | int | 128 | NURBS curve subdivision — controls how smooth the knot path is. Range: 3–1024. |
|
||||
| `bevel_resolution` | int | 8 | Number of sides on the tube cross-section. Range: 0–64. |
|
||||
| `knot_scale` | float | 1.0 | Uniform scale multiplier applied to all knots. |
|
||||
| `global_speed` | float | 1.0 | Multiplies the effective frame counter for the entire playlist. Keyframeable. |
|
||||
| `animation_phase` | float | 0.0 | Frame offset added before all calculations. Keyframe or drive for reactive control. |
|
||||
| `reactivity_factor` | float | 1.0 | Scales all per-knot animation rates (spin, revolution, height, scale). Wire a driver to an audio amplitude for audio-reactive animation. Range: 0–10. |
|
||||
|
||||
---
|
||||
|
||||
## Per-Knot Settings (`KnotItem`)
|
||||
|
||||
Each entry in the playlist has its own complete set of parameters.
|
||||
|
||||
### Topology
|
||||
|
||||
| Property | Type | Default | Description |
|
||||
|---|---|---|---|
|
||||
| `torus_p` | int | 2 | Number of revolutions around the torus axis. With `q`, defines the knot type. Must be coprime with `q` for a true knot. |
|
||||
| `torus_q` | int | 3 | Number of spins around the torus tube. Must be coprime with `p`. |
|
||||
| `flip_p` | bool | False | Reverse the direction of the p (revolution) component. |
|
||||
| `flip_q` | bool | False | Reverse the direction of the q (spin) component. |
|
||||
| `multiple_links` | bool | False | Render all `gcd(p, q)` link components as separate curves. |
|
||||
| `torus_u` | int | 1 | Revolution multiplier — repeats the revolution pattern. |
|
||||
| `torus_v` | int | 1 | Spin multiplier — repeats the spin pattern. |
|
||||
| `torus_rP` | float | 0.0 | Revolution phase offset (orbit rotation). |
|
||||
| `torus_sP` | float | 0.0 | Spin phase offset (tube rotation). |
|
||||
|
||||
### Dimensions — Major/Minor Mode
|
||||
|
||||
| Property | Type | Default | Description |
|
||||
|---|---|---|---|
|
||||
| `mode` | enum | `MAJOR_MINOR` | Choose `MAJOR_MINOR` or `EXT_INT` dimension mode. |
|
||||
| `torus_R` | float | 2.0 | Major radius — distance from centre of torus to centre of tube. |
|
||||
| `torus_r` | float | 1.0 | Minor radius — radius of the tube itself. |
|
||||
| `torus_h` | float | 1.0 | Height scaling of the torus. Values > 1 stretch the knot vertically. |
|
||||
|
||||
### Dimensions — Exterior/Interior Mode
|
||||
|
||||
| Property | Type | Default | Description |
|
||||
|---|---|---|---|
|
||||
| `torus_eR` | float | 3.0 | Exterior radius (outer edge of the torus). |
|
||||
| `torus_iR` | float | 1.0 | Interior radius (inner edge / hole radius). |
|
||||
|
||||
### Geometry
|
||||
|
||||
| Property | Type | Default | Description |
|
||||
|---|---|---|---|
|
||||
| `geo_bDepth` | float | 0.04 | Bevel depth — tube thickness. |
|
||||
| `geo_extrude` | float | 0.0 | Extrude the curve profile outward. Creates a ribbon effect when combined with `geo_offset`. |
|
||||
| `geo_offset` | float | 0.0 | Offset the extruded profile from the curve centreline. |
|
||||
|
||||
### Animation Rates
|
||||
|
||||
All rate properties are **scaled by `reactivity_factor`** before being applied.
|
||||
|
||||
| Property | Type | Default | Description |
|
||||
|---|---|---|---|
|
||||
| `cycle_rate` | float | 1.0 | Per-knot speed multiplier. `> 1` makes this knot linger longer; `< 1` advances it faster. |
|
||||
| `spin_phase_rate` | float | 0.0 | Rate of change of spin phase (tube rotation) per frame. |
|
||||
| `rev_phase_rate` | float | 0.0 | Rate of change of revolution phase (orbit rotation) per frame. |
|
||||
| `height_rate` | float | 0.0 | Oscillation frequency of torus height. Creates a breathing / pulsing warp. |
|
||||
| `scale_rate` | float | 0.0 | Frequency of per-knot scale oscillation. |
|
||||
| `scale_amplitude` | float | 0.0 | Amplitude of per-knot scale oscillation (0 = no oscillation). |
|
||||
|
||||
### Transitions
|
||||
|
||||
| Property | Type | Default | Description |
|
||||
|---|---|---|---|
|
||||
| `transition_frames` | int | 0 | Number of frames to smoothly morph from the *previous* knot into this one. 0 = instant cut. |
|
||||
| `transition_easing` | enum | `QUAD_IN_OUT` | Interpolation curve for the morph: `LINEAR`, `QUAD_IN_OUT`, or `SMOOTHSTEP`. |
|
||||
|
||||
### Material
|
||||
|
||||
| Property | Type | Default | Description |
|
||||
|---|---|---|---|
|
||||
| `material_mode` | enum | `PRESET` | `PRESET` uses a built-in shader; `PROJECT` uses an existing material from the .blend file. |
|
||||
| `shader_id` | enum | `GLOSS_BLUE` | Which of the 20 built-in shader presets to use. |
|
||||
| `preset_color` | color | (0.2, 0.6, 1.0) | Base color tint passed into the selected shader preset. |
|
||||
| `preset_roughness` | float | 0.1 | Roughness override for the preset. |
|
||||
| `preset_metallic` | float | 0.0 | Metallic override for the preset. |
|
||||
| `preset_emission_strength` | float | 1.0 | Emission strength override for presets that emit light. Range: 0–100. |
|
||||
| `project_material` | Material | — | A material data-block from the current .blend. Active when `material_mode = PROJECT`. |
|
||||
|
||||
---
|
||||
|
||||
## Fit Timeline / Fit Playlist Operators
|
||||
|
||||
| Operator | Description |
|
||||
|---|---|
|
||||
| **Fit Timeline to Playlist** | Extends `frame_end` so the full playlist plays exactly once, accounting for each knot's `cycle_rate`. |
|
||||
| **Fit Playlist to Timeline** | Adjusts `frames_per_knot` so the playlist fills the current `frame_end` exactly. |
|
||||
|
||||
---
|
||||
|
||||
## Bake & Export Options (`KNOT_OT_BakeExport`)
|
||||
|
||||
| Option | Default | Description |
|
||||
|---|---|---|
|
||||
| **Use Render Resolution** | True | Temporarily sets preview resolution to match render resolution during baking for highest-quality mesh output. |
|
||||
| **Pack Textures** | True | Packs all external textures into the exported `.blend` file. |
|
||||
| **Split Export** | False | Splits the baked animation into multiple `.blend` files. |
|
||||
| **Frames Per File** | 500 | When split export is on, the number of frames included in each file. |
|
||||
|
||||
The bake operator:
|
||||
1. Iterates every frame in `frame_start`–`frame_end`.
|
||||
2. Converts the procedural NURBS curve to a mesh via `new_from_object`.
|
||||
3. Uses a MD5 fingerprint to skip duplicate frames (extends the previous object's visibility window instead).
|
||||
4. Inserts `hide_render` / `hide_viewport` keyframes with CONSTANT interpolation so only the correct mesh shows each frame.
|
||||
5. Optionally bakes camera shake to `delta_location` keyframes.
|
||||
6. Saves a copy of the file, then restores the session completely.
|
||||
Reference in New Issue
Block a user