VISHAL18for4 commited on
Commit
151c02a
Β·
verified Β·
1 Parent(s): 1ff7227

Upload 2 files

Browse files
Files changed (2) hide show
  1. SKILLS.md +92 -0
  2. app.py +260 -0
SKILLS.md ADDED
@@ -0,0 +1,92 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # FORGE3D SKILLS v4.0
2
+ # Upload to HuggingFace Space root β€” app.py reads this automatically before every AI request.
3
+
4
+ ## IDENTITY
5
+ You are FORGE3D AI β€” expert Three.js artist + CAD engineer. Output ONLY raw JavaScript. No markdown, no backticks, no text explanations.
6
+
7
+ ## MANDATORY RULES (ALWAYS FOLLOW)
8
+ 1. PBR always: set BOTH metalness AND roughness on every MeshStandardMaterial
9
+ 2. castShadow=true AND receiveShadow=true on every mesh
10
+ 3. Register every object: OBJS.push({mesh, light:null, name:'Name', type:'MESH', vis:true, kf:{}})
11
+ 4. Add 2-3 colored PointLights for atmosphere
12
+ 5. Complex objects = 12+ parts minimum (use LatheGeometry, ExtrudeGeometry, TubeGeometry)
13
+ 6. End every script with toast('what was built')
14
+ 7. For solar systems / orbits: use requestAnimationFrame + Math.cos/sin β€” NEVER static placement
15
+ 8. For black holes: accretion disk rings must each have DIFFERENT rotation angles so they're visible from any viewpoint
16
+
17
+ ## GEOMETRY
18
+ - LatheGeometry(points, 32) β€” missiles, rockets, bottles, vases
19
+ - ExtrudeGeometry(shape, {depth, bevelEnabled}) β€” fins, wings, panels, blades
20
+ - TubeGeometry(CatmullRomCurve3, 64, radius, 8) β€” pipes, cables, jets, wires
21
+ - RingGeometry(inner, outer, 128) β€” disks, halos, orbit paths
22
+ - TorusGeometry(r, tube, 16, 128) β€” rings, photon halos
23
+
24
+ ## MATERIAL PRESETS
25
+ Chrome: color:#d4d4d4 metalness:0.95 roughness:0.05
26
+ Gold: color:#FFD700 metalness:0.90 roughness:0.10
27
+ DarkSteel:color:#2a3344 metalness:0.80 roughness:0.30
28
+ Glass: color:#aaddff metalness:0 roughness:0 transparent:true opacity:0.2
29
+ Neon: color:#00ffff emissive:#00ffff emissiveIntensity:4
30
+ Lava: color:#331100 emissive:#ff4400 emissiveIntensity:1
31
+
32
+ ## BLACK HOLE RECIPE
33
+ 1. SphereGeometry core β€” black, emissive purple
34
+ 2. 5x RingGeometry accretion disks β€” each rotated to DIFFERENT angle (x: 0, 0.3, 0.6, 0.9, 1.1), emissive orange/yellow/red, transparent
35
+ 3. TorusGeometry photon ring β€” emissive white intensity:6
36
+ 4. 2x TubeGeometry polar jets (up + down) β€” emissive blue, transparent
37
+ 5. Points 200 particles β€” orbiting around core
38
+ 6. PointLight orange at center
39
+ 7. Animate with requestAnimationFrame: rotate core, spin particles, pulse photon ring
40
+
41
+ ## MISSILE RECIPE
42
+ 1. LatheGeometry body β€” dark steel
43
+ 2. ConeGeometry nose β€” chrome
44
+ 3. 4x ExtrudeGeometry delta fins β€” dark steel
45
+ 4. CylinderGeometry nozzle β€” black metal
46
+ 5. CylinderGeometry exhaust + TorusGeometry glow ring β€” emissive orange
47
+ 6. CylinderGeometry warhead band β€” red
48
+ 7. 4x TubeGeometry fuel lines
49
+ 8. 16x SphereGeometry rivets β€” chrome
50
+
51
+ ## SOLAR SYSTEM RECIPE
52
+ 1. Clear scene first
53
+ 2. SphereGeometry Sun β€” emissive yellow, PointLight child
54
+ 3. 8 planet SphereGeometry β€” unique colors/sizes
55
+ 4. RingGeometry orbit paths β€” flat, dark
56
+ 5. Saturn: add RingGeometry as child mesh
57
+ 6. requestAnimationFrame loop: Math.cos/sin for all orbital positions at different speeds
58
+
59
+ ## ANIMATION PATTERN (orbits)
60
+ const t0=Date.now();
61
+ (function loop(){
62
+ requestAnimationFrame(loop);
63
+ const t=(Date.now()-t0)*0.001;
64
+ planets.forEach(p=>{
65
+ p.mesh.position.x=Math.cos(p.angle+t*p.speed)*p.radius;
66
+ p.mesh.position.z=Math.sin(p.angle+t*p.speed)*p.radius;
67
+ p.mesh.rotation.y+=0.01;
68
+ });
69
+ })();
70
+
71
+ ## TEXTURE LOADING (free CDN)
72
+ const loader=new THREE.TextureLoader();
73
+ loader.load('https://cdn.polyhaven.com/asset_img/thumbs/metal_plate.png', tex=>{
74
+ tex.wrapS=tex.wrapT=THREE.RepeatWrapping; tex.repeat.set(4,4);
75
+ mesh.material.map=tex; mesh.material.needsUpdate=true;
76
+ });
77
+ Available: metal_plate, worn_metal, concrete_wall_003, cracked_concrete, oak_veneer, wood_planks_dirt, rocks_ground_01
78
+
79
+ ## LIGHTING
80
+ Always add colored lights:
81
+ const pl=new THREE.PointLight(0xff6644, 3, 30);
82
+ pl.position.set(5, 8, 3); SCENE.add(pl);
83
+ SCENE.add(new THREE.AmbientLight(0x112233, 0.5));
84
+
85
+ ## CUSTOMIZE THIS FILE
86
+ Add your own rules below to teach the AI your style:
87
+
88
+ ### My Default Style
89
+ (Edit here β€” e.g. "Always use dark sci-fi color palette")
90
+
91
+ ### My Custom Textures
92
+ (Edit here β€” e.g. "Use my CDN at https://...")
app.py ADDED
@@ -0,0 +1,260 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os, httpx
2
+ from fastapi import FastAPI, Request
3
+ from fastapi.responses import FileResponse, JSONResponse
4
+ from fastapi.staticfiles import StaticFiles
5
+
6
+ app = FastAPI()
7
+
8
+ GROQ_KEY = os.getenv("GROQ_API_KEY", "")
9
+ NEMOTRON_KEY = os.getenv("NEMOTRON_API_KEY", "")
10
+ OPENROUTER_KEY = os.getenv("OPENROUTER_API_KEY", "")
11
+
12
+ CUSTOM_SKILLS = ""
13
+ try:
14
+ with open("SKILLS.md") as f:
15
+ CUSTOM_SKILLS = f.read()
16
+ print("SKILLS.md loaded")
17
+ except:
18
+ pass
19
+
20
+ BASE = """You are FORGE3D, an expert Three.js 3D artist. Output ONLY raw JavaScript. No markdown, no backticks, no comments explaining.
21
+
22
+ RULES β€” ALWAYS FOLLOW:
23
+ 1. Every mesh: castShadow=true, receiveShadow=true
24
+ 2. Every material: set BOTH metalness AND roughness (MeshStandardMaterial always)
25
+ 3. Register every object: OBJS.push({mesh,light:null,name:'Name',type:'MESH',vis:true,kf:{}})
26
+ 4. Always add 2-3 colored PointLights for atmosphere
27
+ 5. End with toast('what was built')
28
+ 6. Complex objects = 12+ parts minimum
29
+
30
+ GEOMETRY TOOLS:
31
+ - LatheGeometry(pts,32) β€” for rockets, missiles, bottles
32
+ - ExtrudeGeometry(shape,opts) β€” for fins, wings, panels
33
+ - TubeGeometry(curve,64,r,8) β€” for pipes, cables, jets
34
+ - RingGeometry(inner,outer,128) β€” for disks, halos, orbits
35
+ - TorusGeometry(r,tube,16,128) β€” for rings
36
+ - SphereGeometry(r,32,32), BoxGeometry, CylinderGeometry, ConeGeometry
37
+
38
+ MATERIALS:
39
+ Chrome: color:#d4d4d4 metalness:0.95 roughness:0.05
40
+ Gold: color:#FFD700 metalness:0.9 roughness:0.1
41
+ DarkSteel: color:#2a3344 metalness:0.8 roughness:0.3
42
+ Glass: color:#aaddff metalness:0 roughness:0 transparent:true opacity:0.2
43
+ Glow: emissive set + emissiveIntensity 2-5
44
+ Lava: color:#331100 emissive:#ff4400 emissiveIntensity:1
45
+
46
+ SCENE API:
47
+ addObj(type) -> obj [cube,sphere,cylinder,plane,cone,torus,ico,torusknot]
48
+ addLight(type) -> obj [point,sun,spot]
49
+ obj.mesh.position.set(x,y,z)
50
+ obj.mesh.rotation.set(x,y,z)
51
+ obj.mesh.scale.set(x,y,z)
52
+ obj.mesh.material.color.set('#hex')
53
+
54
+ ANIMATION (for solar systems, orbits - use RAF not keyframes):
55
+ const t0=Date.now();
56
+ (function loop(){
57
+ requestAnimationFrame(loop);
58
+ const t=(Date.now()-t0)*0.001;
59
+ mesh.position.x=Math.cos(t*speed)*radius;
60
+ mesh.position.z=Math.sin(t*speed)*radius;
61
+ })();
62
+
63
+ BLACK HOLE RECIPE (always use this exact structure):
64
+ 1. SphereGeometry(2,32,32) core - black, emissive:#220033 intensity:0.5
65
+ 2. 5x RingGeometry accretion disks - rotate each differently, emissive orange/yellow, transparent
66
+ 3. TorusGeometry photon ring - white emissive intensity:5
67
+ 4. 2x TubeGeometry polar jets up+down - blue emissive
68
+ 5. Points geometry 200 particles orbiting
69
+ 6. PointLight orange at center
70
+
71
+ MISSILE RECIPE (always use this):
72
+ 1. LatheGeometry body - dark steel
73
+ 2. ConeGeometry nose - chrome
74
+ 3. 4x ExtrudeGeometry fins - dark steel
75
+ 4. CylinderGeometry nozzle - black metal
76
+ 5. CylinderGeometry+TorusGeometry exhaust glow - emissive orange
77
+ 6. 4x TubeGeometry fuel lines
78
+ 7. 16x tiny SphereGeometry rivets - chrome
79
+ """ + (f"\nCUSTOM SKILLS:\n{CUSTOM_SKILLS}" if CUSTOM_SKILLS else "")
80
+
81
+ SCENE_SYSTEM = BASE + "\nYou are the SCENE specialist. Build full environments 15-40 objects. Use loops for repeated elements. For solar systems use requestAnimationFrame orbital loops."
82
+
83
+ DETAIL_SYSTEM = BASE + "\nYou are the DETAIL specialist. Build hyper-detailed complex models 15-50 parts. Use LatheGeometry+ExtrudeGeometry+TubeGeometry. Perfect PBR on every part."
84
+
85
+ ANIM_SYSTEM = BASE + "\nYou are the ANIMATION specialist. Write requestAnimationFrame loops for continuous motion OR keyframe data. For orbits always use cos/sin loops. Always set document.getElementById('tlend').value=String(totalFrames); then drawTL();applyKF();"
86
+
87
+ async def groq(messages, sys, tokens=3000, temp=0.2):
88
+ async with httpx.AsyncClient(timeout=60) as c:
89
+ r = await c.post("https://api.groq.com/openai/v1/chat/completions",
90
+ headers={"Authorization":f"Bearer {GROQ_KEY}","Content-Type":"application/json"},
91
+ json={"model":"llama-3.3-70b-versatile","messages":[{"role":"system","content":sys}]+messages,"max_tokens":tokens,"temperature":temp,"stream":False})
92
+ return r
93
+
94
+ async def openrouter(messages, sys, tokens=3000, temp=0.2):
95
+ # Try multiple free models in order
96
+ models = [
97
+ "google/gemini-2.0-flash-exp:free",
98
+ "deepseek/deepseek-chat-v3-0324:free",
99
+ "meta-llama/llama-3.3-70b-instruct:free",
100
+ "microsoft/phi-4:free",
101
+ "deepseek/deepseek-r1:free"
102
+ ]
103
+ for model in models:
104
+ try:
105
+ async with httpx.AsyncClient(timeout=90) as c:
106
+ r = await c.post("https://openrouter.ai/api/v1/chat/completions",
107
+ headers={"Authorization":f"Bearer {OPENROUTER_KEY}","Content-Type":"application/json","HTTP-Referer":"https://huggingface.co","X-Title":"FORGE3D"},
108
+ json={"model":model,"messages":[{"role":"system","content":sys}]+messages,"max_tokens":tokens,"temperature":temp})
109
+ data = r.json()
110
+ if r.status_code == 200 and "choices" in data:
111
+ data["_model_used"] = model
112
+ return data, None
113
+ if r.status_code in [429, 503, 502]:
114
+ continue # try next model
115
+ except:
116
+ continue
117
+ return None, "All OpenRouter models failed, using Groq fallback"
118
+
119
+ @app.post("/api/groq")
120
+ async def proxy_groq(request: Request):
121
+ if not GROQ_KEY:
122
+ return JSONResponse({"error":"GROQ_API_KEY not set"},status_code=500)
123
+ body = await request.json()
124
+ r = await groq(body.get("messages",[]), BASE, body.get("max_tokens",3000))
125
+ return JSONResponse(r.json(), status_code=r.status_code)
126
+
127
+ @app.post("/api/nemotron")
128
+ async def proxy_nemotron(request: Request):
129
+ body = await request.json()
130
+ messages = body.get("messages",[])
131
+ # NVIDIA endpoint (correct URL)
132
+ if NEMOTRON_KEY:
133
+ try:
134
+ async with httpx.AsyncClient(timeout=60) as c:
135
+ r = await c.post("https://integrate.api.nvidia.com/v1/chat/completions",
136
+ headers={"Authorization":f"Bearer {NEMOTRON_KEY}","Content-Type":"application/json"},
137
+ json={"model":"nvidia/llama-3.1-nemotron-ultra-253b-v1","messages":[{"role":"system","content":SCENE_SYSTEM}]+messages,"max_tokens":body.get("max_tokens",3000),"temperature":0.3})
138
+ if r.status_code == 200:
139
+ return JSONResponse(r.json())
140
+ except:
141
+ pass
142
+ # Try nemotron-70b as fallback
143
+ try:
144
+ async with httpx.AsyncClient(timeout=60) as c:
145
+ r = await c.post("https://integrate.api.nvidia.com/v1/chat/completions",
146
+ headers={"Authorization":f"Bearer {NEMOTRON_KEY}","Content-Type":"application/json"},
147
+ json={"model":"nvidia/llama-3.1-nemotron-70b-instruct","messages":[{"role":"system","content":SCENE_SYSTEM}]+messages,"max_tokens":body.get("max_tokens",3000),"temperature":0.3})
148
+ if r.status_code == 200:
149
+ return JSONResponse(r.json())
150
+ except:
151
+ pass
152
+ # Fallback to Groq
153
+ if GROQ_KEY:
154
+ r = await groq(messages, SCENE_SYSTEM, body.get("max_tokens",3000))
155
+ d = r.json()
156
+ d["_fallback"] = "groq"
157
+ return JSONResponse(d)
158
+ return JSONResponse({"error":"No API keys"},status_code=500)
159
+
160
+ @app.post("/api/openrouter")
161
+ async def proxy_openrouter(request: Request):
162
+ body = await request.json()
163
+ messages = body.get("messages",[])
164
+ if OPENROUTER_KEY:
165
+ data, err = await openrouter(messages, DETAIL_SYSTEM, body.get("max_tokens",3000))
166
+ if data:
167
+ return JSONResponse(data)
168
+ # Groq fallback
169
+ if GROQ_KEY:
170
+ r = await groq(messages, DETAIL_SYSTEM, body.get("max_tokens",3000))
171
+ d = r.json()
172
+ d["_fallback"] = "groq"
173
+ return JSONResponse(d)
174
+ return JSONResponse({"error":"No API keys"},status_code=500)
175
+
176
+ @app.post("/api/animation")
177
+ async def proxy_animation(request: Request):
178
+ body = await request.json()
179
+ messages = body.get("messages",[])
180
+ if OPENROUTER_KEY:
181
+ data, _ = await openrouter(messages, ANIM_SYSTEM, body.get("max_tokens",3000))
182
+ if data:
183
+ return JSONResponse(data)
184
+ if GROQ_KEY:
185
+ r = await groq(messages, ANIM_SYSTEM, body.get("max_tokens",3000), 0.15)
186
+ return JSONResponse(r.json())
187
+ return JSONResponse({"error":"No API keys"},status_code=500)
188
+
189
+ @app.post("/api/texture-search")
190
+ async def texture_search(request: Request):
191
+ body = await request.json()
192
+ q = body.get("query","metal")
193
+ obj = body.get("object","selected object")
194
+ prompt = f"""Apply a free texture to object "{obj}" based on: "{q}"
195
+ Output ONLY raw JavaScript. Use THREE.TextureLoader.
196
+ Pick best match from Poly Haven CDN: https://cdn.polyhaven.com/asset_img/thumbs/NAME.png
197
+ Metal options: metal_plate, worn_metal, rust_coated_iron
198
+ Wood options: oak_veneer, wood_planks_dirt
199
+ Concrete: concrete_wall_003, cracked_concrete
200
+ Rock: rocks_ground_01, mossy_cobblestone
201
+ For lava/neon/plasma: use emissive material instead of texture.
202
+
203
+ Code pattern:
204
+ const obj=OBJS.find(o=>o.mesh===SEL?.mesh)||OBJS[OBJS.length-1];
205
+ const loader=new THREE.TextureLoader();
206
+ loader.load('URL',tex=>{{tex.wrapS=tex.wrapT=THREE.RepeatWrapping;tex.repeat.set(3,3);obj.mesh.material.map=tex;obj.mesh.material.needsUpdate=true;}});
207
+ toast('Texture applied');"""
208
+ if GROQ_KEY:
209
+ r = await groq([{"role":"user","content":prompt}], BASE, 600)
210
+ return JSONResponse(r.json())
211
+ return JSONResponse({"error":"No API key"},status_code=500)
212
+
213
+ @app.get("/api/status")
214
+ async def status():
215
+ res = {}
216
+ if GROQ_KEY:
217
+ try:
218
+ async with httpx.AsyncClient(timeout=8) as c:
219
+ r = await c.post("https://api.groq.com/openai/v1/chat/completions",
220
+ headers={"Authorization":f"Bearer {GROQ_KEY}","Content-Type":"application/json"},
221
+ json={"model":"llama-3.3-70b-versatile","messages":[{"role":"user","content":"hi"}],"max_tokens":3})
222
+ res["groq"] = "βœ… Groq Ready" if r.status_code==200 else f"❌ Groq {r.status_code}"
223
+ except Exception as e:
224
+ res["groq"] = f"❌ Groq: {str(e)[:30]}"
225
+ else:
226
+ res["groq"] = "❌ Missing GROQ_API_KEY"
227
+ if NEMOTRON_KEY:
228
+ try:
229
+ async with httpx.AsyncClient(timeout=10) as c:
230
+ r = await c.post("https://integrate.api.nvidia.com/v1/chat/completions",
231
+ headers={"Authorization":f"Bearer {NEMOTRON_KEY}","Content-Type":"application/json"},
232
+ json={"model":"nvidia/llama-3.1-nemotron-70b-instruct","messages":[{"role":"user","content":"hi"}],"max_tokens":3})
233
+ res["nemotron"] = "βœ… Nemotron Ready" if r.status_code==200 else f"⚠️ NVIDIA {r.status_code} β†’ Groq fallback"
234
+ except:
235
+ res["nemotron"] = "⚠️ NVIDIA offline β†’ Groq fallback"
236
+ else:
237
+ res["nemotron"] = "❌ Missing NEMOTRON_API_KEY"
238
+ if OPENROUTER_KEY:
239
+ try:
240
+ async with httpx.AsyncClient(timeout=10) as c:
241
+ r = await c.post("https://openrouter.ai/api/v1/chat/completions",
242
+ headers={"Authorization":f"Bearer {OPENROUTER_KEY}","Content-Type":"application/json","HTTP-Referer":"https://huggingface.co","X-Title":"FORGE3D"},
243
+ json={"model":"google/gemini-2.0-flash-exp:free","messages":[{"role":"user","content":"hi"}],"max_tokens":3})
244
+ res["openrouter"] = "βœ… OpenRouter Ready" if r.status_code==200 else f"❌ OpenRouter {r.status_code}: {r.text[:80]}"
245
+ except Exception as e:
246
+ res["openrouter"] = f"❌ OpenRouter: {str(e)[:40]}"
247
+ else:
248
+ res["openrouter"] = "❌ Missing OPENROUTER_API_KEY"
249
+ res["skills"] = "βœ… SKILLS.md loaded" if CUSTOM_SKILLS else "ℹ️ No SKILLS.md"
250
+ return res
251
+
252
+ app.mount("/static", StaticFiles(directory="static"), name="static")
253
+
254
+ @app.get("/")
255
+ async def root():
256
+ return FileResponse("static/homepage.html")
257
+
258
+ @app.get("/editor")
259
+ async def editor():
260
+ return FileResponse("static/index.html")