Shader POC: Add shader_enabled property to UIFrame (#106)
Proof of concept for shader support on UIFrame: - Add shader and shader_enabled members to UIFrame - Add initializeTestShader() with hardcoded wave/glow fragment shader - Add shader_enabled Python property for toggling - Apply shader when drawing RenderTexture sprite - Auto-update time uniform for animated effects Also fixes position corruption when toggling RenderTexture usage: - Standard rendering path now uses `position` as source of truth - Prevents box position from staying at (0,0) after texture render Test files: - tests/shader_poc_test.py: Visual test of 6 render variants - tests/shader_toggle_test.py: Regression test for position bug Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
475fe94148
commit
41d551e6e1
4 changed files with 331 additions and 6 deletions
132
tests/shader_poc_test.py
Normal file
132
tests/shader_poc_test.py
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
Shader POC Test - Issue #106
|
||||
|
||||
Tests 6 render variants:
|
||||
1. Basic frame (no special flags)
|
||||
2. clip_children=True
|
||||
3. cache_subtree=True
|
||||
4. Basic + shader_enabled
|
||||
5. clip_children + shader_enabled
|
||||
6. cache_subtree + shader_enabled
|
||||
|
||||
The shader applies a wave distortion + glow effect.
|
||||
Shader-enabled frames should show visible animation.
|
||||
"""
|
||||
import mcrfpy
|
||||
import sys
|
||||
|
||||
# Create test scene
|
||||
scene = mcrfpy.Scene("shader_test")
|
||||
mcrfpy.current_scene = scene
|
||||
ui = scene.children
|
||||
|
||||
# Create a background
|
||||
bg = mcrfpy.Frame(pos=(0, 0), size=(1024, 768), fill_color=(30, 30, 40, 255))
|
||||
ui.append(bg)
|
||||
|
||||
# Helper to create a test frame with children
|
||||
def create_test_frame(x, y, label, clip=False, cache=False, shader=False):
|
||||
"""Create a frame with some child content for testing."""
|
||||
frame = mcrfpy.Frame(
|
||||
pos=(x, y),
|
||||
size=(200, 200),
|
||||
fill_color=(60, 80, 120, 255),
|
||||
outline_color=(200, 200, 255, 255),
|
||||
outline=2.0,
|
||||
clip_children=clip,
|
||||
cache_subtree=cache
|
||||
)
|
||||
|
||||
# Add some child content
|
||||
title = mcrfpy.Caption(text=label, pos=(5, 5), font_size=12)
|
||||
title.fill_color = (255, 255, 200, 255)
|
||||
frame.children.append(title)
|
||||
|
||||
# Add a sprite or shape inside
|
||||
inner = mcrfpy.Frame(
|
||||
pos=(20, 35),
|
||||
size=(110, 60),
|
||||
fill_color=(100, 150, 200, 255),
|
||||
outline_color=(255, 255, 255, 200),
|
||||
outline=1.0
|
||||
)
|
||||
frame.children.append(inner)
|
||||
|
||||
# Add text inside inner frame
|
||||
inner_text = mcrfpy.Caption(text="Content", pos=(25, 20), font_size=14)
|
||||
inner_text.fill_color = (255, 255, 255, 255)
|
||||
inner.children.append(inner_text)
|
||||
|
||||
# Enable shader if requested
|
||||
if shader:
|
||||
frame.shader_enabled = True
|
||||
|
||||
return frame
|
||||
|
||||
# Row 1: Without shader
|
||||
y1 = 50
|
||||
title1 = mcrfpy.Caption(text="Without Shader:", pos=(20, y1 - 30), font_size=16)
|
||||
title1.fill_color = (255, 255, 100, 255)
|
||||
ui.append(title1)
|
||||
|
||||
# 1. Basic (no flags)
|
||||
basic = create_test_frame(50, y1, "Basic", clip=False, cache=False, shader=False)
|
||||
ui.append(basic)
|
||||
|
||||
# 2. clip_children
|
||||
clipped = create_test_frame(300, y1, "clip_children", clip=True, cache=False, shader=False)
|
||||
ui.append(clipped)
|
||||
|
||||
# 3. cache_subtree
|
||||
cached = create_test_frame(550, y1, "cache_subtree", clip=False, cache=True, shader=False)
|
||||
ui.append(cached)
|
||||
|
||||
# Row 2: With shader
|
||||
y2 = 300
|
||||
title2 = mcrfpy.Caption(text="With Shader (should animate):", pos=(20, y2 - 30), font_size=16)
|
||||
title2.fill_color = (255, 255, 100, 255)
|
||||
ui.append(title2)
|
||||
|
||||
# 4. Basic + shader
|
||||
basic_shader = create_test_frame(50, y2, "Basic+Shader", clip=False, cache=False, shader=True)
|
||||
ui.append(basic_shader)
|
||||
|
||||
# 5. clip_children + shader
|
||||
clipped_shader = create_test_frame(300, y2, "clip+Shader", clip=True, cache=False, shader=True)
|
||||
ui.append(clipped_shader)
|
||||
|
||||
# 6. cache_subtree + shader
|
||||
cached_shader = create_test_frame(550, y2, "cache+Shader", clip=False, cache=True, shader=True)
|
||||
ui.append(cached_shader)
|
||||
|
||||
# Add instructions
|
||||
#instructions = mcrfpy.Caption(
|
||||
# text="Press Q or Escape to quit. Bottom row should show animated wave/glow effects.",
|
||||
# pos=(20, 400),
|
||||
# font_size=14
|
||||
#)
|
||||
#instructions.fill_color = (180, 180, 180, 255)
|
||||
#ui.append(instructions)
|
||||
|
||||
# Debug info
|
||||
#debug_info = mcrfpy.Caption(
|
||||
# text=f"Frames created: 6 variants (3 without shader, 3 with shader)",
|
||||
# pos=(20, 430),
|
||||
# font_size=12
|
||||
#)
|
||||
#debug_info.fill_color = (120, 120, 120, 255)
|
||||
#ui.append(debug_info)
|
||||
|
||||
# Keyboard handler
|
||||
def on_key(key, state):
|
||||
if state == "start" and key in ("Q", "Escape"):
|
||||
print("PASS: Shader POC test complete - exiting")
|
||||
sys.exit(0)
|
||||
|
||||
scene.on_key = on_key
|
||||
|
||||
print("Shader POC Test running...")
|
||||
print("- Top row: No shader (static)")
|
||||
print("- Bottom row: Shader enabled (should animate with wave/glow)")
|
||||
print("Press Q or Escape to quit")
|
||||
90
tests/shader_toggle_test.py
Normal file
90
tests/shader_toggle_test.py
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
Shader Toggle Test - Regression test for position corruption bug
|
||||
|
||||
Tests that toggling shader_enabled on and off does not corrupt frame position.
|
||||
This was a bug similar to #223 where box.setPosition(0,0) during texture
|
||||
rendering was never restored when switching back to standard rendering.
|
||||
"""
|
||||
import mcrfpy
|
||||
import sys
|
||||
|
||||
scene = mcrfpy.Scene("toggle_test")
|
||||
mcrfpy.current_scene = scene
|
||||
ui = scene.children
|
||||
|
||||
# Background
|
||||
bg = mcrfpy.Frame(pos=(0, 0), size=(800, 600), fill_color=(20, 20, 30, 255))
|
||||
ui.append(bg)
|
||||
|
||||
# Create test frame at a specific position
|
||||
test_frame = mcrfpy.Frame(
|
||||
pos=(200, 200),
|
||||
size=(150, 100),
|
||||
fill_color=(80, 120, 180, 255),
|
||||
outline_color=(255, 255, 255, 255),
|
||||
outline=3.0
|
||||
)
|
||||
label = mcrfpy.Caption(text="Test Frame", pos=(10, 10), font_size=14)
|
||||
label.fill_color = (255, 255, 255, 255)
|
||||
test_frame.children.append(label)
|
||||
ui.append(test_frame)
|
||||
|
||||
# Status display
|
||||
status = mcrfpy.Caption(text="Status: Initial", pos=(20, 20), font_size=16)
|
||||
status.fill_color = (255, 255, 100, 255)
|
||||
ui.append(status)
|
||||
|
||||
pos_display = mcrfpy.Caption(text="", pos=(20, 50), font_size=14)
|
||||
pos_display.fill_color = (200, 200, 200, 255)
|
||||
ui.append(pos_display)
|
||||
|
||||
instructions = mcrfpy.Caption(
|
||||
text="Press 1: Enable shader | 2: Disable shader | Q: Quit",
|
||||
pos=(20, 550), font_size=14
|
||||
)
|
||||
instructions.fill_color = (150, 150, 150, 255)
|
||||
ui.append(instructions)
|
||||
|
||||
def update_display():
|
||||
pos_display.text = f"Position: ({test_frame.x}, {test_frame.y}) | Shader: {test_frame.shader_enabled}"
|
||||
|
||||
update_display()
|
||||
|
||||
test_count = 0
|
||||
|
||||
def on_key(key, state):
|
||||
global test_count
|
||||
if state != "start":
|
||||
return
|
||||
|
||||
if key == "Num1" or key == "1":
|
||||
test_frame.shader_enabled = True
|
||||
status.text = "Status: Shader ENABLED"
|
||||
update_display()
|
||||
test_count += 1
|
||||
elif key == "Num2" or key == "2":
|
||||
test_frame.shader_enabled = False
|
||||
status.text = "Status: Shader DISABLED"
|
||||
update_display()
|
||||
test_count += 1
|
||||
|
||||
# Check if position is still correct
|
||||
if test_frame.x != 200 or test_frame.y != 200:
|
||||
status.text = f"BUG! Position corrupted to ({test_frame.x}, {test_frame.y})"
|
||||
status.fill_color = (255, 100, 100, 255)
|
||||
else:
|
||||
status.text = "Status: Shader DISABLED - Position OK!"
|
||||
status.fill_color = (100, 255, 100, 255)
|
||||
elif key in ("Q", "Escape"):
|
||||
if test_frame.x == 200 and test_frame.y == 200:
|
||||
print(f"PASS: Position remained correct after {test_count} toggles")
|
||||
else:
|
||||
print(f"FAIL: Position corrupted to ({test_frame.x}, {test_frame.y})")
|
||||
sys.exit(0)
|
||||
|
||||
scene.on_key = on_key
|
||||
|
||||
print("Shader Toggle Test")
|
||||
print("Press 1 to enable shader, 2 to disable, Q to quit")
|
||||
print("Frame should stay at (200, 200) regardless of shader state")
|
||||
Loading…
Add table
Add a link
Reference in a new issue