Big update

This commit is contained in:
Stefan Cepko
2026-06-02 05:19:38 -04:00
parent cb79e028be
commit 39a4725334
7 changed files with 593 additions and 243 deletions
+112 -15
View File
@@ -79,19 +79,30 @@ def _update_knot_material_cb(self, context) -> None:
if not self.uid:
self.uid = f"knot_{random.randint(100000, 999999)}"
if self.material_mode == 'PRESET':
_ensure_item_preset_material(self)
try:
scene = getattr(context, "scene", None)
if scene is None:
return
# Rebuild blend materials to keep transitions up to date
prebuild_playlist_blend_materials(context.scene)
if self.material_mode == 'PRESET':
_ensure_item_preset_material(self)
# Force redraw of 3-D viewport
for area in context.screen.areas:
if area.type == 'VIEW_3D':
area.tag_redraw()
# Rebuild blend materials to keep transitions up to date
prebuild_playlist_blend_materials(scene)
# Instantly update the active viewport knot's material
_update_viewport_knot_material(context.scene)
# Instantly update the active viewport knot's material
_update_viewport_knot_material(scene)
# Force redraw of 3-D viewport if screen exists
screen = getattr(context, "screen", None)
if screen:
for area in screen.areas:
if area.type == 'VIEW_3D':
area.tag_redraw()
except Exception:
# Safe fallback: if this callback fires during depsgraph evaluation
# (e.g., driven by an F-curve), context accesses will throw internal state bugs.
pass
# ---------------------------------------------------------------------------
@@ -111,10 +122,14 @@ class KnotGlobalSettings(bpy.types.PropertyGroup):
"""Scene-level playback and rendering settings."""
frames_per_knot: bpy.props.IntProperty(
name="Frames per Knot", default=12, min=1)
resolution: bpy.props.IntProperty(
name="Curve Resolution", default=128, min=3, max=1024)
bevel_resolution: bpy.props.IntProperty(
name="Bevel Resolution", default=8, min=0, max=64)
preview_resolution: bpy.props.IntProperty(
name="Preview Curve Res", default=64, min=3, max=1024)
render_resolution: bpy.props.IntProperty(
name="Render Curve Res", default=128, min=3, max=2048)
preview_bevel_resolution: bpy.props.IntProperty(
name="Preview Bevel Res", default=4, min=0, max=64)
render_bevel_resolution: bpy.props.IntProperty(
name="Render Bevel Res", default=8, min=0, max=64)
knot_scale: bpy.props.FloatProperty(
name="Global Scale", default=1.0, min=0.01)
global_speed: bpy.props.FloatProperty(
@@ -127,6 +142,38 @@ class KnotGlobalSettings(bpy.types.PropertyGroup):
name="Reactivity", default=1.0, min=0.0, max=10.0,
description="Scales all per-knot animation rates. Wire a driver here for audio-reactive animation.")
# New 8 Global Variables
global_turbulence: bpy.props.FloatProperty(
name="Turbulence (Noise)", default=0.0, min=0.0,
description="Injects 3D noise/displacement into the final curve vertices.")
global_emission_multiplier: bpy.props.FloatProperty(
name="Emission Multiplier", default=1.0, min=0.0,
description="Master scalar for the shader's emission strength.")
global_master_thickness: bpy.props.FloatProperty(
name="Master Thickness", default=1.0, min=0.0,
description="Multiplier for the knot's bevel depth.")
global_hue_shift: bpy.props.FloatProperty(
name="Hue Shift", default=0.0,
description="Offset applied to the material's color hue.")
camera_shake_amplitude: bpy.props.FloatProperty(
name="Camera Shake Amp", default=0.0, min=0.0,
description="Injects X/Y/Z jitter into the active camera's location.")
auto_turntable_speed: bpy.props.FloatProperty(
name="Turntable Speed", default=0.0,
description="Constant Z-axis rotation speed applied to the master knot object.")
smooth_shading: bpy.props.BoolProperty(
name="Smooth Shading", default=True,
description="Enforce Smooth shading on the generated mesh.")
viewport_wireframe: bpy.props.BoolProperty(
name="Viewport Wireframe", default=False,
description="Force the object to display as wireframe in the 3D viewport.")
# UI expand state
ui_show_global: bpy.props.BoolProperty(name="Global Settings", default=True)
ui_show_playback: bpy.props.BoolProperty(name="Playback", default=True)
ui_show_playlist: bpy.props.BoolProperty(name="Playlist", default=True)
ui_show_knot_edit: bpy.props.BoolProperty(name="Selected Knot", default=True)
class KnotItem(bpy.types.PropertyGroup):
"""One entry in the knot playlist."""
@@ -147,25 +194,37 @@ class KnotItem(bpy.types.PropertyGroup):
# Topology (Torus Knot)
torus_p: bpy.props.IntProperty(name="Revolutions (p)", default=2, min=1)
mod_torus_p: bpy.props.FloatProperty(name="Mod p", default=0.0)
torus_q: bpy.props.IntProperty(name="Spins (q)", default=3, min=1)
mod_torus_q: bpy.props.FloatProperty(name="Mod q", default=0.0)
# Topology (Mobius)
mobius_twists: bpy.props.IntProperty(name="Half Twists", default=1, min=1)
mod_mobius_twists: bpy.props.FloatProperty(name="Mod Twists", default=0.0)
mobius_width: bpy.props.FloatProperty(name="Width", default=1.0, min=0.1)
mod_mobius_width: bpy.props.FloatProperty(name="Mod Width", default=0.0)
# Topology (Lissajous 3D)
liss_kx: bpy.props.IntProperty(name="kx (Freq X)", default=3, min=1)
mod_liss_kx: bpy.props.FloatProperty(name="Mod kx", default=0.0)
liss_ky: bpy.props.IntProperty(name="ky (Freq Y)", default=2, min=1)
mod_liss_ky: bpy.props.FloatProperty(name="Mod ky", default=0.0)
liss_kz: bpy.props.IntProperty(name="kz (Freq Z)", default=4, min=1)
mod_liss_kz: bpy.props.FloatProperty(name="Mod kz", default=0.0)
liss_amp: bpy.props.FloatProperty(name="Amplitude", default=2.0, min=0.1)
mod_liss_amp: bpy.props.FloatProperty(name="Mod Amp", default=0.0)
# Topology (Spherical Spiral)
spiral_turns: bpy.props.IntProperty(name="Turns", default=10, min=1)
mod_spiral_turns: bpy.props.FloatProperty(name="Mod Turns", default=0.0)
spiral_R: bpy.props.FloatProperty(name="Radius", default=2.0, min=0.1)
mod_spiral_R: bpy.props.FloatProperty(name="Mod Radius", default=0.0)
# Radii (Torus Knot)
torus_R: bpy.props.FloatProperty(name="Major", default=2.0, min=0.0)
mod_torus_R: bpy.props.FloatProperty(name="Mod Major", default=0.0)
torus_r: bpy.props.FloatProperty(name="Minor", default=1.0, min=0.0)
mod_torus_r: bpy.props.FloatProperty(name="Mod Minor", default=0.0)
# Colors (legacy TKP path)
multiple_links: bpy.props.BoolProperty(name="Multiple Links", default=False)
@@ -205,7 +264,8 @@ class KnotItem(bpy.types.PropertyGroup):
# Transition
transition_frames: bpy.props.IntProperty(
name="Transition Frames", default=0, min=0,
description="Number of frames to morph into the next knot (0 = instant)")
description="Number of frames to morph into the next knot (0 = instant)",
update=_update_knot_material_cb)
transition_easing: bpy.props.EnumProperty(
name="Easing",
items=[
@@ -217,8 +277,11 @@ class KnotItem(bpy.types.PropertyGroup):
# Geometry
geo_extrude: bpy.props.FloatProperty(name="Extrude", default=0.0, min=0.0)
mod_geo_extrude: bpy.props.FloatProperty(name="Mod Extrude", default=0.0)
geo_offset: bpy.props.FloatProperty(name="Offset", default=0.0)
mod_geo_offset: bpy.props.FloatProperty(name="Mod Offset", default=0.0)
geo_bDepth: bpy.props.FloatProperty(name="Bevel Depth", default=0.04, min=0.0)
mod_geo_bDepth: bpy.props.FloatProperty(name="Mod BDepth", default=0.0)
# Dimensions mode
mode: bpy.props.EnumProperty(
@@ -226,13 +289,25 @@ class KnotItem(bpy.types.PropertyGroup):
items=[('MAJOR_MINOR', "Major/Minor", ""), ('EXT_INT', "Exterior/Interior", "")],
default='MAJOR_MINOR')
torus_eR: bpy.props.FloatProperty(name="Exterior", default=3.0, min=0.0)
mod_torus_eR: bpy.props.FloatProperty(name="Mod Ext", default=0.0)
torus_iR: bpy.props.FloatProperty(name="Interior", default=1.0, min=0.0)
mod_torus_iR: bpy.props.FloatProperty(name="Mod Int", default=0.0)
torus_h: bpy.props.FloatProperty(name="Height", default=1.0, min=0.0)
mod_torus_h: bpy.props.FloatProperty(name="Mod Height", default=0.0)
# Direction
flip_p: bpy.props.BoolProperty(name="Flip p", default=False)
flip_q: bpy.props.BoolProperty(name="Flip q", default=False)
# UI expand state (display-only, not serialised to animation)
ui_show_shape: bpy.props.BoolProperty(name="Shape", default=True)
ui_show_geometry: bpy.props.BoolProperty(name="Geometry", default=False)
ui_show_links: bpy.props.BoolProperty(name="Links & Phases", default=False)
ui_show_anim: bpy.props.BoolProperty(name="Animation Rates", default=True)
ui_show_material: bpy.props.BoolProperty(name="Material", default=True)
ui_show_colors: bpy.props.BoolProperty(name="Colors", default=False)
ui_show_trans: bpy.props.BoolProperty(name="Transition", default=False)
# Material
material_mode: bpy.props.EnumProperty(
name="Material Mode",
@@ -267,17 +342,29 @@ class KnotItem(bpy.types.PropertyGroup):
return {
"shape_type": self.shape_type,
"torus_p": self.torus_p,
"mod_torus_p": self.mod_torus_p,
"torus_q": self.torus_q,
"mod_torus_q": self.mod_torus_q,
"mobius_twists": self.mobius_twists,
"mod_mobius_twists": self.mod_mobius_twists,
"mobius_width": self.mobius_width,
"mod_mobius_width": self.mod_mobius_width,
"liss_kx": self.liss_kx,
"mod_liss_kx": self.mod_liss_kx,
"liss_ky": self.liss_ky,
"mod_liss_ky": self.mod_liss_ky,
"liss_kz": self.liss_kz,
"mod_liss_kz": self.mod_liss_kz,
"liss_amp": self.liss_amp,
"mod_liss_amp": self.mod_liss_amp,
"spiral_turns": self.spiral_turns,
"mod_spiral_turns": self.mod_spiral_turns,
"spiral_R": self.spiral_R,
"mod_spiral_R": self.mod_spiral_R,
"torus_R": self.torus_R,
"mod_torus_R": self.mod_torus_R,
"torus_r": self.torus_r,
"mod_torus_r": self.mod_torus_r,
"multiple_links": self.multiple_links,
"use_colors": self.use_colors,
"colorSet": self.colorSet,
@@ -325,6 +412,7 @@ class KnotGeneratorSettings(bpy.types.PropertyGroup):
# Standalone booleans without an associated range or prob
r_shape_type: bpy.props.BoolProperty(name="Randomize Shape Type", default=True)
r_modulations: bpy.props.BoolProperty(name="Randomize Mods", default=True, description="Add random attenuverter modulations to the generated knots")
r_transition_easing: bpy.props.BoolProperty(name="Easing", default=False)
r_material: bpy.props.BoolProperty(
name="Randomize Material", default=True,
@@ -337,6 +425,15 @@ class KnotGeneratorSettings(bpy.types.PropertyGroup):
allowed_materials: bpy.props.CollectionProperty(type=KnotAllowedMaterial)
allowed_materials_index: bpy.props.IntProperty(name="Index", default=0)
# UI expand state for generator rand sub-boxes
ui_show_rand_shape: bpy.props.BoolProperty(name="Shape", default=True)
ui_show_rand_geo: bpy.props.BoolProperty(name="Geometry", default=False)
ui_show_rand_anim: bpy.props.BoolProperty(name="Animation", default=True)
ui_show_rand_mat: bpy.props.BoolProperty(name="Material", default=True)
ui_show_base: bpy.props.BoolProperty(name="Base Knot Defaults", default=False)
ui_show_generator: bpy.props.BoolProperty(name="Random Generator", default=True)
ui_show_rand_toggles: bpy.props.BoolProperty(name="Randomise Toggles", default=True)
# ── Integer range triplets ────────────────────────────────────────────────────
_add_rand_int(KnotGeneratorSettings, "torus_p", "Revolutions (p)", 1, 8, rand_default=True, val_min=1)