diff --git a/AGXUnity/Model/DeformableTerrainMaterial.cs b/AGXUnity/Model/DeformableTerrainMaterial.cs index bcf479e5..f0831f6c 100644 --- a/AGXUnity/Model/DeformableTerrainMaterial.cs +++ b/AGXUnity/Model/DeformableTerrainMaterial.cs @@ -19,8 +19,8 @@ public static string DefaultTerrainMaterialsPath if ( s_defaultTerrainMaterialsPath == null ) { var terrainMaterialLibraryOptions = new string[] { - "data/TerrainMaterials", - "data/MaterialLibrary/TerrainMaterials" + "TerrainMaterials", + "MaterialLibrary/TerrainMaterials" }; foreach ( var materialLibraryOption in terrainMaterialLibraryOptions ) { diff --git a/AGXUnity/NativeHandler.cs b/AGXUnity/NativeHandler.cs index 9a58c8be..2d8df39a 100644 --- a/AGXUnity/NativeHandler.cs +++ b/AGXUnity/NativeHandler.cs @@ -84,7 +84,7 @@ private void Configure() for ( int i = 0; i < (int)agxIO.Environment.Type.NUM_TYPES; ++i ) envInstance.getFilePath( (agxIO.Environment.Type)i ).clear(); - envInstance.getFilePath( agxIO.Environment.Type.RESOURCE_PATH ).pushbackPath( dataAGXRuntimePath ); + envInstance.getFilePath( agxIO.Environment.Type.RESOURCE_PATH ).pushbackPath( dataAGXRuntimePath + "/data" ); envInstance.getFilePath( agxIO.Environment.Type.RUNTIME_PATH ).pushbackPath( dataAGXRuntimePath ); if ( string.IsNullOrEmpty( envInstance.findComponent( "Referenced.agxEntity" ) ) ) diff --git a/AGXUnity/Sensor/LidarSensor.cs b/AGXUnity/Sensor/LidarSensor.cs index cb10d3da..429c8b3d 100644 --- a/AGXUnity/Sensor/LidarSensor.cs +++ b/AGXUnity/Sensor/LidarSensor.cs @@ -8,6 +8,17 @@ namespace AGXUnity.Sensor { + public class TestHelper + { + static public bool LidarRayPatternsAreFound() + { + var _ = NativeHandler.Instance; + var fileTest = agxIO.Environment.instance().getFilePath( agxIO.Environment.Type.RESOURCE_PATH ).find( "MaterialLibrary/TerrainMaterials/avia.bin" ); + return !string.IsNullOrEmpty( fileTest ); + } + + } + /// /// IModelData is an empty interface to allow for a lidar sensor to hold generic data dependent on the /// underlying lidar model used. @@ -70,17 +81,72 @@ public class GenericSweepData : IModelData public float VerticalResolution = 0.5f; } + + [Serializable] + public class LivoxData : IModelData + { + /// + /// Optionally downsample the amount of points generated. 1 is default, 2 is sample every other point in the pattern, 3 every 3 points etc. + /// + [Tooltip("Scale down the amount of points per second by skipping points in the pattern if set larger than 1.")] + [Min(1)] + public uint Downsample = 1; + } + + [Serializable] + public class ReadFromFileData : IModelData + { + /// + /// The frequency [Hz] of the lidar sweep + /// + public float Frequency = 10f; + /// + /// The amount of points from the pattern per frame + /// + [Min(1)] + public uint FrameSize = 10000; + /// + /// Path to the .csv or binare file with the ray pattern + /// + public string FilePath = ""; + /// + /// If true: file has only azimuth,elevation (no first column) + /// + public bool TwoColumns = false; + /// + /// If true: read values as degrees, if false: read as radians + /// + public bool AnglesInDegrees = true; + /// + /// If true skip first line of file + /// + public bool FirstLineIsHeader = true; + /// + /// CSV column delimiter + /// + public char Delimiter = ','; + + } + public enum LidarModelPreset { NONE, LidarModelGenericHorizontalSweep, LidarModelOusterOS0, LidarModelOusterOS1, - LidarModelOusterOS2 + LidarModelOusterOS2, + LidarModelLivoxAvia, + LidarModelLivoxHap, + LidarModelLivoxHorizon, + LidarModelLivoxMid40, + LidarModelLivoxMid70, + LidarModelLivoxMid360, + LidarModelLivoxTele, + LidarModelReadFromFile, } /// - /// WIP component for lidar sensor + /// Lidar Sensor Component /// [DisallowMultipleComponent] [AddComponentMenu( "AGXUnity/Sensors/Lidar Sensor" )] @@ -115,8 +181,24 @@ public LidarModelPreset LidarModelPreset LidarModelPreset.LidarModelOusterOS1 => new OusterData(), LidarModelPreset.LidarModelOusterOS2 => new OusterData(), LidarModelPreset.LidarModelGenericHorizontalSweep => new GenericSweepData(), + LidarModelPreset.LidarModelLivoxAvia => new LivoxData(), + LidarModelPreset.LidarModelLivoxHap => new LivoxData(), + LidarModelPreset.LidarModelLivoxHorizon => new LivoxData(), + LidarModelPreset.LidarModelLivoxMid40 => new LivoxData(), + LidarModelPreset.LidarModelLivoxMid70 => new LivoxData(), + LidarModelPreset.LidarModelLivoxMid360 => new LivoxData(), + LidarModelPreset.LidarModelLivoxTele => new LivoxData(), + LidarModelPreset.LidarModelReadFromFile => new ReadFromFileData(), _ => null, }; + + LocalRotation = ModelData switch + { + OusterData => new Vector3( 90, 90, 0 ), + GenericSweepData => new Vector3( -90, 90, 0 ), + LivoxData => new Vector3( -90, 90, 0 ), + _ => new Vector3() + }; } m_lidarModelPreset = value; } @@ -431,6 +513,53 @@ private LidarModel CreateLidarModel( LidarModelPreset preset ) lidarModel = new LidarModelOusterOS2( ousterData.ChannelCount, ousterData.BeamSpacing, ousterData.LidarMode ); break; + case LidarModelPreset.LidarModelLivoxAvia: + LivoxData livoxData = ModelData as LivoxData; + lidarModel = new LidarModelLivoxAvia( livoxData.Downsample ); + break; + + case LidarModelPreset.LidarModelLivoxHap: + livoxData = ModelData as LivoxData; + lidarModel = new LidarModelLivoxHap( livoxData.Downsample ); + break; + + case LidarModelPreset.LidarModelLivoxHorizon: + livoxData = ModelData as LivoxData; + lidarModel = new LidarModelLivoxHorizon( livoxData.Downsample ); + break; + + case LidarModelPreset.LidarModelLivoxMid40: + livoxData = ModelData as LivoxData; + lidarModel = new LidarModelLivoxMid40( livoxData.Downsample ); + break; + + case LidarModelPreset.LidarModelLivoxMid70: + livoxData = ModelData as LivoxData; + lidarModel = new LidarModelLivoxMid70( livoxData.Downsample ); + break; + + case LidarModelPreset.LidarModelLivoxMid360: + livoxData = ModelData as LivoxData; + lidarModel = new LidarModelLivoxMid360( livoxData.Downsample ); + break; + + case LidarModelPreset.LidarModelLivoxTele: + livoxData = ModelData as LivoxData; + lidarModel = new LidarModelLivoxTele( livoxData.Downsample ); + break; + + case LidarModelPreset.LidarModelReadFromFile: + ReadFromFileData readFromFileData = ModelData as ReadFromFileData; + RayFileDefinition rayFileDefinition = new RayFileDefinition(); + rayFileDefinition.path = readFromFileData.FilePath; + rayFileDefinition.twoColumns = readFromFileData.TwoColumns; + rayFileDefinition.anglesInDegrees = readFromFileData.AnglesInDegrees; + rayFileDefinition.firstLineIsHeader = readFromFileData.FirstLineIsHeader; + rayFileDefinition.delimiter = readFromFileData.Delimiter; + LidarProperties lidarProperties = new LidarProperties(BeamDivergence, BeamExitRadius); + lidarModel = new LidarModelReadFromFile( rayFileDefinition, readFromFileData.Frequency, readFromFileData.FrameSize, 1, new RangeReal32( LidarRange.Min, LidarRange.Max ), lidarProperties ); + break; + case LidarModelPreset.NONE: default: Debug.LogWarning( "No valid LidarModelPreset selected!" ); @@ -441,6 +570,7 @@ private LidarModel CreateLidarModel( LidarModelPreset preset ) return lidarModel; } + private void OnDrawGizmosSelected() { #if UNITY_EDITOR diff --git a/Editor/AGXUnityEditor/ExternalAGXInitializer.cs b/Editor/AGXUnityEditor/ExternalAGXInitializer.cs index 61150621..e7e9dc50 100644 --- a/Editor/AGXUnityEditor/ExternalAGXInitializer.cs +++ b/Editor/AGXUnityEditor/ExternalAGXInitializer.cs @@ -294,7 +294,7 @@ private bool InitializeCheckout( string agxDir ) select $"{data.Directory.FullName}{Path.DirectorySeparatorChar}bin{Path.DirectorySeparatorChar}x64" ).ToArray(); var installedBinDir = $"{binData[ INSTALLED ].Directory.FullName}{Path.DirectorySeparatorChar}bin{Path.DirectorySeparatorChar}x64"; AGX_PLUGIN_PATH = $"{installedBinDir}{Path.DirectorySeparatorChar}plugins"; - AGX_DATA_DIR = $"{installedBinDir}{Path.DirectorySeparatorChar}data"; + AGX_DATA_DIR = $"{binData[ INSTALLED ].Directory.FullName}{Path.DirectorySeparatorChar}data"; return true; } diff --git a/Editor/AGXUnityEditor/InvokeWrapperInspectorDrawer.cs b/Editor/AGXUnityEditor/InvokeWrapperInspectorDrawer.cs index 6ee4accf..9b98eae9 100644 --- a/Editor/AGXUnityEditor/InvokeWrapperInspectorDrawer.cs +++ b/Editor/AGXUnityEditor/InvokeWrapperInspectorDrawer.cs @@ -1284,6 +1284,22 @@ public static void DrawGenericSweepModelData( AGXUnity.Sensor.GenericSweepData d data.VerticalResolution = EditorGUILayout.FloatField( FindGUIContentFor( data.GetType(), "VerticalResolution" ), data.VerticalResolution ); } + public static void DrawReadFromFileModelData( AGXUnity.Sensor.ReadFromFileData data ) + { + data.Frequency = EditorGUILayout.FloatField( FindGUIContentFor( data.GetType(), "Frequency" ), data.Frequency ); + data.FrameSize = (uint)EditorGUILayout.FloatField( FindGUIContentFor( data.GetType(), "FrameSize" ), data.FrameSize ); + data.FilePath = EditorGUILayout.TextField( FindGUIContentFor( data.GetType(), "FilePath" ), data.FilePath ); + data.TwoColumns = EditorGUILayout.Toggle( FindGUIContentFor( data.GetType(), "TwoColumns" ), data.TwoColumns ); + data.AnglesInDegrees = EditorGUILayout.Toggle( FindGUIContentFor( data.GetType(), "AnglesInDegrees" ), data.AnglesInDegrees ); + data.FirstLineIsHeader = EditorGUILayout.Toggle( FindGUIContentFor( data.GetType(), "FirstLineIsHeader" ), data.FirstLineIsHeader ); + data.Delimiter = char.Parse( EditorGUILayout.TextField( FindGUIContentFor( data.GetType(), "Delimiter" ), data.Delimiter.ToString() ) ); + } + + public static void DrawLivoxModelData( AGXUnity.Sensor.LivoxData data ) + { + data.Downsample = (uint)EditorGUILayout.FloatField( FindGUIContentFor( data.GetType(), "Downsample" ), data.Downsample ); + } + [InspectorDrawer( typeof( AGXUnity.Sensor.IModelData ) )] public static object ModelDataDrawer( object[] objects, InvokeWrapper wrapper ) { @@ -1301,6 +1317,12 @@ public static object ModelDataDrawer( object[] objects, InvokeWrapper wrapper ) case AGXUnity.Sensor.GenericSweepData sweepData: DrawGenericSweepModelData( sweepData ); break; + case AGXUnity.Sensor.LivoxData livoxData: + DrawLivoxModelData( livoxData ); + break; + case AGXUnity.Sensor.ReadFromFileData readFromFileData: + DrawReadFromFileModelData( readFromFileData ); + break; } } diff --git a/Editor/AGXUnityEditor/Manager.cs b/Editor/AGXUnityEditor/Manager.cs index d27e1b0c..a8e3d7c0 100644 --- a/Editor/AGXUnityEditor/Manager.cs +++ b/Editor/AGXUnityEditor/Manager.cs @@ -775,7 +775,7 @@ internal static EnvironmentState ConfigureEnvironment() // to RUNTIME_PATH (for entities and components). The license file is // searched for by the license manager. var dataAndRuntimePath = AGXUnity.IO.Environment.Get( AGXUnity.IO.Environment.Variable.AGX_PLUGIN_PATH ); - envInstance.getFilePath( agxIO.Environment.Type.RESOURCE_PATH ).pushbackPath( dataAndRuntimePath ); + envInstance.getFilePath( agxIO.Environment.Type.RESOURCE_PATH ).pushbackPath( dataAndRuntimePath + "/data" ); envInstance.getFilePath( agxIO.Environment.Type.RUNTIME_PATH ).pushbackPath( dataAndRuntimePath ); } // Check if user would like to initialize AGX Dynamics with an diff --git a/Plugins/x86_64/agxDotNet.dll b/Plugins/x86_64/agxDotNet.dll index 566d6952..3eb391d8 100644 Binary files a/Plugins/x86_64/agxDotNet.dll and b/Plugins/x86_64/agxDotNet.dll differ diff --git a/Tests/Editor/DataTests.cs b/Tests/Editor/DataTests.cs new file mode 100644 index 00000000..affe173b --- /dev/null +++ b/Tests/Editor/DataTests.cs @@ -0,0 +1,22 @@ +using AGXUnity.Model; +using NUnit.Framework; + +namespace AGXUnityTesting.Editor +{ + public class DataTests + { + [Test] + public void TerrainMaterialPresetsAreFound() + { + var presets = DeformableTerrainMaterial.GetAvailablePresets(); + Assert.NotNull( presets ); + Assert.NotZero( presets.Length ); + } + + [Test] + public void LidarRayPatternsAreFound() + { + Assert.True( AGXUnity.Sensor.TestHelper.LidarRayPatternsAreFound() ); + } + } +} diff --git a/Tests/Editor/DataTests.cs.meta b/Tests/Editor/DataTests.cs.meta new file mode 100644 index 00000000..c7f7c943 --- /dev/null +++ b/Tests/Editor/DataTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 43ac71637fb919144bf8d65e1bfb06ba +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Tests/Runtime/DataTests.cs b/Tests/Runtime/DataTests.cs new file mode 100644 index 00000000..14231887 --- /dev/null +++ b/Tests/Runtime/DataTests.cs @@ -0,0 +1,25 @@ +using AGXUnity; +using AGXUnity.Model; +using NUnit.Framework; + +namespace AGXUnityTesting.Runtime +{ + public class DataTests + { + [Test] + public void TerrainMaterialPresetsAreFound() + { + var _ = NativeHandler.Instance; + var presets = DeformableTerrainMaterial.GetAvailablePresets(); + Assert.NotNull( presets ); + Assert.NotZero( presets.Length ); + } + + [Test] + public void LidarRayPatternsAreFound() + { + var _ = NativeHandler.Instance; + Assert.True( AGXUnity.Sensor.TestHelper.LidarRayPatternsAreFound() ); + } + } +} diff --git a/Tests/Runtime/DataTests.cs.meta b/Tests/Runtime/DataTests.cs.meta new file mode 100644 index 00000000..b7079b49 --- /dev/null +++ b/Tests/Runtime/DataTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c28f75074e15cdf46be083d7cc7b7e20 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: