=== 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)
156 lines
4.8 KiB
Python
156 lines
4.8 KiB
Python
"""
|
|
Track 01: SKIN (INTRO) - Morphing Parametric Surface
|
|
"""
|
|
|
|
import bpy
|
|
import bmesh
|
|
import math
|
|
import sys
|
|
import os
|
|
|
|
# Add parent directory to path for utils
|
|
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
|
from utils import *
|
|
|
|
|
|
def create_parametric_surface(u_segments=40, v_segments=40, t=0):
|
|
"""Create a morphing skin-like parametric surface."""
|
|
mesh = bpy.data.meshes.new("SkinSurface")
|
|
obj = bpy.data.objects.new("SkinSurface", mesh)
|
|
bpy.context.collection.objects.link(obj)
|
|
|
|
bm = bmesh.new()
|
|
|
|
# Generate vertices
|
|
verts = []
|
|
for i in range(u_segments + 1):
|
|
u = i / u_segments * math.pi
|
|
row = []
|
|
for j in range(v_segments + 1):
|
|
v = j / v_segments * 2 * math.pi
|
|
|
|
# Parametric equations with time-based morphing
|
|
x = math.sin(u) * math.cos(v) * (1 + 0.2 * math.sin(3 * u + t))
|
|
y = math.sin(u) * math.sin(v) * (1 + 0.2 * math.cos(3 * v + t))
|
|
z = math.cos(u) + 0.3 * math.sin(3 * v + 2 * u + t)
|
|
|
|
vert = bm.verts.new((x * 2, y * 2, z * 2))
|
|
row.append(vert)
|
|
verts.append(row)
|
|
|
|
bm.verts.ensure_lookup_table()
|
|
|
|
# Create faces
|
|
for i in range(u_segments):
|
|
for j in range(v_segments):
|
|
v1 = verts[i][j]
|
|
v2 = verts[i][j + 1]
|
|
v3 = verts[i + 1][j + 1]
|
|
v4 = verts[i + 1][j]
|
|
try:
|
|
bm.faces.new([v1, v2, v3, v4])
|
|
except:
|
|
pass
|
|
|
|
bm.to_mesh(mesh)
|
|
bm.free()
|
|
|
|
# Add subdivision and smooth shading
|
|
bpy.context.view_layer.objects.active = obj
|
|
bpy.ops.object.shade_smooth()
|
|
|
|
return obj
|
|
|
|
|
|
def create_skin_animation():
|
|
"""Create the full Track 01 animation."""
|
|
clear_scene()
|
|
setup_scene(background_color=COLORS["intro"])
|
|
|
|
# Create camera
|
|
camera = create_camera(location=(0, -8, 4), rotation=(1.2, 0, 0))
|
|
animate_camera_orbit(camera, center=(0, 0, 0), radius=8, height=4,
|
|
start_frame=1, end_frame=TOTAL_FRAMES, revolutions=0.5)
|
|
|
|
# Create the morphing surface using shape keys
|
|
base_surface = create_parametric_surface(t=0)
|
|
|
|
# Add material
|
|
mat = create_emission_material("SkinMaterial", COLORS["skin"], strength=1.5)
|
|
base_surface.data.materials.append(mat)
|
|
|
|
# Create shape keys for morphing animation
|
|
base_surface.shape_key_add(name="Basis")
|
|
|
|
# Create morph targets at different time values
|
|
morph_frames = [1, 125, 250, 375, 500, 625, 750]
|
|
|
|
for idx, frame in enumerate(morph_frames):
|
|
t = idx * math.pi / 2
|
|
|
|
# Create temporary mesh for this morph state
|
|
temp = create_parametric_surface(t=t)
|
|
|
|
# Add shape key from temp mesh
|
|
sk = base_surface.shape_key_add(name=f"Morph_{idx}")
|
|
|
|
# Copy vertex positions
|
|
for i, vert in enumerate(temp.data.vertices):
|
|
if i < len(sk.data):
|
|
sk.data[i].co = vert.co
|
|
|
|
# Delete temp object
|
|
bpy.data.objects.remove(temp)
|
|
|
|
# Animate shape keys
|
|
for idx, frame in enumerate(morph_frames):
|
|
for sk_idx in range(1, len(base_surface.data.shape_keys.key_blocks)):
|
|
sk = base_surface.data.shape_keys.key_blocks[sk_idx]
|
|
|
|
# Set value: 1.0 at matching frame, 0.0 at others
|
|
if sk_idx == idx + 1:
|
|
sk.value = 1.0
|
|
else:
|
|
sk.value = 0.0
|
|
sk.keyframe_insert(data_path="value", frame=frame)
|
|
|
|
# Add gentle rotation
|
|
for frame in range(1, TOTAL_FRAMES + 1, 10):
|
|
t = frame / TOTAL_FRAMES
|
|
base_surface.rotation_euler = (0, 0, t * math.pi * 0.5)
|
|
base_surface.keyframe_insert(data_path="rotation_euler", frame=frame)
|
|
|
|
# Add entrance and exit animations
|
|
keyframe_scale(base_surface, 1, 0.01)
|
|
keyframe_scale(base_surface, 90, 1.0)
|
|
keyframe_scale(base_surface, TOTAL_FRAMES - 30, 1.0)
|
|
keyframe_scale(base_surface, TOTAL_FRAMES, 0.01)
|
|
|
|
return base_surface
|
|
|
|
|
|
if __name__ == "__main__":
|
|
create_skin_animation()
|
|
|
|
# Save the blend file
|
|
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", "track01_skin.blend"))
|
|
|
|
# Export GLTF
|
|
bpy.ops.export_scene.gltf(
|
|
filepath=os.path.join(output_dir, "exports", "track01_skin.gltf"),
|
|
export_animations=True,
|
|
export_format='GLTF_SEPARATE'
|
|
)
|
|
|
|
# Export Alembic
|
|
bpy.ops.wm.alembic_export(
|
|
filepath=os.path.join(output_dir, "exports", "track01_skin.abc"),
|
|
start=1,
|
|
end=TOTAL_FRAMES,
|
|
export_hair=False,
|
|
export_particles=False
|
|
)
|
|
|
|
print("Track 01 - Skin: Export complete!")
|