diff --git a/.github/workflows/jira-link.yaml b/.github/workflows/jira-link.yaml new file mode 100644 index 00000000..18240fb7 --- /dev/null +++ b/.github/workflows/jira-link.yaml @@ -0,0 +1,22 @@ +name: jira-link + +on: + pull_request: + types: [opened, edited, reopened, synchronize] + +jobs: + jira-link: + runs-on: ubuntu-20.04 + steps: + - name: check pull request title and source branch name + run: | + echo "Checking pull request with title ${{ github.event.pull_request.title }} from source branch ${{ github.event.pull_request.head.ref }}" + if ! [[ "${{ github.event.pull_request.title }}" =~ ^AIRO-[0-9]+[[:space:]].*$ ]] && ! [[ "${{ github.event.pull_request.head.ref }}" =~ ^AIRO-[0-9]+.*$ ]] + then + echo -e "Please make sure one of the following is true:\n \ + 1. the pull request title starts with 'AIRO-xxxx ', e.g. 'AIRO-1024 My Pull Request'\n \ + 2. the source branch starts with 'AIRO-xxx', e.g. 'AIRO-1024-my-branch'" + exit 1 + else + echo "Completed checking" + fi diff --git a/.github/workflows/stale.yaml b/.github/workflows/stale.yaml new file mode 100644 index 00000000..a77c69e5 --- /dev/null +++ b/.github/workflows/stale.yaml @@ -0,0 +1,27 @@ +name: 'Stale issue handler' +on: + workflow_dispatch: + schedule: + - cron: '0 17 * * *' # 17:00 UTC; 10:00 PDT + +permissions: + issues: write + +jobs: + stale: + runs-on: ubuntu-latest + steps: + - uses: actions/stale@v4.0.0 + id: stale + with: + stale-issue-label: 'stale' + stale-issue-message: 'This issue has been marked stale because it has been open for 14 days with no activity. Please remove the stale label or comment on this issue, or the issue will be automatically closed in the next 14 days.' + days-before-stale: 14 + days-before-pr-stale: -1 + days-before-close: 14 + days-before-pr-close: -1 + exempt-issue-labels: 'blocked,must,should,keep,pinned,work-in-progress,request,announcement' + close-issue-message: 'This issue has been marked stale for 14 days and will now be closed. If this issue is still valid, please ping a maintainer.' + - name: Print outputs + run: echo ${{ join(steps.stale.outputs.*, ',') }} + \ No newline at end of file diff --git a/.gitignore b/.gitignore index ef68f9fc..d7110478 100644 --- a/.gitignore +++ b/.gitignore @@ -24,6 +24,9 @@ # Autogenerated Jetbrains Rider plugin /[Aa]ssets/Plugins/Editor/JetBrains* +# Rider cache directory +.idea + # Visual Studio cache directory .vs/ @@ -73,3 +76,7 @@ crashlytics-build.properties # .DS_Store **/.DS_Store + +# Sonarqube +.scannerwork/** +.sonarqube/** \ No newline at end of file diff --git a/.yamato/sonar.yml b/.yamato/sonar.yml new file mode 100644 index 00000000..bb5581fb --- /dev/null +++ b/.yamato/sonar.yml @@ -0,0 +1,33 @@ +name: Sonarqube Scan +agent: + type: Unity::metal::macmini + image: package-ci/mac + flavor: m1.mac +variables: + PROJECT_PATH: TestUrdfImporter + SONARQUBE_PROJECT_KEY: ai-robotics-urdf-importer + SONARQUBE_PROJECT_BASE_DIR: /Users/bokken/build/output/Unity-Technologies/URDF-Importer/com.unity.robotics.urdf-importer + MSBUILD_SLN_PATH: ./TestUrdfImporter/TestUrdfImporter.sln + UNITY_VERSION: 2020.3.11f1 +commands: + - npm install upm-ci-utils@stable -g --registry https://artifactory.prd.it.unity3d.com/artifactory/api/npm/upm-npm + - unity-downloader-cli -u $UNITY_VERSION -c Editor + - brew install mono corretto + - curl https://github.com/SonarSource/sonar-scanner-msbuild/releases/download/5.2.1.31210/sonar-scanner-msbuild-5.2.1.31210-net46.zip -o sonar-scanner-msbuild-net46.zip -L + - unzip sonar-scanner-msbuild-net46.zip -d ~ + - chmod a+x ~/sonar-scanner-4.6.1.2450/bin/sonar-scanner + - .Editor/Unity.app/Contents/MacOS/Unity -projectPath $PROJECT_PATH -batchmode -quit -nographics -logFile - -executeMethod "UnityEditor.SyncVS.SyncSolution" + - command: | + cd $PROJECT_PATH + for file in *.csproj; do sed -i.backup "s/^[[:blank:]]*false<\/ReferenceOutputAssembly>/true<\/ReferenceOutputAssembly>/g" $file; rm $file.backup; done + cd ../ + - mono ~/SonarScanner.MSBuild.exe begin /k:$SONARQUBE_PROJECT_KEY /d:sonar.host.url=$SONARQUBE_ENDPOINT_URL_PRD /d:sonar.login=$SONARQUBE_TOKEN_PRD /d:sonar.projectBaseDir=$SONARQUBE_PROJECT_BASE_DIR + - msbuild $MSBUILD_SLN_PATH + - mono ~/SonarScanner.MSBuild.exe end /d:sonar.login=$SONARQUBE_TOKEN_PRD +triggers: + cancel_old_ci: true + expression: | + ((pull_request.target eq "main" OR pull_request.target eq "dev") + AND NOT pull_request.push.changes.all match "**/*.md") OR + (push.branch eq "main" OR push.branch eq "dev") + \ No newline at end of file diff --git a/.yamato/yamato-config.yml b/.yamato/yamato-config.yml index cbb6fcb7..9d960d9e 100644 --- a/.yamato/yamato-config.yml +++ b/.yamato/yamato-config.yml @@ -1,9 +1,10 @@ name: URDF Importer Unit Tests agent: type: Unity::VM - image: robotics/ci-ubuntu20:latest + image: robotics/ci-ubuntu20:v0.1.0-795910 flavor: i1.large variables: + COVERAGE_EXPECTED: 35 PATH: /root/.local/bin:/home/bokken/bin:/home/bokken/.local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin:/sbin:/home/bokken/.npm-global/bin commands: - git submodule update --init --recursive @@ -15,14 +16,15 @@ commands: - command: | linecoverage=$(cat test-results/Report/Summary.xml | grep Linecoverage | grep -Eo '[+-]?[0-9]+([.][0-9]+)?') echo "Line coverage: $linecoverage%" - if (( $(echo "$linecoverage < 20" | bc -l) )); then exit 1; fi + if (( $(echo "$linecoverage < $COVERAGE_EXPECTED" | bc -l) )) + then echo "ERROR: Code coverage is under threshold of $COVERAGE_EXPECTED%" && exit 1 + fi triggers: cancel_old_ci: true expression: | - (pull_request.target eq "main" AND - NOT pull_request.push.changes.all match "**/*.md") OR - (pull_request.target eq "dev" AND - NOT pull_request.push.changes.all match "**/*.md") + ((pull_request.target eq "main" OR pull_request.target eq "dev") + AND NOT pull_request.push.changes.all match "**/*.md") OR + (push.branch eq "main" OR push.branch eq "dev") artifacts: logs: paths: diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 761e00b2..99656aec 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -30,24 +30,30 @@ does the following: - Has corresponding changes to documentation, unit tests and sample environments (if applicable) - Contains a summary of the tests performed to validate your changes +- Updates the [Changelog](com.unity.robotics.urdf-importer/CHANGELOG.md) and describes changes in the Unreleased section - Links to issue numbers that the PR resolves (if any) +## Workflow + +This project is provided as a package to be imported into a Unity project. In order to avoid common issues regarding version control and unmanaged files, the suggested workflow for contributing to the package is as follows: + +1. Clone this repository to a known location on your local machine (e.g. Documents). +2. Git checkout the `dev` branch to ensure you have the latest staged changes. +3. In a development Unity project (e.g. a new project created from the Unity Hub of the minimum Unity [version](README.md) or above), open the `Window > Package Manager`. +4. Click the `+` in the top-left of the Package Manager window and select `Add package from disk...`. +5. Navigate to the cloned repository, and select the `com.unity./package.json` file. Click `Open`. +6. The local version of the package is now added to your Unity project! Changes made to your cloned directory will update in your Unity project. + ## Code style -All Python code should follow the [PEP 8 style guidelines](https://pep8.org/). - -All C# code should follow the [Microsoft C# Coding Conventions](https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/inside-a-program/coding-conventions). -Additionally, the [Unity Coding package](https://docs.unity3d.com/Packages/com.unity.coding@0.1/manual/index.html) -can be used to format, encode, and lint your code according to the standard Unity -development conventions. Be aware that these Unity conventions will supersede the -Microsoft C# Coding Conventions where applicable. +All C# code should follow the [Microsoft C# Coding Conventions](https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/inside-a-program/coding-conventions). (Code may be reformatted to Unity coding standards where applicable.) -Please note that even if the code you are changing does not adhere to these guidelines, -we expect your submissions to follow these conventions. +Please note that even if the code you are changing does not follow these conventions, +we expect your submissions to do so. ## Contributor License Agreements diff --git a/README.md b/README.md index 795228ac..d36b5a57 100644 --- a/README.md +++ b/README.md @@ -4,11 +4,19 @@ [![License](https://img.shields.io/badge/license-Apache--2.0-green.svg)](LICENSE.md) ![Unity](https://img.shields.io/badge/unity-2020.2+-brightgreen) +--- + +We're currently working on lots of things! Please take a short moment fill out our [survey](https://unitysoftware.co1.qualtrics.com/jfe/form/SV_0ojVkDVW0nNrHkW) to help us identify what products and packages to build next. + +--- + +## Introduction + URDF Importer allows you to import a robot defined in [URDF](http://wiki.ros.org/urdf/XML) format in a Unity scene. URDF defines the geometry, visual meshes, kinematic and dynamic attributes of a Robot. Importer parses a URDF file and imports it into Unity using PhyX 4.0 articulation bodies. -# Using the Package +## Using the Package -## Adding the URDF package +### Adding the URDF package 1. Open the Package Manager from Unity Menu. Click `Window -> Package Manager`. A new package manager window will appear. @@ -16,11 +24,11 @@ URDF Importer allows you to import a robot defined in [URDF](http://wiki.ros.org -3. Enter the git URL for the URDF Importer with the latest version tag (currently v0.5.0) `https://github.com/Unity-Technologies/URDF-Importer.git?path=/com.unity.robotics.urdf-importer#v0.5.0` in the text box and press `Enter`. +3. Enter the git URL for the URDF Importer with the latest version tag (currently v0.5.2) `https://github.com/Unity-Technologies/URDF-Importer.git?path=/com.unity.robotics.urdf-importer#v0.5.2` in the text box and press `Enter`. 4. Click `Import URDF`. -## Importing the robot using URDF file +### Importing the robot using URDF file 1. Copy the URDF and the associated files in the assets folder in the Project window. Make sure the [location](https://github.com/Unity-Technologies/Unity-Robotics-Hub/blob/main/tutorials/urdf_importer/urdf_appendix.md#file-hierarchy) of the mesh files is correct. @@ -35,7 +43,7 @@ URDF Importer allows you to import a robot defined in [URDF](http://wiki.ros.org 4. Click `Import URDF` -# Tutorials +## Tutorials Instructions for using URDF importer can be found [here](https://github.com/Unity-Technologies/Unity-Robotics-Hub/blob/main/tutorials/urdf_importer/urdf_tutorial.md). diff --git a/com.unity.robotics.urdf-importer/CHANGELOG.md b/com.unity.robotics.urdf-importer/CHANGELOG.md index be341237..e5fbf33e 100644 --- a/com.unity.robotics.urdf-importer/CHANGELOG.md +++ b/com.unity.robotics.urdf-importer/CHANGELOG.md @@ -20,6 +20,38 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) a ### Fixed + +## [0.5.2-preview] - 2022-02-01 + +Added Sonarqube scanner + +### Fixed +Fixed inability to read relative file paths + +Correct Axis change issues in URDF Importer + + +## [0.5.1-preview] - 2021-10-04 +Fixed bug with multiple references to the same mesh during import + +Add the [Close Stale Issues](https://github.com/marketplace/actions/close-stale-issues) action + +### Upgrade Notes + +### Known Issues + +### Added + +Start supporting file:// type URI. + +### Changed + +### Deprecated + +### Removed + +### Fixed + ## [0.5.0-preview] - 2021-07-15 ### Upgrade Notes diff --git a/com.unity.robotics.urdf-importer/Editor/CustomEditors/UrdfRobotEditor.cs b/com.unity.robotics.urdf-importer/Editor/CustomEditors/UrdfRobotEditor.cs index 87168d19..0d522943 100644 --- a/com.unity.robotics.urdf-importer/Editor/CustomEditors/UrdfRobotEditor.cs +++ b/com.unity.robotics.urdf-importer/Editor/CustomEditors/UrdfRobotEditor.cs @@ -10,7 +10,7 @@ You may obtain a copy of the License at WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. -*/ +*/ using System.IO; using UnityEditor; @@ -28,7 +28,7 @@ public class UrdfRobotEditor : UnityEditor.Editor public void OnEnable() { - axisType = serializedObject.FindProperty("choosenAxis"); + axisType = serializedObject.FindProperty("chosenAxis"); } public override void OnInspectorGUI() { @@ -37,10 +37,6 @@ public override void OnInspectorGUI() urdfRobot = (UrdfRobot) target; - EditorGUILayout.PropertyField(axisType, new GUIContent("Axis Type")); - serializedObject.ApplyModifiedProperties(); - UrdfRobotExtensions.CorrectAxis(urdfRobot.gameObject); - GUILayout.Space(5); GUILayout.Label("All Rigidbodies", EditorStyles.boldLabel); DisplaySettingsToggle(new GUIContent("Use Gravity", "If disabled, robot is not affected by gravity."), urdfRobot.SetRigidbodiesUseGravity, UrdfRobot.useGravity); @@ -61,9 +57,12 @@ public override void OnInspectorGUI() EditorGUILayout.EndHorizontal(); GUILayout.Space(5); + EditorGUI.BeginDisabledGroup(true); EditorGUILayout.PropertyField(axisType, new GUIContent("Axis Type", "Adjust this if the models that make up your robot are facing the wrong direction.")); - serializedObject.ApplyModifiedProperties(); - UrdfRobotExtensions.CorrectAxis(urdfRobot.gameObject); + EditorGUI.EndDisabledGroup(); + // Legacy code for correcting axis in the inspector + // serializedObject.ApplyModifiedProperties(); + // UrdfRobotExtensions.CorrectAxis(urdfRobot.gameObject); if (urdfRobot.GetComponent() == null || urdfRobot.GetComponent() == null) { diff --git a/com.unity.robotics.urdf-importer/Editor/MenuItems/FileImportMenu.cs b/com.unity.robotics.urdf-importer/Editor/MenuItems/FileImportMenu.cs index b0763dfb..6eee09fc 100644 --- a/com.unity.robotics.urdf-importer/Editor/MenuItems/FileImportMenu.cs +++ b/com.unity.robotics.urdf-importer/Editor/MenuItems/FileImportMenu.cs @@ -38,8 +38,8 @@ private void OnGUI() //Select the original up axis of the imported mesh GUILayout.Space(5); EditorGUILayout.BeginHorizontal(); - settings.choosenAxis = (ImportSettings.axisType)EditorGUILayout.EnumPopup( - "Select Axis Type" , settings.choosenAxis); + settings.chosenAxis = (ImportSettings.axisType)EditorGUILayout.EnumPopup( + "Select Axis Type" , settings.chosenAxis); EditorGUILayout.EndHorizontal(); //Window title diff --git a/com.unity.robotics.urdf-importer/Runtime/AssetHandlers/LocateAssetHandler.cs b/com.unity.robotics.urdf-importer/Runtime/AssetHandlers/LocateAssetHandler.cs index 209d32bd..35d02dc9 100644 --- a/com.unity.robotics.urdf-importer/Runtime/AssetHandlers/LocateAssetHandler.cs +++ b/com.unity.robotics.urdf-importer/Runtime/AssetHandlers/LocateAssetHandler.cs @@ -28,7 +28,7 @@ public static T FindUrdfAsset(string urdfFileName) where T : UnityEngine.Obje var originalUrdfPath = UrdfAssetPathHandler.GetRelativeAssetPathFromUrdfPath(urdfFileName, false); if (originalUrdfPath.ToLower().EndsWith(".stl")) {// it is an asset that requires post processing - if (UrdfRobotExtensions.importsettings.OverwriteExistingPrefabs || !RuntimeUrdf.AssetExists(fileAssetPath, true)) + if ((UrdfRobotExtensions.importsettings.OverwriteExistingPrefabs || !RuntimeUrdf.AssetExists(fileAssetPath, true)) && !UrdfGeometryCollision.UsedTemplateFiles.Contains(Path.GetFileNameWithoutExtension(fileAssetPath))) {// post process again to (re)create prefabs StlAssetPostProcessor.PostprocessStlFile(originalUrdfPath); } diff --git a/com.unity.robotics.urdf-importer/Runtime/AssetHandlers/UrdfAssetPathHandler.cs b/com.unity.robotics.urdf-importer/Runtime/AssetHandlers/UrdfAssetPathHandler.cs index 64058948..b5af45ad 100644 --- a/com.unity.robotics.urdf-importer/Runtime/AssetHandlers/UrdfAssetPathHandler.cs +++ b/com.unity.robotics.urdf-importer/Runtime/AssetHandlers/UrdfAssetPathHandler.cs @@ -14,6 +14,8 @@ limitations under the License. using System.IO; using UnityEngine; +using System; +using System.Diagnostics; namespace Unity.Robotics.UrdfImporter { @@ -50,19 +52,24 @@ public static string GetPackageRoot() public static string GetRelativeAssetPath(string absolutePath) { + string assetPath = absolutePath; var absolutePathUnityFormat = absolutePath.SetSeparatorChar(); if (!absolutePathUnityFormat.StartsWith(Application.dataPath.SetSeparatorChar())) { #if UNITY_EDITOR if (!RuntimeUrdf.IsRuntimeMode()) { - return null; + if (absolutePath.Length > Application.dataPath.Length) + { + assetPath = absolutePath.Substring(Application.dataPath.Length - "Assets".Length); + } } #endif - return absolutePath; // so that it works in runtime } - - var assetPath = "Assets" + absolutePath.Substring(Application.dataPath.Length); + else + { + assetPath = "Assets" + absolutePath.Substring(Application.dataPath.Length); + } return assetPath.SetSeparatorChar(); } @@ -82,24 +89,27 @@ public static string GetFullAssetPath(string relativePath) public static string GetRelativeAssetPathFromUrdfPath(string urdfPath, bool convertToPrefab=true) { - if (!urdfPath.StartsWith(@"package://")) + string path; + bool useFileUri = false; + if (!urdfPath.StartsWith(@"file://") && !urdfPath.StartsWith(@"package://")) { - Debug.LogWarning(@$"{urdfPath} is not a valid URDF package file path. Path should start with package://, and URDF file should be in the directory root."); if (urdfPath.Substring(0, 3) == "../") - { - Debug.LogWarning("Attempting to replace file path's starting instance of `../` with standard package notation `package://` to prevent manual path traversal at root of directory!"); + { + UnityEngine.Debug.LogWarning("Attempting to replace file path's starting instance of `../` with standard package notation `package://` to prevent manual path traversal at root of directory!"); urdfPath = $@"package://{urdfPath.Substring(3)}"; - } - else - { - return null; - } + } } - string path; + // loading assets relative path from ROS/ROS2 package. if (urdfPath.StartsWith(@"package://")) { path = urdfPath.Substring(10).SetSeparatorChar(); } + // loading assets from file:// type URI. + else if (urdfPath.StartsWith(@"file://")) + { + path = urdfPath.Substring(7).SetSeparatorChar(); + useFileUri = true; + } else { path = urdfPath.SetSeparatorChar(); @@ -111,6 +121,9 @@ public static string GetRelativeAssetPathFromUrdfPath(string urdfPath, bool conv path = path.Substring(0, path.Length - 3) + "prefab"; } + if (useFileUri) { + return path; + } return Path.Combine(packageRoot, path); } #endregion @@ -120,7 +133,7 @@ public static bool IsValidAssetPath(string path) #if UNITY_EDITOR if (!RuntimeUrdf.IsRuntimeMode()) { - return GetRelativeAssetPath(path) != null; + return Directory.Exists(path) || File.Exists(path); } #endif //RuntimeImporter. TODO: check if the path really exists diff --git a/com.unity.robotics.urdf-importer/Runtime/Extensions/UrdfGeometryCollision.cs b/com.unity.robotics.urdf-importer/Runtime/Extensions/UrdfGeometryCollision.cs index 6f18e541..52cc1b4f 100644 --- a/com.unity.robotics.urdf-importer/Runtime/Extensions/UrdfGeometryCollision.cs +++ b/com.unity.robotics.urdf-importer/Runtime/Extensions/UrdfGeometryCollision.cs @@ -21,6 +21,10 @@ namespace Unity.Robotics.UrdfImporter { public class UrdfGeometryCollision : UrdfGeometry { + public static List UsedTemplateFiles => s_UsedTemplateFiles; + static List s_UsedTemplateFiles = new List(); + static List s_CreatedAssetNames = new List(); + public static void Create(Transform parent, GeometryTypes geometryType, Link.Geometry geometry = null) { GameObject geometryGameObject = null; @@ -123,9 +127,17 @@ private static void ConvertCylinderToCollider(MeshFilter filter) var packageRoot = UrdfAssetPathHandler.GetPackageRoot(); var filePath = RuntimeUrdf.AssetDatabase_GUIDToAssetPath(RuntimeUrdf.AssetDatabase_CreateFolder($"{packageRoot}", "meshes")); var name =$"{filePath}/Cylinder.asset"; - Debug.Log($"Creating new cylinder file: {name}"); - RuntimeUrdf.AssetDatabase_CreateAsset(collider, name, uniquePath:true); - RuntimeUrdf.AssetDatabase_SaveAssets(); + // Only create new asset if one doesn't exist + if (!RuntimeUrdf.AssetExists(name)) + { + Debug.Log($"Creating new cylinder file: {name}"); + RuntimeUrdf.AssetDatabase_CreateAsset(collider, name, uniquePath:true); + RuntimeUrdf.AssetDatabase_SaveAssets(); + } + else + { + collider = RuntimeUrdf.AssetDatabase_LoadAssetAtPath(name); + } } MeshCollider current = go.AddComponent(); current.sharedMesh = collider; @@ -196,16 +208,27 @@ private static void ConvertMeshToColliders(GameObject gameObject, string locatio List colliderMeshes = decomposer.GenerateConvexMeshes(meshFilter.sharedMesh); foreach (Mesh collider in colliderMeshes) { + var c = collider; if (!RuntimeUrdf.IsRuntimeMode()) { meshIndex++; string name = $"{filePath}/{templateFileName}_{meshIndex}.asset"; - Debug.Log($"Creating new mesh file: {name}"); - RuntimeUrdf.AssetDatabase_CreateAsset(collider, name); - RuntimeUrdf.AssetDatabase_SaveAssets(); + // Only create new asset if one doesn't exist or should overwrite + if ((UrdfRobotExtensions.importsettings.OverwriteExistingPrefabs || !RuntimeUrdf.AssetExists(name)) && !s_CreatedAssetNames.Contains(name)) + { + Debug.Log($"Creating new mesh file: {name}"); + RuntimeUrdf.AssetDatabase_CreateAsset(c, name); + RuntimeUrdf.AssetDatabase_SaveAssets(); + s_CreatedAssetNames.Add(name); + s_UsedTemplateFiles.Add(templateFileName); + } + else + { + c = RuntimeUrdf.AssetDatabase_LoadAssetAtPath(name); + } } MeshCollider current = child.AddComponent(); - current.sharedMesh = collider; + current.sharedMesh = c; current.convex = setConvex; } Component.DestroyImmediate(child.GetComponent()); @@ -215,5 +238,11 @@ private static void ConvertMeshToColliders(GameObject gameObject, string locatio } } + public static void BeginNewUrdfImport() + { + s_CreatedAssetNames.Clear(); + s_UsedTemplateFiles.Clear(); + } + } } diff --git a/com.unity.robotics.urdf-importer/Runtime/Extensions/UrdfLinkExtensions.cs b/com.unity.robotics.urdf-importer/Runtime/Extensions/UrdfLinkExtensions.cs index a6059af5..8cbfc7c2 100644 --- a/com.unity.robotics.urdf-importer/Runtime/Extensions/UrdfLinkExtensions.cs +++ b/com.unity.robotics.urdf-importer/Runtime/Extensions/UrdfLinkExtensions.cs @@ -23,8 +23,8 @@ public static GameObject Create(Transform parent, Link link = null, Joint joint GameObject linkObject = new GameObject("link"); linkObject.transform.SetParentAndAlign(parent); UrdfLink urdfLink = linkObject.AddComponent(); - UrdfVisualsExtensions.Create(linkObject.transform, link?.visuals); UrdfCollisionsExtensions.Create(linkObject.transform, link?.collisions); + UrdfVisualsExtensions.Create(linkObject.transform, link?.visuals); if (link != null) { diff --git a/com.unity.robotics.urdf-importer/Runtime/Extensions/UrdfRobotExtensions.cs b/com.unity.robotics.urdf-importer/Runtime/Extensions/UrdfRobotExtensions.cs index afe22508..8f3d0bfc 100644 --- a/com.unity.robotics.urdf-importer/Runtime/Extensions/UrdfRobotExtensions.cs +++ b/com.unity.robotics.urdf-importer/Runtime/Extensions/UrdfRobotExtensions.cs @@ -71,7 +71,7 @@ private static ImportPipelineData ImportPipelineInit(string filename, ImportSett im.wasRuntimeMode = RuntimeUrdf.IsRuntimeMode(); im.forceRuntimeMode = forceRuntimeMode; - if (forceRuntimeMode) + if (forceRuntimeMode) { RuntimeUrdf.SetRuntimeMode(true); } @@ -80,8 +80,9 @@ private static ImportPipelineData ImportPipelineInit(string filename, ImportSett if (!UrdfAssetPathHandler.IsValidAssetPath(im.robot.filename)) { - Debug.LogError("URDF file and ressources must be placed in Assets Folder:\n" + Application.dataPath); - if (forceRuntimeMode) + Debug.LogError("URDF file and resources must be placed in project folder:" + + $"\n{Application.dataPath.Substring(0, Application.dataPath.Length - "Assets".Length)}"); + if (forceRuntimeMode) { // set runtime mode back to what it was RuntimeUrdf.SetRuntimeMode(im.wasRuntimeMode); } @@ -91,10 +92,10 @@ private static ImportPipelineData ImportPipelineInit(string filename, ImportSett } // Creates the robot game object. - private static void ImportPipelineCreateObject(ImportPipelineData im) + private static void ImportPipelineCreateObject(ImportPipelineData im) { im.robotGameObject = new GameObject(im.robot.name); - + importsettings = im.settings; im.settings.totalLinks = im.robot.links.Count; @@ -104,12 +105,12 @@ private static void ImportPipelineCreateObject(ImportPipelineData im) im.robotGameObject.AddComponent(); im.robotGameObject.AddComponent(); - if (RuntimeUrdf.IsRuntimeMode()) + if (RuntimeUrdf.IsRuntimeMode()) {// In runtime mode, we have to disable controller while robot is being constructed. im.robotGameObject.GetComponent().enabled = false; } - im.robotGameObject.GetComponent().SetAxis(im.settings.choosenAxis); + im.robotGameObject.GetComponent().SetAxis(im.settings.chosenAxis); UrdfAssetPathHandler.SetPackageRoot(Path.GetDirectoryName(im.robot.filename)); UrdfMaterial.InitializeRobotMaterials(im.robot); @@ -119,12 +120,12 @@ private static void ImportPipelineCreateObject(ImportPipelineData im) // Creates the stack of robot joints. Should be called iteratively until false is returned. private static bool ProcessJointStack(ImportPipelineData im) { - if (im.importStack == null) + if (im.importStack == null) { im.importStack = new Stack>(); im.importStack.Push(new Tuple(im.robot.root, im.robotGameObject.transform, null)); } - + if (im.importStack.Count != 0) { Tuple currentLink = im.importStack.Pop(); @@ -152,7 +153,7 @@ private static void ImportPipelinePostCreate(ImportPipelineData im) CorrectAxis(im.robotGameObject); CreateCollisionExceptions(im.robot, im.robotGameObject); - if (im.forceRuntimeMode) + if (im.forceRuntimeMode) { // set runtime mode back to what it was RuntimeUrdf.SetRuntimeMode(im.wasRuntimeMode); } @@ -164,8 +165,8 @@ private static void ImportPipelinePostCreate(ImportPipelineData im) /// URDF filename /// Import Settings /// If true, will show the progress of import step by step - /// - /// When true, runs the runtime loading mode even in Editor. When false, uses the default behavior, + /// + /// When true, runs the runtime loading mode even in Editor. When false, uses the default behavior, /// i.e. runtime will be enabled in standalone build and disable when running in editor. /// In runtime mode, the Controller component of the robot will be added but not activated automatically and has to be enabled manually. /// This is to allow initializing the controller values (stiffness, damping, etc.) before the controller.Start() is called @@ -173,6 +174,7 @@ private static void ImportPipelinePostCreate(ImportPipelineData im) /// public static IEnumerator Create(string filename, ImportSettings settings, bool loadStatus = false, bool forceRuntimeMode = false) { + UrdfGeometryCollision.BeginNewUrdfImport(); ImportPipelineData im = ImportPipelineInit(filename, settings, loadStatus, forceRuntimeMode); if (im == null) { @@ -221,8 +223,9 @@ public static GameObject CreateRuntime(string filename, ImportSettings settings) public static void CorrectAxis(GameObject robot) { + //Debug.Log("hit"); UrdfRobot robotScript = robot.GetComponent(); - if (robotScript == null) + if (robotScript == null) { Debug.LogError("Robot has no UrdfRobot component attached. Abandon correcting axis"); return; @@ -236,7 +239,7 @@ public static void CorrectAxis(GameObject robot) Quaternion correctZtoY = Quaternion.Inverse((correctYtoZ)); Quaternion correction = new Quaternion(); - if (robotScript.choosenAxis == ImportSettings.axisType.zAxis) + if (robotScript.chosenAxis == ImportSettings.axisType.zAxis) { correction = correctYtoZ; } @@ -254,7 +257,7 @@ public static void CorrectAxis(GameObject robot) foreach (UrdfCollision collision in collisionMeshList) { - if (robotScript.choosenAxis != ImportSettings.axisType.zAxis) + if (collision.geometryType == GeometryTypes.Mesh) { collision.transform.localRotation = collision.transform.localRotation * correction; } @@ -368,8 +371,8 @@ public static void CreateTag() SerializedProperty t = tagsProp.GetArrayElementAtIndex(i); if (t.stringValue.Equals(FKRobot.k_TagName)) { - found = true; - break; + found = true; + break; } } @@ -381,7 +384,7 @@ public static void CreateTag() n.stringValue = FKRobot.k_TagName; } - tagManager.ApplyModifiedProperties(); + tagManager.ApplyModifiedProperties(); #endif } @@ -393,11 +396,11 @@ static void SetTag(GameObject go) } catch (Exception) { - Debug.LogError($"Unable to find tag '{FKRobot.k_TagName}'." + + Debug.LogError($"Unable to find tag '{FKRobot.k_TagName}'." + $"Add a tag '{FKRobot.k_TagName}' in the Project Settings in Unity Editor."); return; } - + if (!go) return; diff --git a/com.unity.robotics.urdf-importer/Runtime/RuntimeImport/RuntimeUrdfImporterExample.cs b/com.unity.robotics.urdf-importer/Runtime/RuntimeImport/RuntimeUrdfImporterExample.cs index 513d4794..aade0d4e 100644 --- a/com.unity.robotics.urdf-importer/Runtime/RuntimeImport/RuntimeUrdfImporterExample.cs +++ b/com.unity.robotics.urdf-importer/Runtime/RuntimeImport/RuntimeUrdfImporterExample.cs @@ -56,7 +56,7 @@ private IEnumerator LoadUrdf() ImportSettings settings = new ImportSettings { - choosenAxis = ImportSettings.axisType.yAxis, + chosenAxis = ImportSettings.axisType.yAxis, convexMethod = useVHACD ? ImportSettings.convexDecomposer.vHACD : ImportSettings.convexDecomposer.unity, }; diff --git a/com.unity.robotics.urdf-importer/Runtime/UrdfComponents/ImportSettings.cs b/com.unity.robotics.urdf-importer/Runtime/UrdfComponents/ImportSettings.cs index d117d873..f1c2a23a 100644 --- a/com.unity.robotics.urdf-importer/Runtime/UrdfComponents/ImportSettings.cs +++ b/com.unity.robotics.urdf-importer/Runtime/UrdfComponents/ImportSettings.cs @@ -8,8 +8,8 @@ public class ImportSettings { public enum axisType { - zAxis, yAxis, + zAxis, } public enum convexDecomposer @@ -18,7 +18,7 @@ public enum convexDecomposer vHACD, } - public axisType choosenAxis = axisType.yAxis; + public axisType chosenAxis = axisType.yAxis; public convexDecomposer convexMethod = convexDecomposer.vHACD; public bool OverwriteExistingPrefabs { get; set; } = false; diff --git a/com.unity.robotics.urdf-importer/Runtime/UrdfComponents/UrdfRobot.cs b/com.unity.robotics.urdf-importer/Runtime/UrdfComponents/UrdfRobot.cs index d00ed14a..404ceafe 100644 --- a/com.unity.robotics.urdf-importer/Runtime/UrdfComponents/UrdfRobot.cs +++ b/com.unity.robotics.urdf-importer/Runtime/UrdfComponents/UrdfRobot.cs @@ -23,7 +23,7 @@ public enum GeometryTypes { Box, Cylinder, Sphere, Mesh } public class UrdfRobot : MonoBehaviour { public string FilePath; - public ImportSettings.axisType choosenAxis ; + public ImportSettings.axisType chosenAxis ; [SerializeField] private ImportSettings.axisType currentOrientation = ImportSettings.axisType.yAxis; public List collisionExceptions; @@ -76,12 +76,12 @@ public void ChangeToCorrectedSpace() public bool CheckOrientation() { - return currentOrientation == choosenAxis; + return currentOrientation == chosenAxis; } public void SetOrientation() { - currentOrientation = choosenAxis; + currentOrientation = chosenAxis; } public void AddController() @@ -116,7 +116,7 @@ public void AddFkRobot() public void SetAxis(ImportSettings.axisType setAxis) { - this.choosenAxis = setAxis; + this.chosenAxis = setAxis; } void Start() diff --git a/com.unity.robotics.urdf-importer/Tests/Runtime/AssetHandlers/UrdfAssetPathHandlerTests.cs b/com.unity.robotics.urdf-importer/Tests/Runtime/AssetHandlers/UrdfAssetPathHandlerTests.cs index 2e29eaca..14a124c6 100644 --- a/com.unity.robotics.urdf-importer/Tests/Runtime/AssetHandlers/UrdfAssetPathHandlerTests.cs +++ b/com.unity.robotics.urdf-importer/Tests/Runtime/AssetHandlers/UrdfAssetPathHandlerTests.cs @@ -1,12 +1,9 @@ -using System.IO; -using System.Collections; +using System; using System.Collections.Generic; +using System.IO; using NUnit.Framework; -using UnityEngine; using UnityEditor; -using UnityEngine.TestTools; -using Unity.Robotics.UrdfImporter; -using Object = UnityEngine.Object; +using UnityEngine; namespace Unity.Robotics.UrdfImporter.Tests { @@ -25,7 +22,7 @@ public void SetUp() Directory.CreateDirectory(assetRoot); Directory.CreateDirectory(packageRoot); } - + [Test] public void GetSetPackageRoot_RuntimeModeEnabled_Success() { @@ -37,44 +34,59 @@ public void GetSetPackageRoot_RuntimeModeEnabled_Success() } [Test] - public void GetRelativeAssetPath_Nonruntime_NullValues() + public void GetRelativeAssetPath_Nonruntime_Success() { RuntimeUrdf.runtimeModeEnabled = false; - // Everything returns null during non-runtime - Assert.IsNull(UrdfAssetPathHandler.GetRelativeAssetPath("Invalid/Asset/Path")); - Assert.IsNull(UrdfAssetPathHandler.GetRelativeAssetPath($"{assetRoot}/TestAsset.txt")); - Assert.IsNull(UrdfAssetPathHandler.GetRelativeAssetPath($"Packages/com.unity.robotics.urdf-importer/Tests/Runtime/AssetHandlers/UrdfAssetPathHandlerTests.cs")); + Assert.AreEqual("Invalid/Asset/Path", UrdfAssetPathHandler.GetRelativeAssetPath("Invalid/Asset/Path")); + Assert.AreEqual($"{assetRoot}/TestAsset.txt", + UrdfAssetPathHandler.GetRelativeAssetPath($"{assetRoot}/TestAsset.txt")); +#if UNITY_EDITOR + Assert.AreEqual( + "Packages/com.unity.robotics.urdf-importer/Tests/Runtime/AssetHandlers/UrdfAssetPathHandlerTests.cs", + UrdfAssetPathHandler.GetRelativeAssetPath( + $"{Application.dataPath.Substring(0, Application.dataPath.Length - "Assets".Length)}" + + "Packages/com.unity.robotics.urdf-importer/Tests/Runtime/AssetHandlers/UrdfAssetPathHandlerTests.cs")); +#endif } [Test] public void GetRelativeAssetPath_Runtime_Success() { RuntimeUrdf.runtimeModeEnabled = true; + // Starting with Application.dataPath - Assert.AreEqual("Assets/Valid/Path", UrdfAssetPathHandler.GetRelativeAssetPath($"{Application.dataPath}/Valid/Path")); + Assert.AreEqual("Assets/Valid/Path", + UrdfAssetPathHandler.GetRelativeAssetPath($"{Application.dataPath}/Valid/Path")); + // Not starting with dataPath - Assert.AreEqual("Assets/Valid/Path", UrdfAssetPathHandler.GetRelativeAssetPath($"Assets/Valid/Path")); + Assert.AreEqual("Assets/Valid/Path", UrdfAssetPathHandler.GetRelativeAssetPath("Assets/Valid/Path")); } [Test] public void GetFullAssetPath_AssetAndPackageRoot_Success() { - Assert.AreEqual($"{Application.dataPath}/Tests/Runtime/UrdfAssetPathHandler", UrdfAssetPathHandler.GetFullAssetPath(assetRoot)); - string projectPath = Application.dataPath.Substring(0, Application.dataPath.Length - "Assets".Length); - Assert.AreEqual($"{projectPath}Packages/com.unity.robotics.urdf-importer/Tests/Runtime/UrdfAssetPathHandler", UrdfAssetPathHandler.GetFullAssetPath(packageRoot)); + Assert.AreEqual($"{Application.dataPath}/Tests/Runtime/UrdfAssetPathHandler", + UrdfAssetPathHandler.GetFullAssetPath(assetRoot)); + var projectPath = Application.dataPath.Substring(0, Application.dataPath.Length - "Assets".Length); + Assert.AreEqual( + $"{projectPath}Packages/com.unity.robotics.urdf-importer/Tests/Runtime/UrdfAssetPathHandler", + UrdfAssetPathHandler.GetFullAssetPath(packageRoot)); } - + [Test] public void GetRelativeAssetPathFromUrdfPath_CubeUrdf_Success() { - string urdfRoot = "Packages/com.unity.robotics.urdf-importer/Tests/Runtime/Assets/URDF/cube"; + var urdfRoot = "Packages/com.unity.robotics.urdf-importer/Tests/Runtime/Assets/URDF/cube"; + // Force runtime mode to set testing package root RuntimeUrdf.runtimeModeEnabled = true; UrdfAssetPathHandler.SetPackageRoot(urdfRoot); RuntimeUrdf.runtimeModeEnabled = false; - - Assert.AreEqual($"{urdfRoot}/meshes/cube.prefab", UrdfAssetPathHandler.GetRelativeAssetPathFromUrdfPath("package://meshes/cube.stl")); - Assert.AreEqual($"{urdfRoot}/meshes/cube.prefab", UrdfAssetPathHandler.GetRelativeAssetPathFromUrdfPath("../meshes/cube.stl")); + + Assert.AreEqual($"{urdfRoot}/meshes/cube.prefab", + UrdfAssetPathHandler.GetRelativeAssetPathFromUrdfPath("package://meshes/cube.stl")); + Assert.AreEqual($"{urdfRoot}/meshes/cube.prefab", + UrdfAssetPathHandler.GetRelativeAssetPathFromUrdfPath("../meshes/cube.stl")); } [Test] @@ -83,16 +95,18 @@ public void IsValidAssetPath_Nonruntime_Success() RuntimeUrdf.runtimeModeEnabled = false; AssetDatabase.CreateAsset(new TextAsset("TestAsset"), $"{assetRoot}/TestAsset.txt"); - // Everything returns null during non-runtime Assert.IsFalse(UrdfAssetPathHandler.IsValidAssetPath("Invalid/Asset/Path")); - Assert.IsFalse(UrdfAssetPathHandler.IsValidAssetPath($"{assetRoot}/TestAsset.txt")); - Assert.IsFalse(UrdfAssetPathHandler.IsValidAssetPath($"Packages/com.unity.robotics.urdf-importer/Tests/Runtime/AssetHandlers/UrdfAssetPathHandlerTests.cs")); + Assert.IsTrue(UrdfAssetPathHandler.IsValidAssetPath($"{assetRoot}/TestAsset.txt")); + Assert.IsTrue(UrdfAssetPathHandler.IsValidAssetPath( + "Packages/com.unity.robotics.urdf-importer/Tests/Runtime/AssetHandlers/UrdfAssetPathHandlerTests.cs")); RuntimeUrdf.runtimeModeEnabled = true; + // Everything returns true during runtime Assert.IsTrue(UrdfAssetPathHandler.IsValidAssetPath("Invalid/Asset/Path")); Assert.IsTrue(UrdfAssetPathHandler.IsValidAssetPath($"{assetRoot}/TestAsset.txt")); - Assert.IsTrue(UrdfAssetPathHandler.IsValidAssetPath($"Packages/com.unity.robotics.urdf-importer/Tests/Runtime/AssetHandlers/UrdfAssetPathHandlerTests.cs")); + Assert.IsTrue(UrdfAssetPathHandler.IsValidAssetPath( + "Packages/com.unity.robotics.urdf-importer/Tests/Runtime/AssetHandlers/UrdfAssetPathHandlerTests.cs")); } [Test] @@ -102,22 +116,26 @@ public void GetMaterialAssetPath_AssetAndPackage_Success() RuntimeUrdf.runtimeModeEnabled = true; UrdfAssetPathHandler.SetPackageRoot(assetRoot); RuntimeUrdf.runtimeModeEnabled = false; - Assert.AreEqual($"{assetRoot}/Materials/TestMaterial.mat", UrdfAssetPathHandler.GetMaterialAssetPath("TestMaterial")); - + Assert.AreEqual($"{assetRoot}/Materials/TestMaterial.mat", + UrdfAssetPathHandler.GetMaterialAssetPath("TestMaterial")); // Force runtime mode to set testing package root RuntimeUrdf.runtimeModeEnabled = true; UrdfAssetPathHandler.SetPackageRoot(packageRoot); RuntimeUrdf.runtimeModeEnabled = false; - Assert.AreEqual($"{packageRoot}/Materials/TestMaterial.mat", UrdfAssetPathHandler.GetMaterialAssetPath("TestMaterial")); + Assert.AreEqual($"{packageRoot}/Materials/TestMaterial.mat", + UrdfAssetPathHandler.GetMaterialAssetPath("TestMaterial")); } [TearDown] public void TearDown() { - List outFailedPaths = new List(); - AssetDatabase.DeleteAssets(new string[] {"Assets/Tests"}, outFailedPaths); - AssetDatabase.DeleteAssets(new string[] {"Packages/com.unity.robotics.urdf-importer/Tests/Runtime/UrdfAssetPathHandler"}, outFailedPaths); + var outFailedPaths = new List(); + AssetDatabase.DeleteAssets( + new[] + { + "Assets/Tests", "Packages/com.unity.robotics.urdf-importer/Tests/Runtime/UrdfAssetPathHandler" + }, outFailedPaths); } } } diff --git a/com.unity.robotics.urdf-importer/Tests/Runtime/Extensions/UrdfGeometryCollisionTests.cs b/com.unity.robotics.urdf-importer/Tests/Runtime/Extensions/UrdfGeometryCollisionTests.cs index 64aa91b8..2ac72d0c 100644 --- a/com.unity.robotics.urdf-importer/Tests/Runtime/Extensions/UrdfGeometryCollisionTests.cs +++ b/com.unity.robotics.urdf-importer/Tests/Runtime/Extensions/UrdfGeometryCollisionTests.cs @@ -135,6 +135,7 @@ public void Create_FromStlUnity_CubeMesh() public void Create_FromStlVhacdNotRuntime_CubeMesh() { // Force runtime mode to set testing package root + LogAssert.ignoreFailingMessages = true; RuntimeUrdf.runtimeModeEnabled = true; UrdfAssetPathHandler.SetPackageRoot("Packages/com.unity.robotics.urdf-importer/Tests/Runtime/Assets/URDF/cube/"); RuntimeUrdf.runtimeModeEnabled = false; @@ -156,6 +157,7 @@ public void Create_FromStlVhacdNotRuntime_CubeMesh() // Verify geometry created in Assets Assert.IsNotNull(AssetDatabase.FindAssets("cube t:mesh", new string[] {"Packages/com.unity.robotics.urdf-importer/Tests/Runtime/Assets/URDF/cube/meshes"})); + LogAssert.ignoreFailingMessages = false; AssetDatabase.DeleteAsset("Packages/com.unity.robotics.urdf-importer/Tests/Runtime/Assets/URDF/cube/meshes/cube_1.asset"); Object.DestroyImmediate(parent.gameObject); } diff --git a/com.unity.robotics.urdf-importer/Tests/Runtime/Extensions/UrdfRobotExtensionsTests.cs b/com.unity.robotics.urdf-importer/Tests/Runtime/Extensions/UrdfRobotExtensionsTests.cs new file mode 100644 index 00000000..5821fd34 --- /dev/null +++ b/com.unity.robotics.urdf-importer/Tests/Runtime/Extensions/UrdfRobotExtensionsTests.cs @@ -0,0 +1,158 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using NUnit.Framework; +using Unity.Robotics.UrdfImporter.Control; +using UnityEditor; +using UnityEngine; +using UnityEngine.TestTools; +using Object = UnityEngine.Object; + +namespace Unity.Robotics.UrdfImporter.Tests +{ + public class UrdfRobotExtensionsTests + { + string assetRoot; + string urdfFile; + + [SetUp] + public void SetUp() + { + assetRoot = "Assets/Tests/Runtime/UrdfRobotExtensionsTests"; + urdfFile = + $"{Application.dataPath.Substring(0, Application.dataPath.Length - "Assets".Length)}" + + "Packages/com.unity.robotics.urdf-importer/Tests/Runtime/Assets/URDF/cube/cube.urdf"; + + Directory.CreateDirectory(assetRoot); + RuntimeUrdf.runtimeModeEnabled = false; + } + + [Test] + public void Create_NoParameters_Success() + { + UrdfRobotExtensions.Create(); + var robot = GameObject.Find("Robot"); + Assert.IsNotNull(robot); + Assert.IsNotNull(robot.GetComponent()); + Assert.IsNotNull(robot.GetComponent()); + Assert.IsTrue(robot.GetComponentsInChildren().Length > 0); + Assert.AreEqual("base_link", robot.GetComponentInChildren().name); + + Object.DestroyImmediate(robot); + } + + [UnityTest] + public IEnumerator CreateCoroutine_FromFileDefaultSettings_Success() + { + var coroutineCreate = + UrdfRobotExtensions.Create(urdfFile, ImportSettings.DefaultSettings(), forceRuntimeMode: true); + yield return coroutineCreate; + + var robotGO = coroutineCreate.Current; + Assert.IsNotNull(robotGO); + Assert.IsNotNull(robotGO.GetComponent()); + Assert.IsNotNull(robotGO.GetComponent()); + Assert.AreEqual("Plugins", robotGO.transform.GetChild(0).name); + Assert.IsTrue(robotGO.GetComponentsInChildren().Length > 0); + Assert.IsTrue(robotGO.GetComponentsInChildren().Length > 0); + + coroutineCreate.Dispose(); + Object.DestroyImmediate(robotGO); + } + + [Test] + public void CreateRuntime_FromCubeUrdfDefaultSettings_Success() + { + var robot = UrdfRobotExtensions.CreateRuntime(urdfFile, ImportSettings.DefaultSettings()); + + Assert.IsNotNull(robot); + Assert.IsNotNull(robot.GetComponent()); + Assert.IsNotNull(robot.GetComponent()); + Assert.AreEqual("Plugins", robot.transform.GetChild(0).name); + Assert.IsTrue(robot.GetComponentsInChildren().Length > 0); + Assert.IsTrue(robot.GetComponentsInChildren().Length > 0); + + Object.DestroyImmediate(robot); + } + + [Test] + public void CorrectAxis_CubeUrdf_Success() + { + var robot = UrdfRobotExtensions.CreateRuntime( + $"{Application.dataPath.Substring(0, Application.dataPath.Length - "Assets".Length)}" + + "Packages/com.unity.robotics.urdf-importer/Tests/Runtime/Assets/URDF/cube/cube.urdf", + ImportSettings.DefaultSettings()); + Assert.AreEqual(ImportSettings.axisType.yAxis, robot.GetComponent().chosenAxis); + UrdfRobotExtensions.CorrectAxis(robot); + Assert.IsTrue(Vector3.zero == robot.GetComponentInChildren().transform.localEulerAngles); + Assert.IsTrue(Quaternion.Euler(0, 0, 0) == + robot.GetComponentInChildren().transform.localRotation); + + robot.GetComponent().chosenAxis = ImportSettings.axisType.zAxis; + UrdfRobotExtensions.CorrectAxis(robot); + Assert.IsTrue(Quaternion.Euler(-90, 0, 90) == + robot.GetComponentInChildren().transform.localRotation); + Assert.IsTrue(Quaternion.Euler(-90, 0, 90) == robot.GetComponentInChildren().transform.localRotation); + + Object.DestroyImmediate(robot); + } + +#if UNITY_EDITOR + [Test] + public void ExportRobotToUrdf_NoParameterCreate_Success() + { + UrdfRobotExtensions.Create(); + var emptyRobot = GameObject.Find("Robot"); + var robot = emptyRobot.GetComponent(); + LogAssert.ignoreFailingMessages = true; + + robot.ExportRobotToUrdf(assetRoot); + + Assert.IsTrue(AssetDatabase.IsValidFolder($"{assetRoot}/meshes")); + Assert.IsTrue(AssetDatabase.IsValidFolder($"{assetRoot}/Resources")); + Assert.IsTrue(AssetDatabase.FindAssets("Robot", new[] { assetRoot }).Length > 0); + + LogAssert.ignoreFailingMessages = false; + + Object.DestroyImmediate(emptyRobot); + } +#endif + + [Test] + public void CreateTag_RobotTag_Success() + { + // Open tag manager + var tagManager = + new SerializedObject(AssetDatabase.LoadAllAssetsAtPath("ProjectSettings/TagManager.asset")[0]); + var tagsProp = tagManager.FindProperty("tags"); + + var found = false; + for (var i = 0; i < tagsProp.arraySize; i++) + { + var t = tagsProp.GetArrayElementAtIndex(i); + if (t.stringValue.Equals("robot")) + { + found = true; + break; + } + } + + Assert.IsTrue(found); + } + + [TearDown] + public void TearDown() + { + var outFailedPaths = new List(); + AssetDatabase.DeleteAssets( + new[] + { + "Assets/Tests", + "Packages/com.unity.robotics.urdf-importer/Tests/Runtime/Assets/URDF/cube/Materials", + "Packages/com.unity.robotics.urdf-importer/Tests/Runtime/Assets/URDF/cube/meshes/cube_1.asset", + "Packages/com.unity.robotics.urdf-importer/Tests/Runtime/Assets/URDF/cube/meshes/cube_1.asset.meta" + }, outFailedPaths); + } + } +} diff --git a/com.unity.robotics.urdf-importer/Tests/Runtime/Extensions/UrdfRobotExtensionsTests.cs.meta b/com.unity.robotics.urdf-importer/Tests/Runtime/Extensions/UrdfRobotExtensionsTests.cs.meta new file mode 100644 index 00000000..c2a1c81a --- /dev/null +++ b/com.unity.robotics.urdf-importer/Tests/Runtime/Extensions/UrdfRobotExtensionsTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 62735652de3eb42038cc032237104031 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.robotics.urdf-importer/package.json b/com.unity.robotics.urdf-importer/package.json index 4752a801..e7fac5fa 100644 --- a/com.unity.robotics.urdf-importer/package.json +++ b/com.unity.robotics.urdf-importer/package.json @@ -1,6 +1,6 @@ { "name": "com.unity.robotics.urdf-importer", - "version": "0.5.0-preview", + "version": "0.5.2-preview", "displayName": "URDF Importer", "description": "Facilitates importing configurations from the Universal Robot Description Format", "unity": "2020.2",