Skip to content

Commit 16e9007

Browse files
committed
feat: Fix lyrics positioning and add debug visualization mode
IMPROVEMENTS: - Fixed lyrics text positioning to appear in front of mascot (not behind) - Changed position from (0, 0, -0.5) to (0, -2, 0.2) - Text now properly visible in lower third of frame (subtitle position) - Y=-2 puts text between camera and mascot for better visibility - Added debug visualization mode for troubleshooting positioning - Enable with 'debug_mode: true' in config.yaml under 'advanced' - Shows colored sphere markers at key positions: * Red: Camera position * Green: Mascot position * Blue: Text zone position * Yellow: World origin - Each marker includes text label for easy identification - Added comprehensive POSITIONING_GUIDE.md documentation - Explains scene coordinate system - Visual diagrams of positioning - How lip sync and lyrics synchronization works - Troubleshooting common issues - Best practices for positioning adjustments TECHNICAL DETAILS: - Updated blender_script.py:563-570 (lyrics positioning) - Added blender_script.py:1046-1117 (debug visualizers) - Updated config.yaml with debug_mode option - Scene layout: Camera(0,-6,1) → Text(0,-2,0.2) → Mascot(0,0,1) SYNCHRONIZATION CLARIFICATION: - Lip sync: Automatically synced to audio via phoneme extraction - Lyrics: Manually timed via lyrics.txt file - Both use same audio file for consistent timing reference
1 parent c67fb8f commit 16e9007

File tree

3 files changed

+338
-4
lines changed

3 files changed

+338
-4
lines changed

POSITIONING_GUIDE.md

