scenario-roblox-assets
About scenario-roblox-assets
A Roblox asset-production skill for creators preparing props and avatar accessories in Blender and Roblox Studio. It covers mesh and texture preparation, Studio previews, fit checks, and local validation, while leaving the final upload step to a human or computer-use agent.
Key features
- 01GLB inspection and Blender preparation
- 02Mesh scale, orientation, and triangle checks
- 03Roblox map preparation and audits
- 04Studio preview and accessory fit tests
- 05Local UGC accessory validation
Use cases
- 01Preparing a prop to a target size in studs
- 02Checking a mesh before importing to Studio
- 03Fitting and auditing a hat accessory
- 04Validating an avatar item before upload
- 05Handing off an asset for Roblox import
MCP support
Uses the scenario-roblox-expert toolkit and Studio MCP to preview and audit prepared assets in a Roblox place. The workflow supports local validation without uploading the asset.
Install
Copy the command and run it in a terminal.
npx skills add https:/ / github.com/ scenario-labs/ skills --skill scenario-roblox-assetsAfter installing, type/scenario-roblox-assetsin Claude Code to confirm it loaded.
Bundled files
References and scripts installed along with SKILL.md.
references/avatar-item-checks.mdcritique.mdexpert-notes.mdgui-paths.mdprocedures.mdsources.mdscripts/blender_roblox_prep.pyrbx_assets.pyscripts/agentkit/assets/AssetAudit.luauMeshBuild.luauPayload.luauUGCCheck.luauDetails
From the project READMERoblox Asset Pipeline and UGC
Target: Roblox Studio (October 2026 build) on macOS, driven through the scenario-roblox-expert toolkit.
Expert level here means every mesh arrives in Studio already at its size in studs, facing -Z, under its triangle budget, closed when it must be, with maps sized and split for Roblox, and with numbers that prove it. Fix in Blender, preview and audit in Studio without uploading, validate locally to green, and stop exactly where Roblox needs an uploaded asset.
If a sibling skill named here is missing from your available skills, ask the user to install it
(npx skills add scenario-labs/skills --skill <name>); unattended, proceed from tool schemas and
flag the gap.
REQUIRED BACKGROUND: scenario-roblox-expert (channels, review loop, toolkit, current-Roblox traps).
Stance (the expert delta)
- Treat an AI GLB as raw input with known defects. Image-to-3D output arrives about 0.95 units
across, front facing +Z, every material
doubleSided,metallicFactor1.0 with painted partial metalness, 2048 px JPEG maps and pinholes [added, measured on two generator outputs]. Raw meshes need remesh, pivot and scale work (CallMeDogey). - Decide size in studs in Blender, not in Studio. One Blender unit is one stud; glTF then needs no Importer scale (Creator Docs, Importer; KatsBits [00:02:56]). Props pivot at the bottom center (Common Sense Machines [00:02:39]); accessories at the mesh center (validation #173).
- Turn the front to -Z before export. glTF fronts face +Z; Roblox's LookVector is -Z. A 180 degree yaw in Blender fixes it for good [added].
- An accessory is its bounding box, and a hat is its crown. Bounds are per AccessoryType,
measured around the attachment, and rotation grows the box (KatsBits [00:10:34]). The box follows
AvatarPartScaleType: none or Classic gives theGetValidationRules()box (Hat 3 x 4 x 3), ProportionsNormal the docs row (1.87 x 2.5 x 1.87) [added, live]. Scale a hat so its crown clears the head, then check the box: filled to the Normal box by its brim, the test hat's crown was narrower than the default head [added]. - Measure watertightness; do not trust a fill operator.
bmesh.ops.holes_fillcapped 2 of 9 pinholes on the test hat, and decimation then created edges with three faces [added]. Cap each loop, repair after decimation, count boundary edges again. Never use Make Double Sided to hide holes (Creator Docs, Importer). - Preview without uploading. EditableMesh plus EditableImage rebuild the prepared mesh and its PBR maps in the place for audits, wear tests and captures [added]. That content is session-only and empty in a playtest copy [added]; ship through the Importer with Upload to Roblox off while iterating (Creator Docs; Templates [00:02:31]).
- Validate locally to green, then stop.
ValidateUGCAccessoryAsyncruns Roblox's validator inside a playtest. Fix every string about geometry, schema, materials or bounds; ownership and moderation strings need an uploaded asset and end the agent's part (Creator Docs, validation system). - Textures follow density, not the source. 256 px per 2 x 2 x 2 studs, power of two; OpenGL normals; metalness 0 or 1 unless satin by design; opaque ColorMap on accessories (Creator Docs, texture specifications and SurfaceAppearance).
Establish first
- Destination: in-experience prop, or Marketplace avatar item. Only the second must pass validation and be watertight.
- Size in studs per asset (an R15 character is about 5 to 6 studs tall), and for accessories the
AccessoryType and the
AvatarPartScaleTypeit will declare (Classic, ProportionsNormal, ProportionsSlender). - Budget class: hero prop, clutter, or accessory (4,000 triangles).
- Source: GLB from scenario-3d (preferred), FBX (needs FBX Unit Scale), or OBJ (mesh only).
- Rigged or skinned input goes to scenario-roblox-animation after import.
- Defaults when unanswered: prop, bottom pivot, longest side from the concept's scale, texture side
from
texture_side, hat crown opening 1.35 studs, no scale type (Classic box).
Procedure
Full code and recorded results: procedures.md. Setup:
import rbx_assets as ra (it finds the lead toolkit). Every snippet ran in Studio 0.743 or Blender
5.2 on 2026-10-19, except those marked not yet run; Edit and playtest captures confirmed the fit (a
covered window returns dark frames: judge on numbers then).
- Triage. Goal: know the defects. How:
ra.glb_info(path)offline. GATE: triangles, size, map sizes,doubleSided, skins and extensions written down per asset. - Fix in Blender. Goal: size, facing, pivot, budget, closed mesh, Roblox maps. How:
ra.blender_prep({**ra.PROP_PREP or ra.ACCESSORY_PREP, "input", "out_dir", "yaw": 180, "scale": ...}), with{"opening": 1.35, "band": [0.08, 0.2], "cap": [3, 4, 3]}for hats; it waits for the machine guard, runsblender -b, writes a report and four Workbench views. GATE:ra.gates(report["after"], kind, type)all pass, and the front view shows the front. - Read live rules. Goal: today's limits. How:
ra.run(studio, "return AssetAudit.rules()", kit=["AssetAudit"]). GATE:AccessoryMaxTriangles,MaxTextureSizeand the type's attachmentSizeandOffsetrecorded; the offline gates use them. - Preview and audit in Studio. Goal: the asset in the place, measured. How:
ra.stagethe packed mesh and maps, thenMeshBuild.buildandAssetAudit.prop. GATE:Sizeequals the Blender size within 0.01 stud, the triangle count matches, no audit error, the front capture shows the front. - Build the accessory. Goal: Accessory, Handle, one attachment named for the type. How:
MeshBuild.accessory, thenAssetAudit.accessory(acc, rules). GATE: zero errors;AvatarPartScaleTypeis the only warning (the Accessory Fitting Tool adds it). - Wear test. Goal: it sits right. How:
UGCCheck.rigandUGCCheck.wearin Edit, then captures front and side. GATE: a joint exists, attachment error under 0.01 stud,opening.clears(crown at least head-wide at the head top), and the brim rests on the head in the captures. - Validate locally. Goal: only upload-bound strings left. How: in a
Playtest,ra.stage(..., dm="Server")andUGCCheck.validate(acc, Enum.AccessoryType.Hat); run single-fault copies as negative controls, at most 10 calls per 60 seconds. GATE: the item returns valid with no string; every fault copy fails. - Hand off the import. Goal: the shipping path. How: the human or a computer-use agent imports
<name>_roblox.glbwith the settings in gui-paths.md; the Accessory Fitting Tool generates the final accessory. GATE: the imported MeshPart's Size equals the preview's; P7 audits it again.
Numbers
| Value | Relative to | Source |
|---|---|---|
| 20,000 triangles | One mesh in a place | Creator Docs |
| 4,000 triangles | Rigid or layered accessory | Creator Docs; live AccessoryMaxTriangles |
| 60,000 vertices, 20,000 triangles | One EditableMesh; a quad counts as 2 | EditableMesh reference |
| 256 px per 2 x 2 x 2 studs | Texture side, next power of two | Texture specifications |
| 4096 px | Place textures (since 2026-01-30) | DevForum |
| 1024 px | Avatar ColorMap, live MaxTextureSize; other avatar maps 256 | Validation system; live |
| 1024 px | EditableImage side | AssetService reference |
| Hat 3 x 4 x 3 studs | No AvatarPartScaleType or Classic; ProportionsNormal 1.87 x 2.5 x 1.87 | Live rules and validator; accessory specifications |
| 10 pieces | Disconnected components per accessory MeshPart | Validation #187 |
| 0.05 to 4.0 studs | Per-axis MeshPart size for avatar parts | Validation #176, #177 |
| 0.02 | Lowest MeshPart.Transparency for soft alpha with AlphaMode Transparency | SurfaceAppearance |
| 0 to 40 | EmissiveStrength on Marketplace items | SurfaceAppearance |
| 1.35 studs | Hat crown opening, for a 1.16-stud default R15 head | [added, wear test] |
| 0.3 stud | Hat attachment above the mesh bottom: brim a quarter of the head height below its top | [added, wear test and fit render] |
| 10 calls per 60 s | ValidateUGCAccessoryAsync rate limit | Live error string |
Quality gates
Measurable:
- Blender report: every
ra.gatesentry passes; the raw input fails what it should (negative control). - Studio:
AssetAuditreturns no error;Sizeand triangle count equal the report. - Wear: joint present, attachment error under 0.01 stud.
- Validator: no string left that is fixable without an upload.
Visual (one contact sheet, 1,600 px at most, opened and read):
- Front of each asset faces the camera from -Z; props stand on the ground; nothing black or inside out.
- The hat sits on the head from the front and the side, neither floating nor sunk past the brim.
- Textures read like the concept: details in place, no smeared atlas.
Score the result with critique.md before calling it done.
Common mistakes
| Mistake | Symptom | Fix |
|---|---|---|
| Importing the unit-sized GLB as is | Prop the size of a brick | Scale in Blender to studs; Scale Unit Studs |
| No yaw on glTF input | Lock or face on the back side | yaw: 180 before export |
| Make Double Sided to hide holes | Validation boundary-edge error; double triangles | Close holes in Blender |
Trusting holes_fill | "Not watertight" after the fix | Cap loops, repair after decimation, recount |
| Decimating in Studio's head | Over 4,000 triangles at upload | Decimate in Blender to 3,800 |
| Packed metallic-roughness used as one map | Metal everywhere or nowhere | Split G and B to grayscale maps |
| Partial metalness kept | Tinted, plastic-looking metal | Snap to 0 or 1 after reading the mask |
| ColorMap with alpha on an accessory | Validation #145, see-through spots | opaque_color |
| TextureID and SurfaceAppearance both set | Validation #146 | One of them |
| Attributes or tags left on the accessory | Validation #191 | Strip before validating |
| Pivot at the feet for an accessory | "Mesh data is not centered" | Center pivot |
| Rotating the hat inside the fitting box | Box red although the mesh looks inside | Re-center and orient in Blender |
| Sizing a hat by its brim | Head pokes through the crown | scale.opening, then opening.clears |
Massless true on the Handle | Validator: "Expected to be = false" | Leave the default |
AddAccessory from the client on your own character | No joint; the hat falls | Attach on the server; seat a local preview by CFrame |
| Hat mesh and texture built from Editable content on the server | Clients draw a small checkered cube at the Handle | Keep the joint on the server; on each client, apply a locally built mesh (ApplyMesh) and SurfaceAppearance (procedures P8) |
| Expecting an EditableMesh preview to save | Part empty after reopening | Session only; import for real |
| Expecting the Edit preview in a playtest | Copied parts keep Size but have no mesh | Stage and build in that data model |
Handoffs
- Receives from scenario-3d: GLB or FBX meshes with PBR maps (image-to-3D, remesh, retexture, rigging). Ask for a remesh run when decimation damages the silhouette.
- Receives from scenario-textures: PBR sets for one mesh, which become SurfaceAppearance here. Tileable sets go to scenario-roblox-environment as MaterialVariants.
- Receives from scenario-game-assets: the concept image that scenario-3d turns into a mesh (one centered subject, plain background, front view). Icons, sprites and UI art go to scenario-roblox-ui, decals to scenario-roblox-environment.
- Delivers to scenario-roblox-environment: MeshParts at true size with a bottom pivot, a proposed CollisionFidelity and the gate report; it places, sets final fidelity and LevelOfDetail, and lights.
- Delivers to scenario-roblox-animation: rigged or skinned imports, checked for scale and facing.
- Delivers to scenario-roblox-performance: each import batch, for its memory and batching audit
(
references/memory-and-crowds.md,PerfAudit.assets), which this skill does not repeat.
Current Roblox notes
EditableMeshandEditableImageare not creatable withInstance.new; useAssetService:CreateEditableMesh(),CreateEditableImage()andContent.fromObject. Published experiences need the Mesh and Image APIs setting.CreateSurfaceAppearanceAsynctakes EditableImage content only; maps cannot be swapped afterward.- The Avatar Joint Upgrade joins rigid accessories with
RigidConstraint, not a weld. Find attachments withFindFirstChild(name, true). GenerateMeshAsyncis scheduled for deprecation;GenerationService:GenerateModelAsyncwith schemaCar5orBody1replaces it.- Places take 4096 px textures since 2026-01-30; avatar limits come from
GetValidationRules(). - Studio MCP runs with plugin rights: in Edit it wrote
SurfaceAppearance.ColorMap,ColorMapContentandMeshPart.CollisionFidelitywithout error. Game scripts have fewer rights; test a snippet in a Script before shipping it in one. generate_mesh(Studio MCP) returns only{generationName, tag}and inserts a Model of segmented MeshParts with asset IDs and a TextureID; passsize,maxTrianglesandsegmentation.
References
- references/expert-notes.md: principles by source, disagreements, and what the live runs added.
- references/procedures.md: full code for P1 to P11 with test ids and results.
- references/avatar-item-checks.md: engine calls behind each wrapper, motion on several bodies, six-view opacity, tilt, Fitting Tool leftovers (Unknown type, TexturePack, MeshSize).
- references/critique.md: blocking and quality checks for your own output.
- references/gui-paths.md: Importer, Accessory Fitting Tool, validation plugin and Blender paths.
- references/sources.md: every doc and video with credentials and timestamps.
- scripts/rbx_assets.py: GLB triage, Blender runner with the machine guard, gates, texture sizing, payloads, kit runner.
- scripts/blender_roblox_prep.py: the headless Blender inspect and prep pass.
- scripts/agentkit/assets/:
Payload,MeshBuild,AssetAudit,UGCCheck.