# ProBuilder Workflow Guide Patterns and best practices for AI-driven ProBuilder mesh editing through MCP for Unity. ## Availability ProBuilder is an **optional** Unity package (`com.unity.probuilder`). Check `mcpforunity://project/info` or call `manage_probuilder(action="ping")` to verify it's installed before using any ProBuilder tools. If available, **prefer ProBuilder over primitive GameObjects** for any geometry that needs editing, multi-material faces, or non-trivial shapes. ## Core Workflow: Always Get Info First Before any mesh edit, call `get_mesh_info` with `include='faces'` to understand the geometry: ```python # Step 1: Get face info with directions result = manage_probuilder(action="get_mesh_info", target="MyCube", properties={"include": "faces"}) # Response includes per-face: # index: 0, normal: [0, 1, 0], center: [0, 0.5, 0], direction: "top" # index: 1, normal: [0, -1, 0], center: [0, -0.5, 0], direction: "bottom" # index: 2, normal: [0, 0, 1], center: [0, 0, 0.5], direction: "front" # ... # Step 2: Use the direction labels to pick faces # Want to extrude the top? Find the face with direction="top" manage_probuilder(action="extrude_faces", target="MyCube", properties={"faceIndices": [0], "distance": 1.5}) ``` ### Include Parameter | Value | Returns | Use When | |-------|---------|----------| | `"summary"` | Counts, bounds, materials | Quick check / validation | | `"faces"` | + normals, centers, directions | Selecting faces for editing | | `"edges"` | + edge vertex pairs with world positions (max 200) | Edge-based operations | | `"all"` | Everything | Full mesh analysis | ## Shape Creation ### All 12 Shape Types ```python # Basic shapes manage_probuilder(action="create_shape", properties={"shape_type": "Cube", "name": "MyCube"}) manage_probuilder(action="create_shape", properties={"shape_type": "Sphere", "name": "MySphere"}) manage_probuilder(action="create_shape", properties={"shape_type": "Cylinder", "name": "MyCyl"}) manage_probuilder(action="create_shape", properties={"shape_type": "Plane", "name": "MyPlane"}) manage_probuilder(action="create_shape", properties={"shape_type": "Cone", "name": "MyCone"}) manage_probuilder(action="create_shape", properties={"shape_type": "Prism", "name": "MyPrism"}) # Parametric shapes manage_probuilder(action="create_shape", properties={ "shape_type": "Torus", "name": "MyTorus", "rows": 16, "columns": 24, "innerRadius": 0.5, "outerRadius": 1.0 }) manage_probuilder(action="create_shape", properties={ "shape_type": "Pipe", "name": "MyPipe", "radius": 1.0, "height": 2.0, "thickness": 0.2 }) manage_probuilder(action="create_shape", properties={ "shape_type": "Arch", "name": "MyArch", "radius": 2.0, "angle": 180, "segments": 12 }) # Architectural shapes manage_probuilder(action="create_shape", properties={ "shape_type": "Stair", "name": "MyStairs", "steps": 10 }) manage_probuilder(action="create_shape", properties={ "shape_type": "CurvedStair", "name": "Spiral", "steps": 12 }) manage_probuilder(action="create_shape", properties={ "shape_type": "Door", "name": "MyDoor" }) # Custom polygon manage_probuilder(action="create_poly_shape", properties={ "points": [[0,0,0], [5,0,0], [5,0,5], [2.5,0,7], [0,0,5]], "extrudeHeight": 3.0, "name": "Pentagon" }) ``` ## Common Editing Operations ### Extrude a Roof ```python # 1. Create a building base manage_probuilder(action="create_shape", properties={ "shape_type": "Cube", "name": "Building", "size": [4, 3, 6] }) # 2. Find the top face info = manage_probuilder(action="get_mesh_info", target="Building", properties={"include": "faces"}) # Find face with direction="top" -> e.g. index 2 # 3. Extrude upward for a flat roof extension manage_probuilder(action="extrude_faces", target="Building", properties={"faceIndices": [2], "distance": 0.5}) ``` ### Cut a Hole (Delete Faces) ```python # 1. Get face info info = manage_probuilder(action="get_mesh_info", target="Wall", properties={"include": "faces"}) # Find the face with direction="front" -> e.g. index 4 # 2. Subdivide to create more faces manage_probuilder(action="subdivide", target="Wall", properties={"faceIndices": [4]}) # 3. Get updated face info (indices changed after subdivide!) info = manage_probuilder(action="get_mesh_info", target="Wall", properties={"include": "faces"}) # 4. Delete the center face(s) for the hole manage_probuilder(action="delete_faces", target="Wall", properties={"faceIndices": [6]}) ``` ### Bevel Edges ```python # Get edge info info = manage_probuilder(action="get_mesh_info", target="MyCube", properties={"include": "edges"}) # Bevel specific edges manage_probuilder(action="bevel_edges", target="MyCube", properties={"edgeIndices": [0, 1, 2, 3], "amount": 0.1}) ``` ### Detach Faces to New Object ```python # Detach and keep original (default) manage_probuilder(action="detach_faces", target="MyCube", properties={"faceIndices": [0, 1], "deleteSourceFaces": False}) # Detach and remove from source manage_probuilder(action="detach_faces", target="MyCube", properties={"faceIndices": [0, 1], "deleteSourceFaces": True}) ``` ### Select Faces by Direction ```python # Select all upward-facing faces manage_probuilder(action="select_faces", target="MyMesh", properties={"direction": "up", "tolerance": 0.7}) # Grow selection from a seed face manage_probuilder(action="select_faces", target="MyMesh", properties={"growFrom": [0], "growAngle": 45}) ``` ### Double-Sided Geometry ```python # Create inside faces for a room (duplicate and flip normals) manage_probuilder(action="duplicate_and_flip", target="Room", properties={"faceIndices": [0, 1, 2, 3, 4, 5]}) ``` ### Create Polygon from Existing Vertices ```python # Connect existing vertices into a new face (auto-finds winding order) manage_probuilder(action="create_polygon", target="MyMesh", properties={"vertexIndices": [0, 3, 7, 4]}) ``` ## Vertex Operations ```python # Move vertices by offset manage_probuilder(action="move_vertices", target="MyCube", properties={"vertexIndices": [0, 1, 2, 3], "offset": [0, 1, 0]}) # Weld nearby vertices (proximity-based merge) manage_probuilder(action="weld_vertices", target="MyCube", properties={"vertexIndices": [0, 1, 2, 3], "radius": 0.1}) # Insert vertex on an edge manage_probuilder(action="insert_vertex", target="MyCube", properties={"edge": {"a": 0, "b": 1}, "point": [0.5, 0, 0]}) # Add evenly-spaced points along edges manage_probuilder(action="append_vertices_to_edge", target="MyCube", properties={"edgeIndices": [0, 1], "count": 3}) ``` ## Smoothing Workflow ### Auto-Smooth (Recommended Default) ```python # Apply auto-smoothing with default 30 degree threshold manage_probuilder(action="auto_smooth", target="MyMesh", properties={"angleThreshold": 30}) ``` - **Low angle (15-25)**: More hard edges, faceted look - **Medium angle (30-45)**: Good default, smooth curves + sharp corners - **High angle (60-80)**: Very smooth, only sharpest edges remain hard ### Manual Smoothing Groups ```python # Set specific faces to smooth group 1 manage_probuilder(action="set_smoothing", target="MyMesh", properties={"faceIndices": [0, 1, 2], "smoothingGroup": 1}) # Set other faces to hard edges (group 0) manage_probuilder(action="set_smoothing", target="MyMesh", properties={"faceIndices": [3, 4, 5], "smoothingGroup": 0}) ``` ## Mesh Cleanup Pattern After editing, always clean up: ```python # 1. Center the pivot (important after extrusions that shift geometry) manage_probuilder(action="center_pivot", target="MyMesh") # 2. Optionally freeze transform if you moved/rotated the object manage_probuilder(action="freeze_transform", target="MyMesh") # 3. Validate mesh health result = manage_probuilder(action="validate_mesh", target="MyMesh") # Check result.data.healthy -- if false, repair # 4. Auto-repair if needed manage_probuilder(action="repair_mesh", target="MyMesh") ``` ## Building Complex Objects with ProBuilder When ProBuilder is available, prefer it over primitive GameObjects for complex geometry. ProBuilder lets you create, edit, and combine shapes into detailed objects without external 3D tools. ### Example: Simple House ```python # 1. Create base building manage_probuilder(action="create_shape", properties={ "shape_type": "Cube", "name": "House", "width": 6, "height": 3, "depth": 8 }) # 2. Get face info to find the top face info = manage_probuilder(action="get_mesh_info", target="House", properties={"include": "faces"}) # Find direction="top" -> e.g. index 2 # 3. Extrude the top face to create a flat raised section manage_probuilder(action="extrude_faces", target="House", properties={"faceIndices": [2], "distance": 0.3}) # 4. Re-query faces, then move top vertices inward to form a ridge info = manage_probuilder(action="get_mesh_info", target="House", properties={"include": "faces"}) # Find the new top face after extrude, get its vertex indices # Move them to form a peaked roof shape manage_probuilder(action="move_vertices", target="House", properties={"vertexIndices": [0, 1, 2, 3], "offset": [0, 2, 0]}) # 5. Cut a doorway: subdivide front face, delete center sub-face info = manage_probuilder(action="get_mesh_info", target="House", properties={"include": "faces"}) # Find direction="front", subdivide it manage_probuilder(action="subdivide", target="House", properties={"faceIndices": [4]}) # Re-query, find bottom-center face, delete it info = manage_probuilder(action="get_mesh_info", target="House", properties={"include": "faces"}) manage_probuilder(action="delete_faces", target="House", properties={"faceIndices": [12]}) # 6. Add a door frame with arch manage_probuilder(action="create_shape", properties={ "shape_type": "Door", "name": "Doorway", "position": [0, 0, 4], "width": 1.5, "height": 2.5 }) # 7. Add stairs to the door manage_probuilder(action="create_shape", properties={ "shape_type": "Stair", "name": "FrontSteps", "position": [0, 0, 5], "steps": 3, "width": 2 }) # 8. Smooth organic parts, keep architectural edges sharp manage_probuilder(action="auto_smooth", target="House", properties={"angleThreshold": 30}) # 9. Assign materials per face manage_probuilder(action="set_face_material", target="House", properties={"faceIndices": [0, 1, 2, 3], "materialPath": "Assets/Materials/Brick.mat"}) manage_probuilder(action="set_face_material", target="House", properties={"faceIndices": [4, 5], "materialPath": "Assets/Materials/Roof.mat"}) # 10. Cleanup manage_probuilder(action="center_pivot", target="House") manage_probuilder(action="validate_mesh", target="House") ``` ### Example: Pillared Corridor (Batch) ```python # Create multiple columns efficiently batch_execute(commands=[ {"tool": "manage_probuilder", "params": { "action": "create_shape", "properties": {"shape_type": "Cylinder", "name": f"Pillar_{i}", "radius": 0.3, "height": 4, "segments": 12, "position": [i * 3, 0, 0]} }} for i in range(6) ] + [ # Floor {"tool": "manage_probuilder", "params": { "action": "create_shape", "properties": {"shape_type": "Plane", "name": "Floor", "width": 18, "height": 6, "position": [7.5, 0, 0]} }}, # Ceiling {"tool": "manage_probuilder", "params": { "action": "create_shape", "properties": {"shape_type": "Plane", "name": "Ceiling", "width": 18, "height": 6, "position": [7.5, 4, 0]} }}, ]) # Bevel all pillar tops for decoration for i in range(6): info = manage_probuilder(action="get_mesh_info", target=f"Pillar_{i}", properties={"include": "edges"}) # Find top ring edges, bevel them manage_probuilder(action="bevel_edges", target=f"Pillar_{i}", properties={"edgeIndices": [0, 1, 2, 3], "amount": 0.05}) # Smooth the pillars for i in range(6): manage_probuilder(action="auto_smooth", target=f"Pillar_{i}", properties={"angleThreshold": 45}) ``` ### Example: Custom L-Shaped Room ```python # Use polygon shape for non-rectangular footprint manage_probuilder(action="create_poly_shape", properties={ "points": [ [0, 0, 0], [10, 0, 0], [10, 0, 6], [4, 0, 6], [4, 0, 10], [0, 0, 10] ], "extrudeHeight": 3.0, "name": "LRoom" }) # Create inside faces for the room interior info = manage_probuilder(action="get_mesh_info", target="LRoom", properties={"include": "faces"}) # Duplicate and flip all faces to make interior visible all_faces = list(range(info["data"]["faceCount"])) manage_probuilder(action="duplicate_and_flip", target="LRoom", properties={"faceIndices": all_faces}) # Cut a window: subdivide a wall face, delete center # (follow the get_mesh_info -> subdivide -> get_mesh_info -> delete pattern) ``` ### Example: Torus Knot / Decorative Ring ```python # Create a torus manage_probuilder(action="create_shape", properties={ "shape_type": "Torus", "name": "Ring", "innerRadius": 0.3, "outerRadius": 2.0, "rows": 24, "columns": 32 }) # Smooth it for organic look manage_probuilder(action="auto_smooth", target="Ring", properties={"angleThreshold": 60}) # Assign metallic material manage_probuilder(action="set_face_material", target="Ring", properties={"faceIndices": [], "materialPath": "Assets/Materials/Gold.mat"}) # Note: empty faceIndices = all faces ``` ## Batch Patterns Use `batch_execute` for multi-step workflows to reduce round-trips: ```python batch_execute(commands=[ {"tool": "manage_probuilder", "params": { "action": "create_shape", "properties": {"shape_type": "Cube", "name": "Column1", "position": [0, 0, 0]} }}, {"tool": "manage_probuilder", "params": { "action": "create_shape", "properties": {"shape_type": "Cube", "name": "Column2", "position": [5, 0, 0]} }}, {"tool": "manage_probuilder", "params": { "action": "create_shape", "properties": {"shape_type": "Cube", "name": "Column3", "position": [10, 0, 0]} }}, ]) ``` ## Known Limitations ### Not Yet Working These actions exist in the API but have known bugs that prevent them from working correctly: | Action | Issue | Workaround | |--------|-------|------------| | `set_pivot` | Vertex positions don't persist through `ToMesh()`/`RefreshMesh()`. The `positions` property setter is overwritten when ProBuilder rebuilds the mesh. Needs `SetVertices(IList)` or direct `m_Positions` field access. | Use `center_pivot` instead, or position objects via Transform. | | `convert_to_probuilder` | `MeshImporter` constructor throws internally. May need ProBuilder's editor-only `ProBuilderize` API instead of runtime `MeshImporter`. | Create shapes natively with `create_shape` or `create_poly_shape` instead of converting existing meshes. | ### General Limitations - Face indices are **not stable** across edits -- always re-query `get_mesh_info` after any modification - Edge data is capped at **200 edges** in `get_mesh_info` results - Face data is capped at **100 faces** in `get_mesh_info` results - `subdivide` uses `ConnectElements.Connect` internally (ProBuilder has no public `Subdivide` API), which connects face midpoints rather than traditional quad subdivision ## Key Rules 1. **Always get_mesh_info before editing** -- face indices are not stable across edits 2. **Re-query after modifications** -- subdivide, extrude, delete all change face indices 3. **Use direction labels** -- don't guess face indices, use the direction field 4. **Cleanup after editing** -- center_pivot + validate is good practice 5. **Auto-smooth for organic shapes** -- 30 degrees is a good default 6. **Prefer ProBuilder over primitives** -- when the package is available and you need editable geometry 7. **Use batch_execute** -- for creating multiple shapes or repetitive operations 8. **Screenshot to verify** -- use `manage_camera(action="screenshot", include_image=True)` to check visual results after complex edits