=== WHAT'S BEEN DONE (Recent) === MCP Pipeline Factory: - 38 MCP servers tracked across 7 pipeline stages - 31 servers at Stage 16 (Website Built) — ready to deploy - All 30 production servers patched to 100/100 protocol compliance - Built complete testing infra: mcp-jest, mcp-validator, mcp-add, MCP Inspector - 702 auto-generated test cases ready for live API testing - Autonomous pipeline operator system w/ 7 Discord channels + cron jobs - Dashboard live at 192.168.0.25:8888 (drag-drop kanban) CloseBot MCP: - 119 tools, 4,656 lines TypeScript, compiles clean - 14 modules (8 tool groups + 6 UI apps) GHL MCP: - Stage 11 (Edge Case Testing) — 42 failing tests identified Sub-agent _meta Labels: - All 643 tools across 5 MCPs tagged (GHL, Google Ads, Meta Ads, Google Console, Twilio) OpenClaw Upwork Launch: - 15 graphics, 6 mockups, 2 PDFs, 90-sec Remotion video - 3-tier pricing: $2,499 / $7,499 / $24,999 - First $20k deal closed + $2k/mo retainer (hospice) Other: - Surya Blender animation scripts (7 tracks) - Clawdbot architecture deep dive doc - Pipeline state.json updates === TO-DO (Open Items) === BLOCKERS: - [ ] GHL MCP: Fix 42 failing edge case tests (Stage 11) - [ ] Expired Anthropic API key in localbosses-app .env.local - [ ] Testing strategy decision: structural vs live API vs hybrid NEEDS API KEYS (can't progress without): - [ ] Meta Ads MCP — needs META_ADS_API_KEY for Stage 8→9 - [ ] Twilio MCP — needs TWILIO_API_KEY for Stage 8→9 - [ ] CloseBot MCP — needs CLOSEBOT_API_KEY for live testing - [ ] 702 test cases across all servers need live API credentials PIPELINE ADVANCEMENT: - [ ] Stage 7→8: CloseBot + Google Console need design approval - [ ] Stage 6→7: 22 servers need UI apps built - [ ] Stage 5→6: 5 servers need core tools built (FreshBooks, Gusto, Jobber, Keap, Lightspeed) - [ ] Stage 1→5: 3 new MCPs need scaffolding (Compliance GRC, HR People Ops, Product Analytics) PENDING REVIEW: - [ ] Jake review OpenClaw video + gallery → finalize Upwork listing - [ ] LocalBosses UI redesign (Steve Jobs critique delivered, recs available) QUEUED PROJECTS: - [ ] SongSense AI music analysis product (architecture done, build not started) - [ ] 8-Week Agent Study Plan execution (curriculum posted, Week 1 not started)
151 lines
4.9 KiB
Python
151 lines
4.9 KiB
Python
"""
|
|
Track 03: NOTHING - Sphere Explosion/Fragmentation into Void
|
|
"""
|
|
|
|
import bpy
|
|
import bmesh
|
|
import math
|
|
import random
|
|
import sys
|
|
import os
|
|
|
|
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
|
from utils import *
|
|
|
|
|
|
def create_fragments(count=60, sphere_radius=2.0):
|
|
"""Create fragment particles from a sphere."""
|
|
fragments = []
|
|
mat = create_emission_material("FragmentMat", COLORS["nothing"], strength=1.0)
|
|
|
|
for i in range(count):
|
|
# Random position within sphere
|
|
theta = random.uniform(0, 2 * math.pi)
|
|
phi = random.uniform(0, math.pi)
|
|
r = random.uniform(0, sphere_radius * 0.9)
|
|
|
|
x = r * math.sin(phi) * math.cos(theta)
|
|
y = r * math.sin(phi) * math.sin(theta)
|
|
z = r * math.cos(phi)
|
|
|
|
size = random.uniform(0.15, 0.4)
|
|
|
|
bpy.ops.mesh.primitive_ico_sphere_add(
|
|
radius=size,
|
|
subdivisions=1,
|
|
location=(x, y, z)
|
|
)
|
|
frag = bpy.context.active_object
|
|
frag.name = f"Fragment_{i:03d}"
|
|
frag.data.materials.append(mat)
|
|
|
|
# Store explosion direction
|
|
if r > 0.1:
|
|
direction = (x/r, y/r, z/r)
|
|
else:
|
|
direction = (random.uniform(-1, 1), random.uniform(-1, 1), random.uniform(-1, 1))
|
|
norm = math.sqrt(sum(d*d for d in direction))
|
|
direction = tuple(d/norm for d in direction)
|
|
|
|
fragments.append((frag, (x, y, z), direction))
|
|
|
|
return fragments
|
|
|
|
|
|
def create_nothing_animation():
|
|
"""Create the full Track 03 animation."""
|
|
clear_scene()
|
|
setup_scene(background_color=(0.04, 0.04, 0.04, 1.0)) # Near black
|
|
|
|
# Create camera
|
|
camera = create_camera(location=(0, -10, 4), rotation=(1.15, 0, 0))
|
|
animate_camera_orbit(camera, center=(0, 0, 0), radius=10, height=4,
|
|
start_frame=1, end_frame=TOTAL_FRAMES, revolutions=0.3)
|
|
|
|
# Create initial sphere
|
|
sphere = create_sphere(location=(0, 0, 0), radius=2.0, segments=32, rings=16, name="MainSphere")
|
|
sphere_mat = create_emission_material("SphereMat", COLORS["nothing"], strength=1.2)
|
|
sphere.data.materials.append(sphere_mat)
|
|
|
|
# Animate sphere appearance
|
|
keyframe_scale(sphere, 1, 0.01)
|
|
keyframe_scale(sphere, 60, 1.0)
|
|
keyframe_scale(sphere, 90, 1.0)
|
|
|
|
# Explosion at frame 120
|
|
explosion_frame = 120
|
|
|
|
# Sphere disappears at explosion
|
|
keyframe_scale(sphere, explosion_frame - 1, 1.0)
|
|
keyframe_scale(sphere, explosion_frame, 0.01)
|
|
|
|
# Create fragments
|
|
fragments = create_fragments(count=60, sphere_radius=2.0)
|
|
|
|
# Animate fragments
|
|
for frag, start_pos, direction in fragments:
|
|
# Start invisible
|
|
keyframe_scale(frag, 1, 0.01)
|
|
keyframe_scale(frag, explosion_frame - 1, 0.01)
|
|
|
|
# Appear at explosion
|
|
keyframe_scale(frag, explosion_frame, 1.0)
|
|
keyframe_location(frag, explosion_frame, start_pos)
|
|
|
|
# Drift outward into void
|
|
drift_distance = random.uniform(6, 12)
|
|
end_pos = tuple(start_pos[i] + direction[i] * drift_distance for i in range(3))
|
|
|
|
# Animate drift
|
|
keyframe_location(frag, TOTAL_FRAMES, end_pos)
|
|
|
|
# Fade out (scale down)
|
|
keyframe_scale(frag, TOTAL_FRAMES - 60, 0.6)
|
|
keyframe_scale(frag, TOTAL_FRAMES, 0.1)
|
|
|
|
# Set linear interpolation for drift
|
|
if frag.animation_data:
|
|
for fc in frag.animation_data.action.fcurves:
|
|
for kf in fc.keyframe_points:
|
|
kf.interpolation = 'LINEAR'
|
|
|
|
# Add some ambient dust particles
|
|
dust = []
|
|
dust_mat = create_emission_material("DustMat", COLORS["nothing"], strength=0.3)
|
|
|
|
for i in range(40):
|
|
pos = [random.uniform(-6, 6) for _ in range(3)]
|
|
bpy.ops.mesh.primitive_ico_sphere_add(radius=0.05, subdivisions=0, location=pos)
|
|
d = bpy.context.active_object
|
|
d.name = f"Dust_{i:03d}"
|
|
d.data.materials.append(dust_mat)
|
|
|
|
# Slow random drift
|
|
end_pos = [pos[j] + random.uniform(-2, 2) for j in range(3)]
|
|
keyframe_location(d, 1, pos)
|
|
keyframe_location(d, TOTAL_FRAMES, end_pos)
|
|
|
|
dust.append(d)
|
|
|
|
return sphere, fragments, dust
|
|
|
|
|
|
if __name__ == "__main__":
|
|
create_nothing_animation()
|
|
|
|
output_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
|
bpy.ops.wm.save_as_mainfile(filepath=os.path.join(output_dir, "exports", "track03_nothing.blend"))
|
|
|
|
bpy.ops.export_scene.gltf(
|
|
filepath=os.path.join(output_dir, "exports", "track03_nothing.gltf"),
|
|
export_animations=True,
|
|
export_format='GLTF_SEPARATE'
|
|
)
|
|
|
|
bpy.ops.wm.alembic_export(
|
|
filepath=os.path.join(output_dir, "exports", "track03_nothing.abc"),
|
|
start=1, end=TOTAL_FRAMES
|
|
)
|
|
|
|
print("Track 03 - Nothing: Export complete!")
|