Skip to main content
Glama
RobotExporter.cs8.1 kB
using UnityEngine; using System.IO; using System.Text; using System.Collections.Generic; /// <summary> /// Exporter for robot GameObjects to various 3D formats. /// Used by robotics-mcp robot_model export operation via execute_unity_method. /// /// Supports: /// - OBJ: Native Unity export (no packages required) /// - FBX: Requires Unity FBX Exporter package (com.unity.formats.fbx) /// - GLB: Requires glTF exporter package or conversion via Blender /// </summary> public class RobotExporter : MonoBehaviour { private static RobotExporter instance; void Awake() { if (instance == null) { instance = this; DontDestroyOnLoad(gameObject); } else { Destroy(gameObject); } } /// <summary> /// Export a robot GameObject to a 3D file format. /// Can be called statically via execute_unity_method. /// </summary> /// <param name="robotId">Robot identifier (used to find GameObject)</param> /// <param name="outputPath">Full path where to save the exported file</param> /// <param name="format">Export format: "obj", "fbx", or "glb"</param> /// <param name="includeAnimations">Whether to include animations (FBX/GLB only)</param> /// <returns>Export result message</returns> public static string ExportRobot(string robotId, string outputPath, string format = "obj", bool includeAnimations = true) { try { // Find the robot GameObject GameObject robotObj = GameObject.Find(robotId); if (robotObj == null) { // Try to find via VbotSpawner robotObj = VbotSpawner.GetRobot(robotId); } if (robotObj == null) { return $"ERROR: Robot '{robotId}' not found in scene"; } format = format.ToLower(); switch (format) { case "obj": return ExportToOBJ(robotObj, outputPath); case "fbx": return ExportToFBX(robotObj, outputPath, includeAnimations); case "glb": case "gltf": return ExportToGLB(robotObj, outputPath, includeAnimations); default: return $"ERROR: Unsupported format '{format}'. Supported: obj, fbx, glb"; } } catch (System.Exception e) { return $"ERROR: Export failed: {e.Message}"; } } /// <summary> /// Export GameObject to OBJ format (native Unity, no packages required). /// </summary> private static string ExportToOBJ(GameObject obj, string outputPath) { try { StringBuilder sb = new StringBuilder(); int vertexOffset = 0; // Write header sb.AppendLine("# OBJ file exported from Unity"); sb.AppendLine($"# Robot: {obj.name}"); sb.AppendLine(); // Collect all mesh filters in hierarchy MeshFilter[] meshFilters = obj.GetComponentsInChildren<MeshFilter>(); foreach (MeshFilter mf in meshFilters) { if (mf.sharedMesh == null) continue; Mesh mesh = mf.sharedMesh; Transform transform = mf.transform; // Write object name sb.AppendLine($"o {mf.gameObject.name}"); // Write vertices (apply transform) foreach (Vector3 vertex in mesh.vertices) { Vector3 worldVertex = transform.TransformPoint(vertex); sb.AppendLine($"v {worldVertex.x} {worldVertex.y} {worldVertex.z}"); } // Write normals foreach (Vector3 normal in mesh.normals) { Vector3 worldNormal = transform.TransformDirection(normal).normalized; sb.AppendLine($"vn {worldNormal.x} {worldNormal.y} {worldNormal.z}"); } // Write UVs foreach (Vector2 uv in mesh.uv) { sb.AppendLine($"vt {uv.x} {uv.y}"); } // Write faces (with vertex offset) int[] triangles = mesh.triangles; for (int i = 0; i < triangles.Length; i += 3) { int v1 = triangles[i] + 1 + vertexOffset; int v2 = triangles[i + 1] + 1 + vertexOffset; int v3 = triangles[i + 2] + 1 + vertexOffset; if (mesh.uv.Length > 0 && mesh.normals.Length > 0) { sb.AppendLine($"f {v1}/{v1}/{v1} {v2}/{v2}/{v2} {v3}/{v3}/{v3}"); } else if (mesh.normals.Length > 0) { sb.AppendLine($"f {v1}//{v1} {v2}//{v2} {v3}//{v3}"); } else { sb.AppendLine($"f {v1} {v2} {v3}"); } } vertexOffset += mesh.vertexCount; sb.AppendLine(); } // Write to file string directory = Path.GetDirectoryName(outputPath); if (!string.IsNullOrEmpty(directory) && !Directory.Exists(directory)) { Directory.CreateDirectory(directory); } File.WriteAllText(outputPath, sb.ToString()); return $"SUCCESS: Exported {obj.name} to OBJ: {outputPath}"; } catch (System.Exception e) { return $"ERROR: OBJ export failed: {e.Message}"; } } /// <summary> /// Export GameObject to FBX format (requires Unity FBX Exporter package). /// </summary> private static string ExportToFBX(GameObject obj, string outputPath, bool includeAnimations) { try { // Check if FBX Exporter package is available #if UNITY_EDITOR && UNITY_FORMATS_FBX // Use Unity FBX Exporter package UnityEditor.Formats.Fbx.Exporter.ModelExporter.ExportObject(outputPath, obj); return $"SUCCESS: Exported {obj.name} to FBX: {outputPath}"; #else // FBX Exporter package not available - suggest OBJ or Blender conversion return $"ERROR: FBX export requires Unity FBX Exporter package (com.unity.formats.fbx). " + $"Please install it via Package Manager, or export as OBJ and convert via Blender."; #endif } catch (System.Exception e) { return $"ERROR: FBX export failed: {e.Message}"; } } /// <summary> /// Export GameObject to GLB/glTF format (requires glTF exporter or conversion via Blender). /// </summary> private static string ExportToGLB(GameObject obj, string outputPath, bool includeAnimations) { try { // GLB export requires a package or conversion // For now, export as OBJ and suggest Blender conversion string objPath = outputPath.Replace(".glb", ".obj").Replace(".gltf", ".obj"); string result = ExportToOBJ(obj, objPath); if (result.StartsWith("SUCCESS")) { return $"SUCCESS: Exported {obj.name} to OBJ (GLB not directly supported): {objPath}. " + $"Use Blender to convert OBJ to GLB, or install a glTF exporter package."; } else { return result; } } catch (System.Exception e) { return $"ERROR: GLB export failed: {e.Message}"; } } }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/sandraschi/robotics-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server