
Image 3d
- 1.9k installs
- 21 repo stars
- Updated August 3, 2026
- starchild-ai-agent/official-skills
image-3d is an agent skill for 3d-style image generation: 3d characters, product renders, isometric dioramas, 3d icons, 3d text, interior design renders, architectural visualization, 3d scenes, game assets.
About
The image-3d skill is designed for 3D-style image generation: 3D characters, product renders, isometric dioramas, 3D icons, 3D text, interior design renders, architectural visualization, 3D scenes, game assets. image-3d Use this skill for all 3D-style image generation requests on Starchild. Quick start — category + style only (no custom prompt) 3. Invoke when the user asks about image 3d or related SKILL.md workflows.
- This skill generates 3D-style 2D images (PNG/JPG) — rendered pictures that *look* 3D.
- This skill does NOT produce 3D model files (.glb, .obj, .fbx, .usdz).
- For actual 3D model files, users need dedicated 3D modeling services (Meshy, etc.).
- image-ecommerce → user wants product photos for e-commerce listings (white background, lifestyle shots).
- image-edit → user wants to edit/transform an existing image.
Image 3d by the numbers
- 1,870 all-time installs (skills.sh)
- +72 installs in the week ending Aug 5, 2026 (Skillselion tracking)
- Ranked #168 of 1,880 Design & UI/UX skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
image-3d capabilities & compatibility
- Capabilities
- this skill generates 3d style 2d images (png/jpg · this skill does not produce 3d model files (.glb · for actual 3d model files, users need dedicated · image ecommerce → user wants product photos for
- Use cases
- frontend
What image-3d says it does
3D-style image generation: 3D characters, product renders, isometric dioramas, 3D icons, 3D text, interior design renders, architectural visualization, 3D scenes, game assets.
3D-style image generation: 3D characters, product renders, isometric dioramas, 3D icons, 3D text, interior design renders, architectural visualization, 3D scene
npx skills add https://github.com/starchild-ai-agent/official-skills --skill image-3dAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 1.9k |
|---|---|
| repo stars | ★ 21 |
| Last updated | August 3, 2026 |
| Repository | starchild-ai-agent/official-skills ↗ |
How do I 3d-style image generation: 3d characters, product renders, isometric dioramas, 3d icons, 3d text, interior design renders, architectural visualization, 3d scenes, game assets?
3D-style image generation: 3D characters, product renders, isometric dioramas, 3D icons, 3D text, interior design renders, architectural visualization, 3D scenes, game assets.
Who is it for?
Developers using image 3d workflows documented in SKILL.md.
Skip if: Skip when the task falls outside image-3d scope or needs a different stack.
When should I use this skill?
User asks about image 3d or related SKILL.md workflows.
What you get
Completed image-3d workflow with documented commands, files, and expected deliverables.
- 3D-style raster image
- app icon asset
- product render
By the numbers
- Starchild skill version 1.0.0 in official-skills manifest
Files
image-3d
Use this skill for all 3D-style image generation requests on Starchild.
Covers: 3D character design (chibi, realistic, cartoon, fantasy), 3D product renders (floating, exploded, turntable), isometric dioramas & miniature scenes, 3D app icons (iOS, Material, game), 3D text effects (chrome, neon, wood, candy), interior design visualization (modern, luxury, cozy, industrial), architectural rendering (modern, traditional, futuristic, aerial, night), 3D scenes (fantasy, sci-fi, nature, urban), and game asset renders (weapons, environments, props, vehicles).
Core principle: call the provided script. Do not re-implement proxy/billing plumbing.
⚠️ Important distinction — 3D-style images vs 3D model files:
- This skill generates 3D-style 2D images (PNG/JPG) — rendered pictures that look 3D
- This skill does NOT produce 3D model files (.glb, .obj, .fbx, .usdz)
- For actual 3D model files, users need dedicated 3D modeling services (Meshy, etc.)
When to use image-3d vs other image skills:
- image-3d → user wants a 3D-rendered look: 3D characters, isometric scenes, 3D icons, architectural renders, interior design renders, 3D text effects
- image-create → user wants general creative images (logos, posters, illustrations, memes) — image-create has a basic
3dcategory, but image-3d offers far more granular control - image-ecommerce → user wants product photos for e-commerce listings (white background, lifestyle shots)
- image-edit → user wants to edit/transform an existing image
- image-portrait → user wants a portrait with face/identity preserved
---
1. Quick start — text-to-3D image (most common)
exec(open('skills/image-3d/generate_3d.py').read())
result = generate_3d(
prompt="a cute robot assistant with big eyes and antenna",
category="character",
style="chibi",
)
# result -> {"success": True, "images": [{"local_path": "output/images/..."}], ...}2. Quick start — category + style only (no custom prompt)
exec(open('skills/image-3d/generate_3d.py').read())
result = generate_3d(
category="diorama",
style="isometric",
)
# Uses the built-in style template as the full prompt3. Quick start — reference image → 3D style
exec(open('skills/image-3d/generate_3d.py').read())
result = generate_3d(
prompt="transform into a 3D rendered character",
reference_path="uploads/sketch.jpg",
category="character",
style="cartoon",
)Delivering the result to the user — IMPORTANT
Never hand the user the raw fal.media URL. fal serves files with restrictive CSP headers. The only reliable delivery path is the already-downloaded local file:
1. Use each image's local_path (e.g. output/images/xxx.png) — the script always downloads on success. 2. Tell the user the files are saved to output/images/ and viewable in the workspace file panel. 3. On Web channel, embed inline so the user can preview in chat:
4. On Telegram / WeChat: send via send_to_telegram(file_path="output/images/...", message_type="image") or send_to_wechat(file_path="output/images/...", message_type="image").
---
4. Parameters
| Parameter | Required | Default | Description |
|---|---|---|---|
prompt | yes* | — | Text description of the desired 3D image |
reference_path | no | — | Local file path to a reference image (for 3D stylization) |
reference_url | no | — | Public HTTPS URL of a reference image |
category | no | "character" | 3D category preset (see §5) |
style | no | "default" | Sub-style within the category (see §5) |
model | no | "nanopro" | Model: "nanopro" (fast ~25s) or "gpt" (best quality ~150s) |
count | no | 1 | Number of images to generate (1–4) |
aspect_ratio | no | auto | Output ratio: 1:1, 3:4, 4:3, 9:16, 16:9. Auto-selected by category if not set. |
*A prompt is required for text-to-image mode unless a non-default style is specified (which provides a built-in template).
Prompt priority: prompt + category/style (enhanced) > prompt only > category + style template > category default.
Aspect ratio auto-selection: When not explicitly set, the script picks the best ratio for the category:
3:4for characters (portrait orientation)1:1for products, dioramas, icons, game assets (square showcase)16:9for text, interiors, architecture, scenes (wide cinematic)
---
5. Categories and style presets
N: 3D Character (category="character")
| Style | Key | Best for |
|---|---|---|
| Chibi / Pixar | chibi | Cute stylized characters, mascots, avatars |
| Realistic | realistic | Game-ready characters, detailed anatomy |
| Cartoon | cartoon | Disney/Pixar style, family-friendly |
| Fantasy | fantasy | RPG characters, warriors, mages |
| General | default | Any 3D character |
Character prompt tips:
- Describe pose, outfit, expression, and accessories
- Specify "white background" or "clean background" for character sheets
- For game characters, mention "T-pose" or "action pose"
- Example:
"a female elf ranger with green cloak, holding a bow, forest background"
N: 3D Product Render (category="product")
| Style | Key | Best for |
|---|---|---|
| Floating 3/4 angle | floating | Hero product shots, marketing |
| Exploded view | exploded | Technical illustration, internal structure |
| Turntable / 360° | turntable | Multi-angle showcase |
| General | default | Any product render |
Product prompt tips:
- Describe material (matte, glossy, metallic, wood, glass)
- Specify lighting (studio, dramatic, soft, rim light)
- Mention the product type and key features
- Example:
"wireless earbuds in charging case, matte white, floating angle"
N: 3D Diorama / Isometric (category="diorama")
| Style | Key | Best for |
|---|---|---|
| Isometric | isometric | Miniature worlds, city blocks, game maps |
| Low-poly | lowpoly | Stylized scenes, indie game aesthetic |
| Realistic | realistic | Architectural models, detailed miniatures |
| General | default | Any diorama scene |
Diorama prompt tips:
- Describe the scene contents (buildings, trees, characters, vehicles)
- Mention scale ("miniature", "tiny", "dollhouse")
- Specify mood (warm, cozy, dramatic, whimsical)
- Example:
"a cozy Japanese ramen shop at night, tiny customers inside, warm glow"
N: 3D Icon (category="icon")
| Style | Key | Best for |
|---|---|---|
| iOS style | ios | Apple App Store icons, glossy glass |
| Material Design | material | Google Play icons, flat with depth |
| Game icon | game | RPG items, fantasy game UI |
| General | default | Any 3D icon |
Icon prompt tips:
- Describe the icon subject simply (one main element)
- Specify the shape context ("rounded square", "circular")
- Keep it simple — icons should be recognizable at small sizes
- Example:
"a camera icon with rainbow lens flare"
N: 3D Text (category="text")
| Style | Key | Best for |
|---|---|---|
| Chrome / Metallic | chrome | Bold titles, movie-style text |
| Neon glow | neon | Cyberpunk, nightlife, signs |
| Wood | wood | Rustic, craft, natural brands |
| Candy | candy | Playful, kids, sweet themes |
| General | default | Any 3D text effect |
Text prompt tips:
- ⚠️ AI text rendering is unreliable — keep text SHORT (1-3 words max)
- Describe the text content in quotes:
"the word 'HELLO'" - Specify material and environment
- Example:
"the word 'GAME OVER' in chrome metallic letters, dark background"
L: Interior Design (category="interior")
| Style | Key | Best for |
|---|---|---|
| Modern minimalist | modern | Scandinavian, clean lines, natural light |
| Luxury | luxury | High-end, marble, gold accents |
| Cozy / Hygge | cozy | Warm, comfortable, inviting |
| Industrial | industrial | Loft, exposed brick, urban chic |
| General | default | Any interior render |
Interior prompt tips:
- Describe the room type (living room, bedroom, kitchen, bathroom, office)
- Mention key furniture and materials
- Specify lighting (natural daylight, warm evening, dramatic)
- Specify color palette if important
- Example:
"modern Scandinavian living room with large windows, light wood floors, gray sofa, indoor plants"
L: Architecture (category="architecture")
| Style | Key | Best for |
|---|---|---|
| Modern | modern | Contemporary buildings, glass & steel |
| Traditional | traditional | Classical, heritage, stone & wood |
| Futuristic | futuristic | Sci-fi, organic forms, green tech |
| Aerial view | aerial | Bird's eye, masterplan, site context |
| Night scene | night | Dramatic lighting, facade uplighting |
| General | default | Any architectural render |
Architecture prompt tips:
- Describe building type (house, office, tower, museum, school)
- Mention materials (concrete, glass, steel, wood, stone, brick)
- Specify environment (urban, suburban, hillside, waterfront)
- Mention time of day for lighting (golden hour, midday, dusk, night)
- Example:
"modern three-story house with flat roof, large glass windows, surrounded by garden, golden hour"
N: 3D Scene (category="scene")
| Style | Key | Best for |
|---|---|---|
| Fantasy | fantasy | Magical worlds, floating islands |
| Sci-fi | scifi | Space stations, futuristic tech |
| Nature | nature | Forests, landscapes, natural beauty |
| Urban | urban | City streets, cyberpunk, neon |
| General | default | Any 3D scene |
N: Game Asset (category="game_asset")
| Style | Key | Best for |
|---|---|---|
| Weapon | weapon | Swords, guns, magical weapons |
| Environment | environment | Game levels, world design |
| Prop | prop | Items, objects, collectibles |
| Vehicle | vehicle | Cars, ships, aircraft |
| General | default | Any game asset |
---
6. 3D Rendering keyword guide
When crafting custom prompts, use these keywords to control the 3D look:
Materials
| Keyword | Effect |
|---|---|
PBR materials | Physically-based rendering, realistic surfaces |
subsurface scattering | Translucent skin, wax, marble |
metallic / chrome | Reflective metal surfaces |
glossy / matte | Surface finish control |
glass / transparent | See-through materials |
clay render | Matte gray, no textures, shape focus |
Lighting
| Keyword | Effect |
|---|---|
studio lighting | Clean, professional, controlled |
HDRI lighting | Environment-based, natural reflections |
rim light | Edge highlight, subject separation |
volumetric lighting | God rays, atmospheric depth |
ambient occlusion | Soft contact shadows, depth |
global illumination | Realistic light bouncing |
Camera / Composition
| Keyword | Effect |
|---|---|
isometric view | 45° top-down, no perspective distortion |
3/4 angle | Classic product/character showcase angle |
eye level | Natural human perspective |
bird's eye view | Top-down aerial perspective |
tilt-shift | Miniature/diorama effect |
depth of field | Background blur, subject focus |
Render Engine Style
| Keyword | Effect |
|---|---|
octane render | High-quality, photorealistic |
Blender Cycles | Realistic path tracing |
Unreal Engine 5 | Game-quality, real-time look |
V-Ray | Architectural visualization quality |
Cinema 4D | Clean, stylized 3D |
KeyShot | Product visualization quality |
---
7. Model selection guide
| Model | Key | Speed | Quality | Best for |
|---|---|---|---|---|
| Nano Banana Pro | nanopro | ~25s | Good | Quick iterations, drafts, most 3D styles |
| GPT Image 2 | gpt | ~150s | Best | Final renders, complex scenes, fine details |
Recommendations by category:
- Characters →
nanoprofor drafts,gptfor final character sheets - Products →
gptfor photorealistic renders,nanoprofor quick concepts - Dioramas →
nanoprohandles isometric well;gptfor detailed miniatures - Icons →
nanoprois sufficient for most icons - Text →
gptfor better text rendering (still imperfect) - Interior/Architecture →
gptfor photorealistic archviz,nanoprofor concepts - Scenes →
gptfor cinematic quality,nanoprofor quick mood boards
---
8. Aspect ratio guide
| Category | Default | Recommended alternatives |
|---|---|---|
| character | 3:4 | 1:1 for avatar, 9:16 for full-body |
| product | 1:1 | 4:3 for landscape showcase |
| diorama | 1:1 | 16:9 for panoramic diorama |
| icon | 1:1 | Always 1:1 |
| text | 16:9 | 1:1 for square banners |
| interior | 16:9 | 4:3 for room views, 3:4 for vertical |
| architecture | 16:9 | 3:4 for tall buildings, 1:1 for aerial |
| scene | 16:9 | 9:16 for vertical scenes |
| game_asset | 1:1 | 3:4 for character assets |
---
9. Intent recognition guide
Map user requests to the right category + style:
| User says... | Category | Style | Notes |
|---|---|---|---|
| "3D character", "Pixar style character" | character | chibi or cartoon | |
| "game character", "RPG hero" | character | fantasy | |
| "realistic 3D person" | character | realistic | |
| "3D product render", "product visualization" | product | floating | |
| "exploded view", "internal structure" | product | exploded | |
| "isometric", "miniature scene", "tiny world" | diorama | isometric | |
| "low poly scene", "stylized environment" | diorama | lowpoly | |
| "app icon", "iOS icon" | icon | ios | |
| "game icon", "RPG item icon" | icon | game | |
| "3D text", "chrome text", "metallic letters" | text | chrome | |
| "neon sign", "glowing text" | text | neon | |
| "interior design", "room design", "装修效果图" | interior | modern | |
| "luxury interior", "penthouse" | interior | luxury | |
| "cozy room", "warm interior" | interior | cozy | |
| "building render", "architecture visualization" | architecture | modern | |
| "night render", "building at night" | architecture | night | |
| "aerial view", "masterplan" | architecture | aerial | |
| "fantasy world", "magical scene" | scene | fantasy | |
| "sci-fi scene", "space station" | scene | scifi | |
| "game weapon", "sword render" | game_asset | weapon | |
| "game environment", "level design" | game_asset | environment |
---
10. Error handling
| Error | Cause | Fix |
|---|---|---|
"Unknown model" | Invalid model key | Use "nanopro" or "gpt" |
"Unknown category" | Invalid category | Check §5 for valid categories |
"Unknown style" | Style not in category | Check the style table for that category |
"File not found" | reference_path doesn't exist | Verify the file path |
"Image too large" | Reference image > 10 MB | Resize or compress the image |
"Submit failed" | API error | Check FAL_KEY, retry |
"Generation timed out" | Model took too long | Retry, or switch to nanopro |
---
11. Advanced examples
3D character sheet (multiple angles)
exec(open('skills/image-3d/generate_3d.py').read())
result = generate_3d(
prompt="a steampunk inventor character with goggles, leather apron, and mechanical arm, character sheet showing front and side views",
category="character",
style="realistic",
model="gpt",
aspect_ratio="16:9",
)Isometric city block
exec(open('skills/image-3d/generate_3d.py').read())
result = generate_3d(
prompt="a bustling Tokyo street corner with ramen shop, vending machines, and cherry blossom trees, tiny people walking",
category="diorama",
style="isometric",
)Architectural night render
exec(open('skills/image-3d/generate_3d.py').read())
result = generate_3d(
prompt="a modern art museum with curved glass facade, reflecting pool in front, dramatic uplighting",
category="architecture",
style="night",
model="gpt",
)Interior design — modern living room
exec(open('skills/image-3d/generate_3d.py').read())
result = generate_3d(
prompt="spacious living room with floor-to-ceiling windows overlooking city skyline, minimalist furniture, warm wood accents, indoor plants",
category="interior",
style="modern",
model="gpt",
)Reference image → 3D style
exec(open('skills/image-3d/generate_3d.py').read())
result = generate_3d(
prompt="transform into a 3D Pixar-style character with exaggerated features",
reference_path="uploads/photo.jpg",
category="character",
style="cartoon",
)3D game weapon
exec(open('skills/image-3d/generate_3d.py').read())
result = generate_3d(
prompt="legendary fire sword with glowing runes, ember particles, dark background",
category="game_asset",
style="weapon",
)3D neon text
exec(open('skills/image-3d/generate_3d.py').read())
result = generate_3d(
prompt="the word 'CYBER' in neon pink and blue letters, wet street reflection",
category="text",
style="neon",
)Multiple images for comparison
exec(open('skills/image-3d/generate_3d.py').read())
result = generate_3d(
prompt="a cozy coffee shop interior with exposed brick and warm lighting",
category="interior",
style="cozy",
count=3,
model="nanopro",
)
# Generates 3 variations for the user to choose from"""Cost tracking helper for skill subprocesses.
Skills that call sc-proxy via plain `requests` need to:
1. Tag every paid call with a SC-CALLER-ID that ties it back to the user
turn that triggered the skill (so the agent's per-turn cost summary
shows the cost in the right cost card).
2. After each call, parse the sc-proxy response headers
(`X-Credits-Used`, `X-Credits-Api-Type`) and write a row to the cost
ledger that the agent reads back when it builds the SSE
`cost_summary` event.
This file is intentionally zero-dependency (stdlib only) so it can be
dropped into any skill folder without coupling to starchild-clawd internals.
Env vars consumed (set by the agent before dispatching the bash subprocess):
- STARCHILD_TOOL_CALLER_ID — opaque tag for the current tool call
- STARCHILD_USER_TURN_ID — uuid of the current user turn
- STARCHILD_COST_LEDGER_DIR — optional override for ledger directory
When env vars are absent (e.g. running the script outside an agent), the
helpers degrade gracefully: caller-id falls back to a synthetic string so
the call still goes through, and ledger writes still happen for audit but
the user-turn reader will skip them.
"""
from __future__ import annotations
import fcntl
import json
import os
import time
from datetime import datetime, timezone
from pathlib import Path
from typing import Any, Dict, Optional
from urllib.parse import urlparse
_DEFAULT_LEDGER_DIR = "/data/.starchild/cost_ledger"
# Allowlisted request payload keys we forward into the ledger row's
# `details` field. MUST stay in sync with starchild-clawd's
# core/http_client._record_cost_to_ledger allowlist — anything not in
# that allowlist won't be picked up by the agent and won't render in
# the frontend cost card.
_PAYLOAD_ALLOWLIST = (
# Identity
"model", "provider",
# Image geometry
"aspect_ratio", "quality", "resolution", "image_size", "size",
# Video / motion
"duration", "duration_s", "fps", "motion_strength",
# Quantity
"n", "count",
# Generation knobs
"seed", "steps", "guidance_scale", "cfg_scale", "strength",
"scheduler", "sampler",
# Reference / mode hints
"image_to_image", "image_to_video", "use_reference", "reference_count",
)
def caller_headers(extra: Optional[Dict[str, str]] = None,
tool_default: str = "skill") -> Dict[str, str]:
"""Return an HTTP-headers dict with SC-CALLER-ID filled in.
Resolution order:
1. `extra["SC-CALLER-ID"]` (case-insensitive) — caller wins.
2. STARCHILD_TOOL_CALLER_ID env (set by the agent)
3. Synthetic `f"{tool_default}:{int(time.time())}"` — tags the call so
charges are attributable to *some* identifier even when the agent
didn't inject one (standalone CLI runs, tests, cron).
"""
merged: Dict[str, str] = dict(extra or {})
has_caller = any(k.lower() == "sc-caller-id" for k in merged)
if not has_caller:
cid = os.environ.get("STARCHILD_TOOL_CALLER_ID") \
or f"{tool_default}:{int(time.time())}"
merged["SC-CALLER-ID"] = cid
return merged
def record_response(response,
request_url: str,
request_payload: Optional[Dict[str, Any]] = None,
api_type_hint: Optional[str] = None) -> None:
"""Inspect a sc-proxy response and append a ledger row when paid.
Best-effort. Silently no-ops when:
- response carries no X-Credits-Used / X-Credits-Api-Type
- cost is 0 or unparseable
- file write fails
Never raises — must not break a real request flow.
"""
try:
headers = getattr(response, "headers", None) or {}
used = headers.get("X-Credits-Used") or headers.get("x-credits-used")
api_type = (headers.get("X-Credits-Api-Type")
or headers.get("x-credits-api-type")
or api_type_hint)
if not used or not api_type:
return
try:
cost_f = float(used)
except (TypeError, ValueError):
return
if cost_f <= 0:
return
turn_id = os.environ.get("STARCHILD_USER_TURN_ID") or ""
caller_id = os.environ.get("STARCHILD_TOOL_CALLER_ID") or ""
host = ""
try:
host = urlparse(request_url).netloc or ""
except Exception:
pass
details: Dict[str, Any] = {}
if isinstance(request_payload, dict):
for k in _PAYLOAD_ALLOWLIST:
v = request_payload.get(k)
if v not in (None, "", []):
details[k] = v
# fal.ai puts the model in the URL path, not the body.
if "model" not in details and api_type == "falai":
try:
path = urlparse(request_url).path or ""
model_path = path.lstrip("/")
if "/requests/" in model_path:
model_path = model_path.split("/requests/", 1)[0]
if model_path and not model_path.startswith("requests/"):
details["model"] = model_path
details["provider"] = "fal"
except Exception:
pass
_append_ledger(
turn_id=turn_id,
caller_id=caller_id,
api_type=api_type,
cost_usd=cost_f,
url_host=host,
details=details or None,
)
except Exception:
# Never let cost tracking break the actual request.
pass
def _ledger_dir() -> Path:
base = os.environ.get("STARCHILD_COST_LEDGER_DIR") or _DEFAULT_LEDGER_DIR
p = Path(base)
try:
p.mkdir(parents=True, exist_ok=True)
except OSError:
p = Path("/tmp/starchild_cost_ledger")
p.mkdir(parents=True, exist_ok=True)
return p
def _today_path() -> Path:
today = datetime.now(timezone.utc).strftime("%Y-%m-%d")
return _ledger_dir() / f"{today}.jsonl"
def _derive_tool(caller_id: str, api_type: str) -> str:
"""Match starchild-clawd's _derive_tool_from_caller fallback."""
if not caller_id:
return api_type or "unknown"
# chat:{sid}/tool:{name} → name
if "/tool:" in caller_id:
return caller_id.rsplit("/tool:", 1)[-1] or api_type
# skill:{name} | job:{id} | video:{ts}
head = caller_id.split(":", 1)[0]
return head or api_type or "unknown"
def _append_ledger(*, turn_id: str, caller_id: str, api_type: str,
cost_usd: float, url_host: str,
details: Optional[Dict[str, Any]]) -> None:
row = {
"ts": round(time.time(), 3),
"turn_id": turn_id,
"caller_id": caller_id,
"tool": _derive_tool(caller_id, api_type),
"api_type": api_type or "unknown",
"cost_usd": round(cost_usd, 8),
"url_host": url_host or "",
}
if details:
row["details"] = details
line = json.dumps(row, ensure_ascii=False, separators=(",", ":")) + "\n"
path = _today_path()
try:
with open(path, "ab") as f:
try:
fcntl.flock(f.fileno(), fcntl.LOCK_EX)
except OSError:
pass
try:
f.write(line.encode("utf-8"))
f.flush()
try:
os.fsync(f.fileno())
except OSError:
pass
finally:
try:
fcntl.flock(f.fileno(), fcntl.LOCK_UN)
except OSError:
pass
except OSError:
pass
"""
Image 3D skill exports — script-mode skill.
Usage from a bash block:
python3 - <<'EOF'
import sys
sys.path.insert(0, "/data/workspace/skills/image-3d")
from exports import generate_3d, CATEGORY_STYLES, CATEGORY_ASPECT_RATIOS
result = generate_3d(
prompt="a cute robot character",
category="character",
style="chibi",
)
print(result)
EOF
"""
import os
import sys
# Ensure the skill directory is importable regardless of cwd.
_SKILL_DIR = os.path.dirname(os.path.abspath(__file__))
if _SKILL_DIR not in sys.path:
sys.path.insert(0, _SKILL_DIR)
from generate_3d import ( # noqa: E402
generate_3d,
CATEGORY_STYLES,
CATEGORY_ASPECT_RATIOS,
VALID_ASPECT_RATIOS,
MODELS,
)
__all__ = [
"generate_3d",
"CATEGORY_STYLES",
"CATEGORY_ASPECT_RATIOS",
"VALID_ASPECT_RATIOS",
"MODELS",
]
#!/usr/bin/env python3
"""3D-style image generation script — 3D rendered 2D images for all 3D scenarios.
Generates 3D-style 2D images (NOT .glb/.obj 3D model files).
Covers: 3D characters, product renders, dioramas/isometric, icons, text,
interior design, architecture, scenes, and game assets.
Supports three models:
- nano2 (fal-ai/gemini-3.1-flash-image-preview or /edit) — fastest ~15s, good for drafts
- nanopro (fal-ai/gemini-3-pro-image-preview or /edit) — balanced ~25s, good quality (default)
- gpt (openai/gpt-image-2 or /edit) — best quality, slow ~150s
Modes:
- Text-to-image (primary): generates 3D-style images from text descriptions
- Image-to-3D-style (edit): transforms a reference image into 3D-style rendering
Flow: resolve image → build prompt → submit to fal queue → poll → download.
Cost tracking: uses _cost_track.py to record per-call costs via sc-proxy
headers so the agent's per-turn cost_summary picks up this skill's cost.
Local testing: set FAL_KEY env var to call fal.ai directly (no sc-proxy).
"""
import requests
import json
import time
import os
import sys
import base64
import mimetypes
from datetime import datetime
from pathlib import Path
import urllib3
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
# Make _cost_track importable when this script is invoked from any CWD.
_HERE = os.path.dirname(os.path.abspath(__file__))
if _HERE not in sys.path:
sys.path.insert(0, _HERE)
from _cost_track import caller_headers, record_response # noqa: E402
# Local testing: when FAL_KEY env var is set, call fal.ai directly
# (no sc-proxy). In production, sc-proxy injects the real key.
_FAL_KEY = os.environ.get("FAL_KEY")
_LOCAL_MODE = bool(_FAL_KEY)
PROXY_URL = 'http://sc-proxy.internal:8080'
PROXIES = {} if _LOCAL_MODE else {'http': PROXY_URL, 'https': PROXY_URL}
# ── Model configuration ──────────────────────────────────────────────
MODELS = {
"nano2": {
"edit": "fal-ai/gemini-3.1-flash-image-preview/edit",
"generate": "fal-ai/gemini-3.1-flash-image-preview",
"timeout": 90, # nano2 faster
"poll_interval": 2,
},
"nanopro": {
"edit": "fal-ai/gemini-3-pro-image-preview/edit",
"generate": "fal-ai/gemini-3-pro-image-preview",
"timeout": 120, # 2 min
"poll_interval": 3,
},
"gpt": {
"edit": "openai/gpt-image-2/edit",
"generate": "openai/gpt-image-2",
"timeout": 600, # 10 min
"poll_interval": 5,
},
}
DEFAULT_MODEL = "nanopro"
# Supported image extensions for local file validation
SUPPORTED_IMAGE_EXTS = {'.jpg', '.jpeg', '.png', '.webp', '.bmp'}
MAX_IMAGE_BYTES = 10 * 1024 * 1024 # 10 MB
# ── 3D Category × Style prompt templates ─────────────────────────────
# Each category has multiple style presets. The prompt template encodes
# the 3D rendering keywords (materials, lighting, camera, engine style)
# that produce the best results with text-to-image models.
#
# Design sources:
# - image-create 3D category (character, product, diorama, icon, text, scene)
# - architecture-rendering skill (exterior/interior render prompts)
# - interior-design-visualization skill (room styles, materials)
# - 3d-model-generation skill (PBR materials, game assets)
# - fal-regenerate-3d skill (character pipeline, Pixar/chibi styles)
CATEGORY_STYLES = {
"character": {
"chibi": (
"3D chibi character design, cute stylized proportions, oversized head, "
"Pixar-quality rendering, soft ambient occlusion, vibrant colors, "
"clean studio lighting, white background"
),
"realistic": (
"3D realistic character model, detailed anatomy, PBR materials, "
"studio lighting, neutral pose, game-ready quality, "
"subsurface scattering on skin, high-detail textures"
),
"cartoon": (
"3D cartoon character, Disney/Pixar style, rounded features, "
"bright colors, expressive face, clean background, "
"smooth shading, appealing design, family-friendly aesthetic"
),
"fantasy": (
"3D fantasy character, detailed armor and weapons, epic pose, "
"dramatic lighting, RPG game quality, PBR metallic materials, "
"volumetric fog, cinematic composition"
),
"default": (
"3D character design, professional quality, clean rendering, "
"studio lighting, appealing proportions, detailed materials"
),
},
"product": {
"floating": (
"3D product render, floating at 3/4 angle, studio lighting with "
"soft reflections, gradient background, photorealistic materials, "
"octane render quality, subtle shadow beneath"
),
"exploded": (
"3D exploded view product render, components separated showing "
"internal structure, clean white background, technical illustration "
"quality, precise engineering visualization"
),
"turntable": (
"3D product turntable view, multiple angles in one image, "
"360 degree showcase, white background, commercial quality, "
"consistent studio lighting across all angles"
),
"default": (
"3D product render, photorealistic materials, professional lighting, "
"clean composition, commercial quality, subtle reflections"
),
},
"diorama": {
"isometric": (
"3D isometric diorama, miniature scene, warm lighting, tiny detailed "
"elements, tilt-shift depth of field, clay render aesthetic, "
"45-degree top-down camera angle, charming miniature world"
),
"lowpoly": (
"3D low-poly diorama, geometric shapes, flat shading, pastel colors, "
"minimalist design, game art style, clean edges, "
"stylized environment, indie game aesthetic"
),
"realistic": (
"3D realistic miniature scene, detailed textures, natural lighting, "
"macro photography feel, photorealistic materials, "
"depth of field, architectural model quality"
),
"default": (
"3D diorama scene, detailed miniature world, warm lighting, "
"charming atmosphere, isometric perspective, clean rendering"
),
},
"icon": {
"ios": (
"3D app icon design, glossy material, rounded square shape, "
"soft shadows, glass material with subtle gradient, "
"clean white background, iOS style, skeuomorphic detail"
),
"material": (
"3D material design icon, flat colors with depth, subtle shadows, "
"Google Material style, clean edges, geometric precision, "
"modern UI icon, layered paper effect"
),
"game": (
"3D game icon, detailed rendering, fantasy style, glowing effects, "
"dark background, RPG item icon quality, ornate border, "
"magical particle effects"
),
"default": (
"3D icon design, clean rendering, professional quality, "
"suitable for app or web, subtle shadows, modern aesthetic"
),
},
"text": {
"chrome": (
"3D text rendering, bold chrome letters, metallic reflections, "
"volumetric lighting, cinematic composition, mirror-finish surface, "
"dramatic studio lighting, dark background"
),
"neon": (
"3D neon text, glowing letters, dark background, colorful light effects, "
"cyberpunk atmosphere, light bloom, reflective wet floor, "
"futuristic urban setting"
),
"wood": (
"3D wooden text, natural wood texture, warm lighting, rustic feel, "
"craft quality, visible wood grain, soft shadows, "
"cozy atmosphere, handmade aesthetic"
),
"candy": (
"3D candy text, glossy colorful letters, sweet theme, playful design, "
"fun atmosphere, sugar coating effect, sprinkles, "
"bright pastel background, whimsical"
),
"default": (
"3D text rendering, bold letters, professional quality, "
"clean composition, studio lighting, modern typography"
),
},
"interior": {
"modern": (
"3D interior design render, modern minimalist style, clean lines, "
"natural light through large windows, Scandinavian aesthetic, "
"architectural visualization quality, warm neutral palette, "
"polished concrete and natural wood materials"
),
"luxury": (
"3D luxury interior render, high-end materials, marble and gold accents, "
"dramatic lighting, penthouse quality, crystal chandelier, "
"rich textures, velvet and silk fabrics, opulent atmosphere"
),
"cozy": (
"3D cozy interior render, warm lighting, soft textures, "
"comfortable furniture, hygge atmosphere, warm color palette, "
"plush rugs, ambient candlelight, inviting space"
),
"industrial": (
"3D industrial loft interior, exposed brick walls, metal beams, "
"Edison bulbs, urban chic, raw concrete, reclaimed wood, "
"open floor plan, warehouse conversion aesthetic"
),
"default": (
"3D interior design visualization, professional rendering, "
"realistic materials and lighting, architectural quality, "
"well-composed room view, natural daylight"
),
},
"architecture": {
"modern": (
"3D modern architecture render, clean geometric forms, glass and steel, "
"dramatic sky, architectural visualization, photorealistic quality, "
"landscaping context, golden hour lighting, professional archviz"
),
"traditional": (
"3D traditional architecture render, classical elements, stone and wood, "
"warm lighting, heritage feel, ornate details, "
"historical accuracy, mature landscaping"
),
"futuristic": (
"3D futuristic architecture, organic forms, sustainable design, "
"green technology, sci-fi cityscape, parametric facade, "
"bioluminescent accents, utopian atmosphere"
),
"aerial": (
"3D aerial architectural render, bird's eye view, masterplan perspective, "
"45-degree angle, surrounding context visible, "
"landscaping and roads, professional archviz quality"
),
"night": (
"3D architectural night render, dramatic nighttime scene, "
"warm interior glow through windows, facade uplighting, "
"wet pavement reflections, sophisticated atmosphere"
),
"default": (
"3D architectural visualization, professional rendering, "
"realistic materials, environmental context, "
"natural lighting, photorealistic quality"
),
},
"scene": {
"fantasy": (
"3D fantasy scene, magical environment, floating islands, "
"crystal formations, ethereal lighting, concept art quality, "
"volumetric god rays, enchanted atmosphere"
),
"scifi": (
"3D sci-fi scene, space station interior, holographic displays, "
"futuristic technology, cinematic lighting, neon accents, "
"high-tech environment, cyberpunk aesthetic"
),
"nature": (
"3D nature scene, lush forest, volumetric light rays, "
"detailed foliage, photorealistic rendering, "
"atmospheric perspective, serene mood"
),
"urban": (
"3D urban scene, city street, neon signs, rain reflections, "
"cyberpunk atmosphere, dense urban environment, "
"atmospheric fog, cinematic composition"
),
"default": (
"3D scene rendering, detailed environment, atmospheric lighting, "
"professional quality, immersive composition"
),
},
"game_asset": {
"weapon": (
"3D game weapon render, detailed metallic materials, "
"fantasy RPG style, glowing enchantment effects, "
"dark background, item showcase, game-ready quality"
),
"environment": (
"3D game environment concept, stylized world design, "
"vibrant colors, level design quality, "
"atmospheric lighting, explorable space feel"
),
"prop": (
"3D game prop render, detailed object, stylized textures, "
"clean presentation, game-ready quality, "
"consistent art style, subtle wear and tear"
),
"vehicle": (
"3D game vehicle render, detailed model, dynamic angle, "
"motion blur suggestion, studio lighting, "
"polished materials, racing game quality"
),
"default": (
"3D game asset render, professional quality, "
"game-ready presentation, clean lighting, "
"detailed materials, stylized aesthetic"
),
},
}
# ── Category aspect ratio defaults ────────────────────────────────────
# Auto-selected when user doesn't specify aspect_ratio.
CATEGORY_ASPECT_RATIOS = {
"character": "3:4", # Portrait orientation for characters
"product": "1:1", # Square for product showcase
"diorama": "1:1", # Square for isometric scenes
"icon": "1:1", # Square for icons
"text": "16:9", # Wide for text banners
"interior": "16:9", # Wide for room views
"architecture": "16:9", # Wide for building exteriors
"scene": "16:9", # Wide for environments
"game_asset": "1:1", # Square for asset showcase
}
# ── Constants ─────────────────────────────────────────────────────────
MAX_COUNT = 4 # fal.ai API supports up to 4 images per call
VALID_ASPECT_RATIOS = {
"1:1", "2:3", "3:2", "3:4", "4:3", "4:5", "5:4", "9:16", "16:9", "21:9",
}
DEFAULT_ASPECT_RATIO = "1:1"
VALID_OUTPUT_FORMATS = {"jpeg", "png", "webp"}
DEFAULT_OUTPUT_FORMAT = "png"
OUTPUT_DIR = "output/images"
def _get_auth_key():
"""Return the appropriate fal API key."""
return _FAL_KEY if _LOCAL_MODE else 'fake-falai-key-12345'
def _get_model_config(model_key):
"""Return model config dict for the given key."""
return MODELS.get(model_key, MODELS[DEFAULT_MODEL])
def _resolve_image(image_path=None, image_url=None):
"""Resolve an image input to a URL for the fal API.
Accepts either a local file path or a public URL.
Local files are base64-encoded as data URIs.
Returns (url_string, error_string).
"""
if not image_path and not image_url:
return None, None # No image = text-to-image mode
if image_path:
p = Path(image_path)
if not p.exists():
return None, f"File not found: {image_path}"
if not p.is_file():
return None, f"Not a file: {image_path}"
ext = p.suffix.lower()
if ext not in SUPPORTED_IMAGE_EXTS:
return None, (
f"Unsupported image format: {ext}. "
f"Supported: {', '.join(sorted(SUPPORTED_IMAGE_EXTS))}"
)
size = p.stat().st_size
if size > MAX_IMAGE_BYTES:
return None, (
f"Image too large: {size / 1024 / 1024:.1f} MB "
f"(max {MAX_IMAGE_BYTES / 1024 / 1024:.0f} MB)"
)
mime_type = mimetypes.guess_type(str(p))[0] or "image/jpeg"
with open(p, 'rb') as f:
b64 = base64.b64encode(f.read()).decode('ascii')
return f"data:{mime_type};base64,{b64}", None
# URL input
if not image_url.startswith(("http://", "https://")):
return None, (
"image_url must be a public HTTP(S) URL. "
"For local files, use the reference_path parameter instead."
)
return image_url, None
def _build_3d_prompt(prompt=None, category="character", style=None):
"""Construct the 3D generation prompt from category + style + user prompt.
Priority:
1. prompt + category/style → user prompt enhanced with 3D style template
2. prompt only → use as-is with generic 3D enhancement
3. category + style → use the built-in style template as the full prompt
4. category only → use the default style for that category
Returns the final prompt string.
"""
# Get the style templates for this category
cat_styles = CATEGORY_STYLES.get(category, CATEGORY_STYLES["character"])
# Resolve style key
if style and style in cat_styles:
style_text = cat_styles[style]
else:
style_text = cat_styles.get("default", cat_styles[list(cat_styles.keys())[0]])
if prompt:
# User provided a custom prompt — enhance with 3D style context
return (
f"{prompt}. "
f"Render style: {style_text}."
)
else:
# No custom prompt — use style template as the full prompt
return style_text
def _build_edit_prompt(prompt=None, category="character", style=None):
"""Build prompt for edit mode (with reference image input).
Wraps the 3D prompt with instructions to transform the reference
image into a 3D-style rendering while preserving key features.
"""
base_prompt = _build_3d_prompt(prompt, category, style)
return (
f"Transform this image into a 3D rendered style. "
f"Preserve the key subject, composition, and important details "
f"while applying 3D rendering aesthetics. {base_prompt}"
)
def _aspect_ratio_to_size(aspect_ratio):
"""Convert aspect ratio string to fal image_size dict.
Sizes aligned with image_generate tool capabilities
(core/image_models.py _STD_ASPECTS / _NANO2_ASPECTS).
"""
mapping = {
"1:1": {"width": 1024, "height": 1024},
"2:3": {"width": 680, "height": 1024},
"3:2": {"width": 1024, "height": 680},
"3:4": {"width": 768, "height": 1024},
"4:3": {"width": 1024, "height": 768},
"4:5": {"width": 816, "height": 1024},
"5:4": {"width": 1024, "height": 816},
"9:16": {"width": 576, "height": 1024},
"16:9": {"width": 1024, "height": 576},
"21:9": {"width": 1024, "height": 440},
}
return mapping.get(aspect_ratio, mapping["1:1"])
def _build_request_body(prompt, image_url=None, aspect_ratio="1:1", model_key="nanopro",
count=1, output_format="png"):
"""Build the request body for the fal API."""
body = {
"prompt": prompt,
"num_images": count,
"seed": int(time.time() * 1000) % (2**32),
"output_format": output_format,
}
# Add image URL for edit mode
if image_url:
body["image_urls"] = [image_url]
# nano2/nanopro use aspect_ratio string; gpt uses image_size object
if aspect_ratio and aspect_ratio in VALID_ASPECT_RATIOS:
if model_key != "gpt":
body["aspect_ratio"] = aspect_ratio
else:
body["image_size"] = _aspect_ratio_to_size(aspect_ratio)
body["quality"] = "high"
return body
def _submit_request(prompt, image_url, model_key, headers, aspect_ratio="1:1",
count=1, output_format="png"):
"""Submit a request to the fal queue (edit or generate mode)."""
cfg = _get_model_config(model_key)
# Choose edit vs generate endpoint based on whether we have an image
if image_url:
model_id = cfg["edit"]
else:
model_id = cfg["generate"]
submit_url = f"https://queue.fal.run/{model_id}"
body = _build_request_body(prompt, image_url, aspect_ratio, model_key,
count=count, output_format=output_format)
resp = requests.post(
submit_url, headers=headers, json=body,
proxies=PROXIES, verify=False, timeout=90,
)
record_response(resp, request_url=submit_url, request_payload=body)
if resp.status_code != 200:
return None, f"Submit failed: {resp.status_code} - {resp.text[:300]}"
data = resp.json()
cost = float(resp.headers.get('X-Credits-Used', 0))
data['_cost'] = cost
return data, None
def _poll_until_done(status_url, request_id, model_key):
"""Poll the fal queue until the request completes or fails."""
cfg = _get_model_config(model_key)
headers = {'Authorization': f'Key {_get_auth_key()}'}
deadline = time.time() + cfg["timeout"]
poll_interval = cfg["poll_interval"]
while time.time() < deadline:
try:
poll_resp = requests.get(
status_url, headers=headers,
proxies=PROXIES, verify=False, timeout=60,
)
status_data = poll_resp.json()
status = status_data.get('status')
if status == 'COMPLETED':
return "COMPLETED", None
elif status in ('FAILED', 'CANCELLED'):
return status, f"Generation {status}"
except requests.RequestException:
pass
time.sleep(poll_interval)
return "TIMEOUT", f"Generation timed out after {cfg['timeout'] // 60} minutes"
def _extract_image_urls(result_json):
"""Extract image URLs from fal response across model variants."""
if not isinstance(result_json, dict):
return []
urls = []
for key in ("images", "output", "outputs", "data"):
arr = result_json.get(key)
if isinstance(arr, list):
for item in arr:
if isinstance(item, dict) and isinstance(item.get("url"), str):
urls.append(item["url"])
elif isinstance(item, dict) and isinstance(item.get("b64_json"), str):
urls.append(f"data:image/png;base64,{item['b64_json']}")
elif isinstance(item, str) and item.startswith("http"):
urls.append(item)
if not urls:
for key in ("image", "output_image"):
node = result_json.get(key)
if isinstance(node, dict) and isinstance(node.get("url"), str):
urls.append(node["url"])
elif isinstance(node, str) and node.startswith("http"):
urls.append(node)
return urls
def _download_image(url, index, label, timestamp):
"""Download a single image from fal CDN to the output directory."""
os.makedirs(OUTPUT_DIR, exist_ok=True)
if url.startswith("data:"):
ext = ".png"
filename = f"{timestamp}_{label}_{index}{ext}"
local_path = os.path.join(OUTPUT_DIR, filename)
b64_data = url.split(",", 1)[1]
img_bytes = base64.b64decode(b64_data)
with open(local_path, 'wb') as f:
f.write(img_bytes)
return local_path, len(img_bytes)
ext = ".png"
if ".jpg" in url or ".jpeg" in url:
ext = ".jpg"
elif ".webp" in url:
ext = ".webp"
filename = f"{timestamp}_{label}_{index}{ext}"
local_path = os.path.join(OUTPUT_DIR, filename)
resp = requests.get(url, timeout=120)
resp.raise_for_status()
with open(local_path, 'wb') as f:
f.write(resp.content)
return local_path, len(resp.content)
def generate_3d(
prompt=None,
reference_path=None,
reference_url=None,
category="character",
style=None,
model="nanopro",
count=1,
aspect_ratio=None,
output_format=None,
):
"""Generate 3D-style 2D images.
This is the primary function for all 3D image generation operations.
Produces 3D-rendered 2D images (NOT .glb/.obj 3D model files).
Supports both text-to-image (primary) and edit mode (reference → 3D style).
Args:
prompt: Text description of the desired 3D image.
When provided with a category/style, enhances the style template.
When provided alone, used as the primary instruction with 3D enhancement.
reference_path: Local workspace file path to a reference image (optional).
Used for image-to-3D-style transformation.
reference_url: Public HTTPS URL of a reference image (optional).
Used for image-to-3D-style transformation.
category: 3D category preset — one of:
character, product, diorama, icon, text, interior,
architecture, scene, game_asset.
style: Sub-style within the category (see CATEGORY_STYLES).
If None, uses the "default" style for the category.
model: Model key — "nanopro" (default, fast ~25s) or
"gpt" (best quality ~150s).
count: Number of images to generate (1-4).
Uses fal.ai native num_images for efficient batch generation.
aspect_ratio: Output ratio — "1:1", "3:4", "4:3", "9:16", "16:9".
If None, auto-selected based on category.
output_format: Output image format — "png" (default), "jpeg", or "webp".
Returns:
dict with keys:
success (bool): Whether generation succeeded.
images (list[dict]): List of generated images, each with:
local_path (str): Path to downloaded file.
url (str): Original fal CDN URL.
size_bytes (int): File size.
model (str): Model used.
category (str): Category applied.
style (str): Style applied.
mode (str): "edit" or "generate".
cost (float): Total credits used.
error (str|None): Error message if failed.
"""
# ── Validate model ────────────────────────────────────────────────
model_key = model.lower() if model else DEFAULT_MODEL
if model_key not in MODELS:
return {
"success": False,
"error": f"Unknown model: {model}. Use 'nanopro' or 'gpt'.",
"images": [],
}
# ── Validate category ─────────────────────────────────────────────
if category not in CATEGORY_STYLES:
return {
"success": False,
"error": (
f"Unknown category: {category}. "
f"Available: {', '.join(sorted(CATEGORY_STYLES.keys()))}"
),
"images": [],
}
# ── Validate style ────────────────────────────────────────────────
cat_styles = CATEGORY_STYLES[category]
resolved_style = style or "default"
if resolved_style not in cat_styles:
return {
"success": False,
"error": (
f"Unknown style '{resolved_style}' for category '{category}'. "
f"Available: {', '.join(sorted(cat_styles.keys()))}"
),
"images": [],
}
# ── Resolve aspect ratio ──────────────────────────────────────────
if aspect_ratio is None:
aspect_ratio = CATEGORY_ASPECT_RATIOS.get(category, DEFAULT_ASPECT_RATIO)
if aspect_ratio not in VALID_ASPECT_RATIOS:
return {
"success": False,
"error": f"Invalid aspect_ratio: {aspect_ratio}. Use: {', '.join(sorted(VALID_ASPECT_RATIOS))}",
"images": [],
}
# ── Validate count and output format ──────────────────────────────
count = max(1, min(MAX_COUNT, int(count)))
fmt = output_format if output_format in VALID_OUTPUT_FORMATS else DEFAULT_OUTPUT_FORMAT
# ── Resolve reference image ───────────────────────────────────────
image_url, err = _resolve_image(reference_path, reference_url)
if err:
return {"success": False, "error": err, "images": []}
# Determine mode: edit (with reference image) or generate (text-only)
has_image = image_url is not None
mode = "edit" if has_image else "generate"
# ── Build prompt ──────────────────────────────────────────────────
if has_image:
final_prompt = _build_edit_prompt(prompt, category, resolved_style)
else:
if not prompt and resolved_style == "default":
return {
"success": False,
"error": (
"A prompt is required for text-to-image generation. "
"Provide a prompt describing what you want, or specify "
"a category + style to use a built-in template."
),
"images": [],
}
final_prompt = _build_3d_prompt(prompt, category, resolved_style)
# ── Generate images ───────────────────────────────────────────────
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
all_images = []
total_cost = 0.0
errors = []
headers = {
'Authorization': f'Key {_get_auth_key()}',
'Content-Type': 'application/json',
}
headers.update(caller_headers(tool_default="image-3d"))
# Single API call with num_images=count for efficient batch generation
submit_data, submit_err = _submit_request(
final_prompt, image_url, model_key, headers, aspect_ratio,
count=count, output_format=fmt,
)
if submit_err:
return {"success": False, "error": submit_err, "images": []}
request_id = submit_data.get('request_id')
status_url = submit_data.get('status_url')
response_url = submit_data.get('response_url')
total_cost += submit_data.get('_cost', 0)
if not request_id or not status_url:
return {"success": False, "error": "Missing request_id or status_url in response", "images": []}
# Poll until done
status, poll_err = _poll_until_done(status_url, request_id, model_key)
if status != "COMPLETED":
return {"success": False, "error": poll_err or status, "images": []}
# Fetch result
try:
result_headers = {'Authorization': f'Key {_get_auth_key()}'}
result_resp = requests.get(
response_url, headers=result_headers,
proxies=PROXIES, verify=False, timeout=60,
)
result_json = result_resp.json()
except Exception as e:
return {"success": False, "error": f"Failed to fetch result: {e}", "images": []}
# Extract and download images
img_urls = _extract_image_urls(result_json)
if not img_urls:
return {"success": False, "error": "No images in response", "images": []}
for j, img_url in enumerate(img_urls):
try:
label = f"3d_{category}_{resolved_style}"
local_path, size_bytes = _download_image(
img_url, j, label, timestamp,
)
all_images.append({
"local_path": local_path,
"url": img_url if not img_url.startswith("data:") else "(base64)",
"size_bytes": size_bytes,
})
except Exception as e:
errors.append(f"Download failed: {e}")
# ── Build result ──────────────────────────────────────────────────
result = {
"success": len(all_images) > 0,
"images": all_images,
"model": model_key,
"category": category,
"style": resolved_style,
"mode": mode,
"output_format": fmt,
"count_requested": count,
"count_generated": len(all_images),
"cost": round(total_cost, 6),
}
if errors:
result["error"] = "; ".join(errors)
return resultRelated skills
How it compares
Use image-3d for stylized 3D-looking 2D raster assets rather than skills targeting flat illustration or true 3D mesh output.
FAQ
What does image-3d do?
3D-style image generation: 3D characters, product renders, isometric dioramas, 3D icons, 3D text, interior design renders, architectural visualization, 3D scenes, game assets.
When should I use image-3d?
User asks about image 3d or related SKILL.md workflows.
Is image-3d safe to install?
Review the Security Audits panel on this page before installing in production.