using System.Collections.Generic; using System.IO; using UnityEngine; #if UNITY_EDITOR using UnityEditor; #endif namespace URDF { /// /// Utility class for resolving URDF package:// URIs to Unity asset paths /// public static class URDFMeshResolver { /// /// Resolve a URDF mesh filename (package:// URI) to a Unity asset path /// /// URDF filename like "package://Little_Sophia_Face/meshes/Little_Sophia_Torso_.obj" /// Path to the URDF file for relative resolution /// Unity asset path relative to Assets folder, or null if not found public static string ResolveMeshPath(string urdfFilename, string urdfFilePath) { if (string.IsNullOrEmpty(urdfFilename)) { Debug.LogWarning("URDF mesh filename is null or empty"); return null; } // Handle package:// URIs if (urdfFilename.StartsWith("package://")) { // Remove package:// prefix string relativePath = urdfFilename.Substring("package://".Length); // Extract just the filename from the package path string meshFileName = Path.GetFileName(relativePath); string originalExtension = Path.GetExtension(meshFileName).ToLower(); string fileNameWithoutExt = Path.GetFileNameWithoutExtension(meshFileName); Debug.Log($"Resolving mesh: {urdfFilename} -> looking for {meshFileName} (extension: {originalExtension})"); // Build list of extensions to try: first the one specified in URDF, then fallbacks List extensionsToTry = new List(); if (!string.IsNullOrEmpty(originalExtension)) { extensionsToTry.Add(originalExtension); // Try the specified extension first } // Add fallback extensions (excluding the one we already added) string[] fallbackExtensions = { ".obj", ".glb", ".fbx" }; foreach (string ext in fallbackExtensions) { if (ext != originalExtension) { extensionsToTry.Add(ext); } } // Try direct resolution first: robots/LittleSophia/meshes/filename // This is the most reliable method foreach (string ext in extensionsToTry) { string testFileName = fileNameWithoutExt + ext; string directPath = $"robots/LittleSophia/meshes/{testFileName}"; #if UNITY_EDITOR // Normalize path separators for Unity (always use forward slashes) directPath = directPath.Replace('\\', '/'); string fullPath = Path.Combine(Application.dataPath, directPath); fullPath = fullPath.Replace('/', Path.DirectorySeparatorChar); if (File.Exists(fullPath)) { Debug.Log($"Found mesh at: {directPath} (full path: {fullPath})"); return directPath; } else { Debug.Log($"Checking for mesh: {fullPath} - File does not exist"); } #endif } // Fallback: Try to resolve relative to the URDF file location string urdfDir = Path.GetDirectoryName(urdfFilePath); if (!string.IsNullOrEmpty(urdfDir)) { string meshesDir = Path.Combine(urdfDir, "..", "meshes"); meshesDir = Path.GetFullPath(meshesDir); foreach (string ext in extensionsToTry) { string testFileName = fileNameWithoutExt + ext; string meshPath = Path.Combine(meshesDir, testFileName); if (File.Exists(meshPath)) { // Convert to Unity asset path (relative to Assets folder) string assetsPath = "Assets" + meshPath.Replace(Application.dataPath, "").Replace('\\', '/'); Debug.Log($"Found mesh at: {assetsPath}"); return assetsPath; } } } } // Handle relative paths else if (!Path.IsPathRooted(urdfFilename)) { string urdfDir = Path.GetDirectoryName(urdfFilePath); string meshPath = Path.Combine(urdfDir, urdfFilename); meshPath = Path.GetFullPath(meshPath); if (File.Exists(meshPath)) { string assetsPath = "Assets" + meshPath.Replace(Application.dataPath, "").Replace('\\', '/'); return assetsPath; } } // Handle absolute paths else { if (File.Exists(urdfFilename)) { string assetsPath = "Assets" + urdfFilename.Replace(Application.dataPath, "").Replace('\\', '/'); return assetsPath; } } Debug.LogWarning($"Could not resolve mesh path: {urdfFilename} (URDF file: {urdfFilePath})"); return null; } /// /// Load a mesh from a Unity asset path /// public static Mesh LoadMesh(string assetPath) { if (string.IsNullOrEmpty(assetPath)) return null; #if UNITY_EDITOR // Try loading as a mesh asset Mesh mesh = AssetDatabase.LoadAssetAtPath(assetPath); if (mesh != null) return mesh; // Try loading as a model (FBX, OBJ, etc.) - Unity imports these as GameObjects GameObject prefab = AssetDatabase.LoadAssetAtPath(assetPath); if (prefab != null) { // Extract mesh from the prefab MeshFilter meshFilter = prefab.GetComponentInChildren(); if (meshFilter != null && meshFilter.sharedMesh != null) { return meshFilter.sharedMesh; } } #endif Debug.LogWarning($"Could not load mesh from: {assetPath}"); return null; } } }