s&box Model Importer

GLB/GLTF is not supported by s&box. Convert to FBX first (Blender: File > Export > FBX).
Source Engine MDL
.vvd .vtx
Use Attach Folder to load companion .vvd + .vtx files
Materials
Import Mode
Import FBX, OBJ, or GLB models.
Model
Import Settings
Bone Mappings
Material Generation
Setting this to 1 can make your object black if you don't have a correct config for metallic textures.
Bone Mapping
Humanoid model? Remap Mixamo, Unreal, or custom bones to s&box Citizen skeleton for animations.

or
Source 1 Model Import

Convert Source Engine models and materials into s&box-ready assets.
Select the root asset folder so our auto-detection can match textures to materials.

my_asset/ ← select this
models/
weapon.mdl
weapon.vvd
weapon.vtx
materials/
weapon.vmt
weapon.vtf
Textures are auto-matched to materials when the full folder is provided
Starting up 3D viewer...
Loading Three.js engine
Uploaded Model
s&box Citizen (reference)
Loading citizen...
Bone /
Skeleton Bones
Click a bone in either viewport or in the list below
Material Textures
Level of Detail (LoD)
LoD creates simplified copies of the model that Source 2 can switch to when the asset is farther away or smaller on screen. This improves runtime performance, but aggressive decimation can damage the silhouette, materials, or rigging. LoD is off by default; when you enable it, the lowest-detail LoD starts at 15% of the original mesh. Preview the lowest LoD before exporting.
LoD Levels
Max Threshold
Sets thresholds evenly from 0 to max
Collision Mesh

Choose which mesh to use for the physics collision. Lower-poly meshes improve runtime performance.

Using original mesh as collision.
Generated Files
Drag the folder into your project's Assets/ directory.
.vmdl

              
.vmat (first material)

              

API & integration

Process 3D models programmatically via POST /api/tools/model (async, poll the returned statusUrl until status=done).

JOB=$(curl -s -X POST https://api.sboxcool.com/tools/model \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -F "[email protected]" -F "pipeline=source1" -F "lodLevels=2")
# then poll the returned statusUrl until status=done

Process 3D models programmatically. Get your API key first. Rate limit: 10/min, 60/hr, 500/day. Max 5 concurrent model jobs per user.

POST /api/tools/model

Upload a 3D model to start an async processing pipeline. Returns a job ID to poll for status. Download links expire after 1 hour.

Parameters (multipart/form-data)
FieldDescription
filerequired3D model file (FBX or OBJ, max 100MB)
formatoptionalInput format: fbx (default) or obj
pipelineoptionalstandard (default) or source1 (generates VMDL manifest)
ratiooptionalBase decimation ratio 0.01 - 1.0 (default: 0.5). Lower = fewer faces.
LoD Options
lodLevelsoptionalNumber of additional LoD levels to generate, 0-5 (default: 0)
lodFinalRatiooptionalFinal lowest-detail LoD ratio 0.001 - 1.0 (default: 0.15). Used for exponential decay curve.
lodMaxThresholdoptionalMax switch threshold for furthest LoD, 1-500 (default: 50)
PBR Texture Generation
generatePbroptionaltrue to generate normal/roughness/AO maps from uploaded color texture
pbrNormalStrengthoptional0.5 - 4.0 (default: 1.5)
pbrRoughnessBaseoptional100 - 240 (default: 180)
pbrAoStrengthoptional0 - 1.0 (default: 0.3)
Textures
tex_{name}_{slot}optionalTexture file. name = material name, slot = color, normal, roughness, metalness, emissive, ao
Step 1: Submit job (returns 202)
{
  "ok": true,
  "jobId": "a1b2c3d4e5f6",
  "statusUrl": "/api/tools/model/a1b2c3d4e5f6",
  "message": "Model processing started. Poll statusUrl for progress."
}
Step 2: Poll status (GET /api/tools/model/:id)
{
  "jobId": "a1b2c3d4e5f6",
  "status": "done",           // "queued" | "running" | "done" | "error"
  "progress": ["Decimating model...", "LOD0 ready: 512 KB", ...],
  "result": {
    "originalFaces": 15000,
    "resultFaces": 7500,
    "pipeline": "source1",
    "lodLevels": 2,
    "lods": [
      {"level": 1, "ratio": 0.56, "threshold": 25, "faces": 8400, "size": 190000},
      {"level": 2, "ratio": 0.15, "threshold": 50, "faces": 2250, "size": 62000}
    ]
  },
  "downloads": {
    "lod0": "/uploads/temp/123/abc_model_lod0.glb",
    "lod1": "/uploads/temp/123/abc_model_lod1.glb",
    "lod2": "/uploads/temp/123/abc_model_lod2.glb",
    "vmdl": "/uploads/temp/123/abc_model.vmdl",
    "pbrTextures": "/uploads/temp/123/abc_pbr_textures.zip"
  },
  "expiresAt": 1711900000000
}
Example: cURL
# 1. Submit
JOB=$(curl -s -X POST https://api.sboxcool.com/tools/model \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -F "[email protected]" \
  -F "format=fbx" \
  -F "pipeline=source1" \
  -F "ratio=0.5" \
  -F "lodLevels=2" \
  -F "lodMaxThreshold=50" \
  -F "generatePbr=true" \
  -F "tex_body_color=@body_color.png")

JOB_ID=$(echo $JOB | jq -r '.jobId')

# 2. Poll until done
while true; do
  STATUS=$(curl -s -H "Authorization: Bearer YOUR_API_KEY" \
    https://api.sboxcool.com/tools/model/$JOB_ID)
  echo $STATUS | jq '.status, .progress[-1]'
  [ "$(echo $STATUS | jq -r '.status')" = "done" ] && break
  sleep 3
done

# 3. Download
curl -o model_lod0.glb "https://sboxcool.com$(echo $STATUS | jq -r '.downloads.lod0')"
Example: Python
import requests, time

headers = {"Authorization": "Bearer YOUR_API_KEY"}

# Submit
resp = requests.post("https://api.sboxcool.com/tools/model", headers=headers,
    files={"file": open("character.fbx", "rb"), "tex_body_color": open("body.png", "rb")},
    data={"format": "fbx", "pipeline": "source1", "ratio": "0.5",
          "lodLevels": "2", "generatePbr": "true"})
job = resp.json()

# Poll
while True:
    status = requests.get(f"https://sboxcool.com{job['statusUrl']}", headers=headers).json()
    print(status["status"], status["progress"][-1] if status["progress"] else "")
    if status["status"] in ("done", "error"):
        break
    time.sleep(3)

# Download all LoDs
for key, url in status["downloads"].items():
    data = requests.get(f"https://sboxcool.com{url}").content
    with open(f"{key}.{'zip' if 'zip' in url else 'glb'}", "wb") as f:
        f.write(data)
MCP / HTTP Clients

This is a two-step async API. Step 1: POST with multipart/form-data to upload the model file and start processing (requires file upload support in your MCP/HTTP tool). Step 2: Poll the statusUrl with GET requests until status is "done", then download files from the downloads URLs with simple GET requests. Most MCP HTTP tools can handle the polling + download steps natively since they are plain JSON GET requests. If your MCP tool cannot send multipart/form-data, you will need a proxy or CLI wrapper for the initial upload.