Lines changed: 252 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,252 @@
1+
# Positioning Guide - Lyrics and Mascot
2+
3+
## Overview
4+
5+
This guide explains how the mascot and lyrics positioning works in the Semantic Foragecast Engine, and how to debug positioning issues.
6+
7+
## Scene Layout
8+
9+
### Coordinate System
10+
11+
The scene uses Blender's coordinate system:
12+
- **X-axis**: Left (-) to Right (+)
13+
- **Y-axis**: Back (+) to Front (-)
14+
- **Z-axis**: Down (-) to Up (+)
15+
16+
### Key Positions
17+
18+
| Element | Position (X, Y, Z) | Description |
19+
|---------|-------------------|-------------|
20+
| **Mascot** | (0, 0, 1) | Center of scene, 1 unit above origin |
21+
| **Camera** | (0, -6, 1) | 6 units in front, looking back at mascot |
22+
| **Lyrics Text** | (0, -2, 0.2) | 2 units in front of mascot, lower on screen |
23+
| **Origin** | (0, 0, 0) | World center |
24+
25+
### Visual Layout
26+
27+
```
28+
[Camera at Y=-6]
29+
|
30+
| (looking this direction)
31+
v
32+
[Text at Y=-2, Z=0.2]
33+
34+
[Mascot at Y=0, Z=1]
35+
36+
----- [Stage at Z=0] -----
37+
```
38+
39+
## Lyrics Positioning
40+
41+
### Default Setup (Fixed in Latest Version)
42+
43+
**Previous Issue:**
44+
- Lyrics were positioned at `(0, 0, -0.5)`
45+
- This put them **behind** the mascot and often off-screen
46+
47+
**Current Fix:**
48+
- Lyrics now positioned at `(0, -2, 0.2)`
49+
- Y=-2: Closer to camera than mascot (better visibility)
50+
- Z=0.2: In lower third of frame (subtitle position)
51+
52+
### Why This Works
53+
54+
1. **Camera at Y=-6** looks toward positive Y direction
55+
2. **Text at Y=-2** is between camera and mascot
56+
3. **Text appears "in front"** from camera's perspective
57+
4. **Lower Z value (0.2)** places text in lower screen area
58+
59+
## Debug Mode
60+
61+
### Enabling Debug Visualization
62+
63+
Add this to your `config.yaml`:
64+
65+
```yaml
66+
advanced:
67+
debug_mode: true
68+
```
69+
70+
### What Debug Mode Shows
71+
72+
When enabled, colored sphere markers appear at key positions:
73+
74+
- 🔴 **Red Sphere**: Camera position
75+
- 🟢 **Green Sphere**: Mascot position
76+
- 🔵 **Blue Sphere**: Text zone position
77+
- 🟡 **Yellow Sphere**: World origin
78+
79+
Each marker includes a text label for easy identification.
80+
81+
### Using Debug Mode
82+
83+
1. Enable `debug_mode: true` in config
84+
2. Run the pipeline: `python main.py`
85+
3. Check the first rendered frame
86+
4. Verify all elements are positioned correctly
87+
5. Disable debug mode for final render
88+
89+
## Adjusting Positions
90+
91+
### Moving Lyrics Horizontally
92+
93+
Edit `blender_script.py` line ~567:
94+
95+
```python
96+
y_position = -2.0 # More negative = closer to camera
97+
```
98+
99+
- `-1.5`: Very close to camera (large text)
100+
- `-2.0`: Default (good visibility)
101+
- `-3.0`: Further from camera (smaller text)
102+
103+
### Moving Lyrics Vertically
104+
105+
Edit `blender_script.py` line ~568:
106+
107+
```python
108+
z_position = 0.2 # Higher = moves up on screen
109+
```
110+
111+
- `0.5`: Middle of screen
112+
- `0.2`: Lower third (subtitle position) - DEFAULT
113+
- `-0.2`: Bottom of screen
114+
115+
### Moving Lyrics Left/Right
116+
117+
Add X-offset to text creation:
118+
119+
```python
120+
bpy.ops.object.text_add(location=(x_offset, y_position, z_position))
121+
```
122+
123+
- Negative X: Left
124+
- Positive X: Right
125+
- 0: Center (default)
126+
127+
## Synchronization
128+
129+
### How Lip Sync Works
130+
131+
1. **Audio File** → Analyzed by Phase 1 (`prep_audio.py`)
132+
2. **Phonemes** → Extracted via Rhubarb or mock generation
133+
3. **Timing Data** → Stored in `prep_data.json`
134+
4. **Blender** → Applies phoneme shape keys to mascot mesh
135+
5. **Result** → Mascot mouth moves in sync with audio
136+
137+
### How Lyrics Sync Works
138+
139+
1. **Lyrics File** (`lyrics.txt`) → Manually timed by you
140+
2. **Format**: `START-END word|word|word`
141+
3. **Phase 1** → Parses timing and words
142+
4. **Blender** → Creates text objects with timed visibility
143+
5. **Result** → Words appear/disappear at specified times
144+
145+
### Important: Manual Sync Required
146+
147+
⚠️ **The lyrics timing is NOT automatically synced to the audio!**
148+
149+
You must manually ensure:
150+
- Lyrics timestamps match when words are actually sung
151+
- Format: `0:00-0:03 Hello|world|test`
152+
- Each word gets equal time in its range
153+
154+
### Example Lyrics File
155+
156+
```
157+
0:00-0:03 Welcome|to|the|show
158+
0:03-0:06 Dancing|in|the|lights
159+
0:06-0:09 Music|brings|us|together
160+
```
161+
162+
## Common Issues
163+
164+
### Issue: Lyrics Not Visible
165+
166+
**Symptoms**: Text doesn't appear in rendered frames
167+
168+
**Solutions**:
169+
1. Enable `debug_mode: true` to see text zone marker
170+
2. Check lyrics file exists and has content
171+
3. Verify `enable_lyrics: true` in config
172+
4. Ensure text timing overlaps with rendered frames
173+
174+
### Issue: Lyrics Behind Mascot
175+
176+
**Symptoms**: Text is blocked by mascot
177+
178+
**Solution**:
179+
- Already fixed in latest version
180+
- Text now at Y=-2 (in front of mascot at Y=0)
181+
182+
### Issue: Lip Sync Not Working
183+
184+
**Symptoms**: Mascot mouth doesn't move
185+
186+
**Solutions**:
187+
1. Check `enable_lipsync: true` in config
188+
2. Verify phoneme data in `prep_data.json`
189+
3. Ensure mascot has shape keys (check Blender output)
190+
4. For 3D mode: Requires actual mesh deformation (currently stub)
191+
192+
### Issue: Lyrics Out of Sync
193+
194+
**Symptoms**: Words appear at wrong time
195+
196+
**Solution**:
197+
- Edit `lyrics.txt` manually to match audio timing
198+
- Use audio editor to find exact timestamps
199+
- Format: `MM:SS-MM:SS word|word|word`
200+
201+
## Technical Details
202+
203+
### Text Object Properties
204+
205+
Default text configuration:
206+
- **Size**: 0.6-0.8 units (0.8 for professional style)
207+
- **Alignment**: Centered X and Y
208+
- **Material**: Emission shader (glows)
209+
- **Extrusion**: 0.1-0.15 units (3D depth)
210+
- **Animation**: Scale bounce or professional fade
211+
212+
### Text Materials
213+
214+
Professional style:
215+
- 70% Emission (glow)
216+
- 30% Glossy (reflective)
217+
- Accent color from config
218+
- Emission strength: 2.0
219+
220+
### Animation Timing
221+
222+
Lyrics animation phases:
223+
1. **Appear** (frames 1-5): Scale from 0.1 to 1.0
224+
2. **Display** (bulk of duration): Visible at scale 1.0
225+
3. **Pulse** (mid-point): Brief scale to 1.1
226+
4. **Disappear** (last 3 frames): Hide
227+
228+
## Best Practices
229+
230+
1. **Always test with debug mode first** when changing positions
231+
2. **Use preview mode** (`preview_mode: true`) for fast iteration
232+
3. **Check first frame** to verify positioning before full render
233+
4. **Match lyrics timing** carefully to audio for best results
234+
5. **Use professional style** for production-quality text rendering
235+
236+
## Related Files
237+
238+
- `blender_script.py` - Main positioning code (lines 563-570, 1046-1117)
239+
- `grease_pencil.py` - 2D mode text positioning (lines 590-650)
240+
- `config.yaml` - Configuration including debug_mode
241+
- `assets/lyrics.txt` - Lyrics timing file
242+
243+
## Version History
244+
245+
- **v1.0**: Initial implementation (text behind mascot)
246+
- **v1.1**: Fixed positioning (text in front at Y=-2, Z=0.2)
247+
- **v1.1**: Added debug visualization mode
248+
249+
---
250+
251+
**Last Updated**: 2025-11-18
252+
**Related**: See README.md for full pipeline documentation

blender_script.py

Lines changed: 82 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -560,10 +560,12 @@ def create_lyrics_text(self):
560560
start_time = word_data['start']
561561
end_time = word_data['end']
562562

563-
# Create text object - position BELOW mascot so it's visible from front camera
564-
# Mascot is at (0, 0, 1), text should be in front and below
565-
y_position = 0.0 # Same depth as mascot
566-
z_position = -0.5 # Below mascot (mascot is at z=1, this puts text at z=0.5 after mascot size)
563+
# Create text object - position in front and below mascot for visibility
564+
# Camera is at (0, -6, 1) looking at mascot at (0, 0, 1)
565+
# Text should be closer to camera (more negative Y) and lower on screen (lower Z)
566+
# This puts text in the lower third of the frame, standard for subtitles
567+
y_position = -2.0 # Closer to camera than mascot for better visibility
568+
z_position = 0.2 # Below mascot center (mascot at z=1, this is ~0.8 below)
567569

568570
bpy.ops.object.text_add(location=(0, y_position, z_position))
569571
text_obj = bpy.context.object
@@ -1041,6 +1043,79 @@ def setup_compositor(self):
10411043

10421044
print(f"[OK] Compositor configured with {effects_count} effects")
10431045

1046+
def add_debug_visualizers(self):
1047+
"""
1048+
Add visual markers to help debug scene positioning.
1049+
Creates small sphere markers at key positions with labels.
1050+
Enable by setting 'debug_mode: true' in config.yaml under 'advanced'.
1051+
"""
1052+
if not self.config.get('advanced', {}).get('debug_mode', False):
1053+
return
1054+
1055+
print("Adding debug visualization markers...")
1056+
1057+
# Marker positions to visualize
1058+
markers = [
1059+
{"name": "Camera", "location": (0, -6, 1), "color": (1, 0, 0)}, # Red
1060+
{"name": "Mascot", "location": (0, 0, 1), "color": (0, 1, 0)}, # Green
1061+
{"name": "Text_Zone", "location": (0, -2, 0.2), "color": (0, 0, 1)}, # Blue
1062+
{"name": "Origin", "location": (0, 0, 0), "color": (1, 1, 0)}, # Yellow
1063+
]
1064+
1065+
for marker in markers:
1066+
# Create small sphere
1067+
bpy.ops.mesh.primitive_uv_sphere_add(
1068+
radius=0.1,
1069+
location=marker["location"]
1070+
)
1071+
sphere = bpy.context.object
1072+
sphere.name = f"DEBUG_{marker['name']}"
1073+
1074+
# Create emission material so it's always visible
1075+
mat = bpy.data.materials.new(name=f"Debug_{marker['name']}")
1076+
mat.use_nodes = True
1077+
nodes = mat.node_tree.nodes
1078+
nodes.clear()
1079+
1080+
emission = nodes.new('ShaderNodeEmission')
1081+
emission.inputs['Color'].default_value = (*marker['color'], 1.0)
1082+
emission.inputs['Strength'].default_value = 5.0
1083+
1084+
output = nodes.new('ShaderNodeOutputMaterial')
1085+
mat.node_tree.links.new(emission.outputs[0], output.inputs[0])
1086+
1087+
sphere.data.materials.append(mat)
1088+
1089+
# Add text label
1090+
bpy.ops.object.text_add(location=(
1091+
marker["location"][0] + 0.2,
1092+
marker["location"][1],
1093+
marker["location"][2] + 0.2
1094+
))
1095+
text = bpy.context.object
1096+
text.name = f"DEBUG_Label_{marker['name']}"
1097+
text.data.body = marker['name']
1098+
text.data.size = 0.15
1099+
text.data.align_x = 'LEFT'
1100+
1101+
# Small emission for text
1102+
text_mat = bpy.data.materials.new(name=f"Debug_Text_{marker['name']}")
1103+
text_mat.use_nodes = True
1104+
text_nodes = text_mat.node_tree.nodes
1105+
text_nodes.clear()
1106+
1107+
text_emission = text_nodes.new('ShaderNodeEmission')
1108+
text_emission.inputs['Color'].default_value = (1, 1, 1, 1)
1109+
text_emission.inputs['Strength'].default_value = 3.0
1110+
1111+
text_output = text_nodes.new('ShaderNodeOutputMaterial')
1112+
text_mat.node_tree.links.new(text_emission.outputs[0], text_output.inputs[0])
1113+
1114+
text.data.materials.append(text_mat)
1115+
1116+
print(f"[OK] Added {len(markers)} debug markers")
1117+
print(" Markers: Camera (red), Mascot (green), Text_Zone (blue), Origin (yellow)")
1118+
10441119
def render_animation(self):
10451120
"""Render the animation."""
10461121
print("=" * 70)
@@ -1169,6 +1244,9 @@ def main():
11691244
lyrics = builder.create_lyrics_text()
11701245
builder.animate_lights_to_beats(lights)
11711246

1247+
# Add debug visualizers if enabled
1248+
builder.add_debug_visualizers()
1249+
11721250
# Render setup
11731251
builder.setup_render_settings()
11741252
builder.setup_compositor()

config.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,10 @@ advanced:
158158
# Number of CPU threads for rendering (null = auto)
159159
threads: null
160160

161+
# Debug mode - adds visual markers showing camera, mascot, and text positions
162+
# Useful for troubleshooting positioning issues
163+
debug_mode: false
164+
161165
# Blender settings
162166
blender:
163167
# Path to Blender executable (null = auto-detect from PATH)

0 commit comments

Comments
 (0)