diff --git a/AGXUnity/Collide/Shape.cs b/AGXUnity/Collide/Shape.cs index 54d3c53e..b4087c0d 100644 --- a/AGXUnity/Collide/Shape.cs +++ b/AGXUnity/Collide/Shape.cs @@ -153,6 +153,30 @@ public ShapeMaterial Material } } + [SerializeField] + private bool m_isPulley = false; + + /// + /// When enabled, Wires will snap onto this geometry to simulate a sheave or pulley geometry without having to rely on contacts + /// + [Tooltip( "When enabled, Wires will snap onto this geometry to simulate a sheave or pulley geometry without having to rely on contacts" )] + [InspectorGroupBegin( Name = "Extra Properties" )] + [InspectorGroupEnd] + public bool IsPulley + { + get => m_isPulley; + set + { + m_isPulley = value; + if ( NativeGeometry != null ) { + if ( m_isPulley ) + NativeGeometry.getPropertyContainer().addPropertyBool( "Pulley", true ); + else + NativeGeometry.getPropertyContainer().removePropertyBool( "Pulley" ); + } + } + } + /// /// Native geometry object, if initialized. /// diff --git a/AGXUnity/IO/OpenPLX/OpenPLXImporter.cs b/AGXUnity/IO/OpenPLX/OpenPLXImporter.cs index 79503e06..258e3dab 100644 --- a/AGXUnity/IO/OpenPLX/OpenPLXImporter.cs +++ b/AGXUnity/IO/OpenPLX/OpenPLXImporter.cs @@ -119,6 +119,7 @@ public UnityEngine.Object ImportOpenPLXFile( string path, string model = null ) else ErrorCallback?.Invoke(); + GC.Collect(); return importedObject; } diff --git a/AGXUnity/Model/DeformableTerrain.cs b/AGXUnity/Model/DeformableTerrain.cs index 7450e1bb..2b89c505 100644 --- a/AGXUnity/Model/DeformableTerrain.cs +++ b/AGXUnity/Model/DeformableTerrain.cs @@ -85,7 +85,7 @@ protected override bool Initialize() LicenseManager.LicenseInfo.HasModuleLogError( LicenseInfo.Module.AGXTerrain | LicenseInfo.Module.AGXGranular, this ); m_initialHeights = TerrainData.GetHeights( 0, 0, TerrainDataResolution, TerrainDataResolution ); - + Terrain.terrainData = Instantiate( Terrain.terrainData ); InitializeNative(); Simulation.Instance.StepCallbacks.PostStepForward += OnPostStepForward; @@ -144,13 +144,6 @@ private void ResetTerrainDataHeightsAndTransform() TerrainData.SetHeights( 0, 0, m_initialHeights ); transform.position = transform.position + MaximumDepth * Vector3.up; - -#if UNITY_EDITOR - // If the editor is closed during play the modified height - // data isn't saved, this resolves corrupt heights in such case. - UnityEditor.EditorUtility.SetDirty( TerrainData ); - UnityEditor.AssetDatabase.SaveAssets(); -#endif } private void OnPostStepForward() diff --git a/AGXUnity/Model/DeformableTerrainConnector.cs b/AGXUnity/Model/DeformableTerrainConnector.cs index 942ba262..71938237 100644 --- a/AGXUnity/Model/DeformableTerrainConnector.cs +++ b/AGXUnity/Model/DeformableTerrainConnector.cs @@ -26,6 +26,8 @@ public Vector3 GetOffsetPosition() if ( InitialHeights != null ) return needsReturnData ? Terrain.terrainData.GetHeights( 0, 0, resolution, resolution ) : null; + Terrain.terrainData = Instantiate( Terrain.terrainData ); + if ( float.IsNaN( MaximumDepth ) ) { Debug.LogError( "Writing terrain offset without first setting depth!" ); MaximumDepth = 0; @@ -41,12 +43,6 @@ internal void OnReset() transform.position += MaximumDepth * Vector3.up; Terrain.terrainData.SetHeights( 0, 0, InitialHeights ); -#if UNITY_EDITOR - // If the editor is closed during play the modified height - // data isn't saved, this resolves corrupt heights in such case. - UnityEditor.EditorUtility.SetDirty( Terrain.terrainData ); - UnityEditor.AssetDatabase.SaveAssets(); -#endif InitialHeights = null; } } diff --git a/AGXUnity/Model/DeformableTerrainPager.cs b/AGXUnity/Model/DeformableTerrainPager.cs index bbe19945..d4f0d5c1 100644 --- a/AGXUnity/Model/DeformableTerrainPager.cs +++ b/AGXUnity/Model/DeformableTerrainPager.cs @@ -411,8 +411,7 @@ private void InitializeNative() foreach ( var rb in m_rigidbodies ) Native.add( rb.Body.GetInitialized().Native, rb.requiredRadius, rb.preloadRadius ); - if ( MaterialPatches.Length != 0 ) - Debug.LogWarning( "Nonhomogenous terrain is not yet supported for DeformableTerrainPager.", this ); + GetSimulation().add( Native ); } protected override void OnDestroy() @@ -444,7 +443,7 @@ private void UpdateHeights() private void UpdateTerrain( agxTerrain.TerrainPager.TileAttachments tile ) { - var terrain = tile.m_terrainTile; + var terrain = tile.m_terrainTile.get(); var modifications = terrain.getModifiedVertices(); if ( modifications.Count == 0 ) return; @@ -456,12 +455,12 @@ private void UpdateTerrain( agxTerrain.TerrainPager.TileAttachments tile ) var result = new float[,] { { 0.0f } }; agx.Vec2i index = new agx.Vec2i(0,0); - Vector2Int tileIndex = GetTileIndex(terrain.get()); + Vector2Int tileIndex = GetTileIndex(terrain); UnityTerrainAdapter.UnityModificationCallback modCallbackFn = ( Terrain tile, Vector2Int unityIndex ) => { tile.terrainData.SetHeightsDelayLOD( unityIndex.x, unityIndex.y, result ); - OnModification?.Invoke( terrain.get(), index, tile, unityIndex ); + OnModification?.Invoke( terrain, index, tile, unityIndex ); m_updatedTerrains.Add( tile ); }; @@ -845,17 +844,33 @@ public override void TriggerModifyAllCells() public override bool ReplaceTerrainMaterial( DeformableTerrainMaterial oldMat, DeformableTerrainMaterial newMat ) { - throw new NotImplementedException( "Terrain pager does not yet support Inhomogeneous terrain" ); + if ( Native == null ) + return true; + + if ( oldMat == null || newMat == null ) + return false; + + var success = m_terrainDataSource.exchangeTerrainMaterial(oldMat.GetInitialized().Native, newMat.GetInitialized().Native); + return success; } public override void SetAssociatedMaterial( DeformableTerrainMaterial terrMat, ShapeMaterial shapeMat ) { - throw new NotImplementedException( "Terrain pager does not yet support Inhomogeneous terrain" ); + if ( Native == null ) + return; + + m_terrainDataSource.setAssociatedMaterial( shapeMat.GetInitialized().Native, terrMat.GetInitialized().Native ); } public override void AddTerrainMaterial( DeformableTerrainMaterial terrMat, Shape shape = null ) { - throw new NotImplementedException( "Terrain pager does not yet support Inhomogeneous terrain" ); + if ( Native == null ) + return; + + if ( shape != null ) + m_terrainDataSource.addTerrainMaterial( terrMat.GetInitialized().Native, shape.GetInitialized().NativeGeometry ); + else + m_terrainDataSource.addTerrainMaterial( terrMat.GetInitialized().Native ); } protected override bool IsNativeNull() { return Native == null; } @@ -867,8 +882,9 @@ protected override void SetShapeMaterial( agx.Material material, agxTerrain.Terr protected override void SetTerrainMaterial( agxTerrain.TerrainMaterial material ) { - Native?.getTemplateTerrain().setTerrainMaterial( material ); - OnPropertiesUpdated(); + m_terrainDataSource.setDefaultTerrainMaterial( material ); + if ( Material != null ) + m_terrainDataSource.setAssociatedMaterial( Material.Native, material ); } protected override void SetEnable( bool enable ) diff --git a/AGXUnity/Model/MovableTerrain.cs b/AGXUnity/Model/MovableTerrain.cs index f69358d8..2c265fcd 100644 --- a/AGXUnity/Model/MovableTerrain.cs +++ b/AGXUnity/Model/MovableTerrain.cs @@ -450,6 +450,15 @@ public override void EditorUpdate() #endif } + protected override bool PerformMigration() + { + if ( m_serializationVersion < 2 ) { + PlacementMode = Placement.Manual; + return true; + } + return false; + } + protected override bool Initialize() { // Only printing the errors if something is wrong. diff --git a/AGXUnity/Model/TerrainMaterialPatch.cs b/AGXUnity/Model/TerrainMaterialPatch.cs index 035b92eb..e6365b48 100644 --- a/AGXUnity/Model/TerrainMaterialPatch.cs +++ b/AGXUnity/Model/TerrainMaterialPatch.cs @@ -38,7 +38,11 @@ public DeformableTerrainMaterial TerrainMaterial return; } - if ( (bool)ParentTerrain?.ReplaceTerrainMaterial( m_terrainMaterial, value ) ) + if ( m_terrainMaterial == null ) { + ParentTerrain?.AddTerrainMaterial( value ); + m_terrainMaterial = value; + } + else if ( (bool)ParentTerrain?.ReplaceTerrainMaterial( m_terrainMaterial, value ) ) m_terrainMaterial = value; } } @@ -51,7 +55,8 @@ public DeformableTerrainMaterial TerrainMaterial public void AddShape( Shape shape ) { shape.enabled &= !DisableShapes; - shape.Visual.GetComponent().enabled &= !DisableVisuals; + if ( shape.Visual != null ) + shape.Visual.GetComponent().enabled &= !DisableVisuals; ParentTerrain?.AddTerrainMaterial( m_terrainMaterial, shape.GetInitialized() ); } diff --git a/AGXUnity/Model/Track.cs b/AGXUnity/Model/Track.cs index 038e4623..ca16ef72 100644 --- a/AGXUnity/Model/Track.cs +++ b/AGXUnity/Model/Track.cs @@ -1,3 +1,4 @@ +using AGXUnity.Collide; using AGXUnity.Utils; using agxVehicle; using System.Collections.Generic; @@ -266,19 +267,7 @@ public ShapeMaterial Material /// Registered track wheel instances. /// [HideInInspector] - public TrackWheel[] Wheels - { - get { return m_wheels.ToArray(); } - } - - protected override bool PerformMigration() - { - if ( m_serializationVersion < 2 ) { - FullDoF = true; - return true; - } - return false; - } + public TrackWheel[] Wheels => m_wheels.ToArray(); /// /// Associate track wheel instance to this track. @@ -313,18 +302,77 @@ public bool Remove( TrackWheel wheel ) /// /// Track wheel instance. /// True if is associated to this track. - public bool Contains( TrackWheel wheel ) + public bool Contains( TrackWheel wheel ) => m_wheels.Contains( wheel ); + + /// + /// Verifies so that all added track wheels still exists. Wheels that + /// has been deleted are removed. + /// + public void RemoveInvalidWheels() => m_wheels.RemoveAll( wheel => wheel == null ); + + [SerializeField] + private List m_supportGeometries = new List(); + + /// + /// Registered support geometry instances. + /// + [HideInInspector] + public Shape[] SupportGeometries => m_supportGeometries.ToArray(); + + /// + /// Associate support geometry instance to this track. + /// + /// support geometry instance to add. + /// True if added, false if null or already added. + public bool Add( Shape supportGeometry ) { - return m_wheels.Contains( wheel ); + if ( supportGeometry == null || m_supportGeometries.Contains( supportGeometry ) ) + return false; + + m_supportGeometries.Add( supportGeometry ); + + if ( Native != null ) + Native.addSupportGroupId( supportGeometry.GetInitialized().NativeGeometry ); + + return true; } /// - /// Verifies so that all added track wheels still exists. Wheels that + /// Disassociate support geometry instance from this track. + /// + /// Support geometry instance to remove. + /// True if removed, false if null or not associated to this track. + public bool Remove( Shape supportGeometry ) + { + if ( supportGeometry == null ) + return false; + + if ( Native != null ) + Native.removeSupportGroupId( supportGeometry.GetInitialized().NativeGeometry ); + + return m_supportGeometries.Remove( supportGeometry ); + } + + /// + /// True if is associated to this track. + /// + /// Support geometry instance. + /// True if is associated to this track. + public bool Contains( Shape supportGeometry ) => m_supportGeometries.Contains( supportGeometry ); + + /// + /// Verifies so that all added support geometries still exists. Geometries that /// has been deleted are removed. /// - public void RemoveInvalidWheels() + public void RemoveInvalidSupportGeometries() => m_supportGeometries.RemoveAll( geom => geom == null ); + + protected override bool PerformMigration() { - m_wheels.RemoveAll( wheel => wheel == null ); + if ( m_serializationVersion < 2 ) { + FullDoF = true; + return true; + } + return false; } private class OnInitializeAdapter : TrackNodeOnInitializeCallback @@ -354,6 +402,7 @@ protected override bool Initialize() return false; RemoveInvalidWheels(); + RemoveInvalidSupportGeometries(); if ( m_wheels.Count == 0 ) { Debug.LogError( "Component: Track requires at least one wheel to initialize.", this ); @@ -389,6 +438,9 @@ protected override bool Initialize() foreach ( var wheel in Wheels ) Native.add( wheel.Native ); + foreach ( var geom in SupportGeometries ) + Native.addSupportGroupId( geom.GetInitialized().NativeGeometry ); + if ( WidthVariation != null || ThicknessVariation != null ) Native.initialize( new OnInitializeAdapter( Width, Thickness, WidthVariation, ThicknessVariation ) ); else diff --git a/AGXUnity/Model/TrackProperties.cs b/AGXUnity/Model/TrackProperties.cs index c3a434d4..1ab727f8 100644 --- a/AGXUnity/Model/TrackProperties.cs +++ b/AGXUnity/Model/TrackProperties.cs @@ -37,6 +37,8 @@ protected override bool PerformMigration() HingeStiffnessRotational = new Vector3( convertCompliance( m_hingeComplianceRotational.x ), convertCompliance( m_hingeComplianceRotational.y ), 100.0f ); HingeAttenuationRotational = new Vector3( convertDamping( m_hingeDampingRotational.x ), convertDamping( m_hingeDampingRotational.y ), 2.0f ); + FullDoF = true; + return true; } diff --git a/AGXUnity/Rendering/TerrainPatchRenderer.cs b/AGXUnity/Rendering/TerrainPatchRenderer.cs index b3220f99..2188198a 100644 --- a/AGXUnity/Rendering/TerrainPatchRenderer.cs +++ b/AGXUnity/Rendering/TerrainPatchRenderer.cs @@ -7,28 +7,12 @@ namespace AGXUnity.Rendering { - /// - /// Wrapper class for storing/resetting initial state of TerrainData. - /// This is by no means a complete store/restore, only the parts used by . - /// - class InitialTerrainData + class TileData { - private float[,,] m_alphamaps; - private TerrainLayer[] m_layers; - - public InitialTerrainData( TerrainData td ) - { - m_alphamaps = td.GetAlphamaps( 0, 0, td.alphamapWidth, td.alphamapHeight ); - m_layers = td.terrainLayers; - } - - public void Reset( TerrainData td ) - { - if ( td != null ) { - td.terrainLayers = m_layers; - td.SetAlphamaps( 0, 0, m_alphamaps ); - } - } + public float[,,] alphamap; + public Dictionary m_materialMapping; + public int m_layerCount; + public int m_defaultLayerIndex; } [RequireComponent( typeof( DeformableTerrainBase ) )] @@ -38,11 +22,9 @@ public void Reset( TerrainData td ) public class TerrainPatchRenderer : ScriptComponent { private DeformableTerrainBase terrain; - private float[,,] alphamap; - private Dictionary m_materialMapping; - private InitialTerrainData m_initialData; - private int m_layerCount; - private int m_defaultLayerIndex; + + private Dictionary m_perTileData; + private List m_shouldUpdate; [SerializeField] private TerrainLayer m_defaultLayer; @@ -57,7 +39,7 @@ public TerrainLayer DefaultLayer get => m_defaultLayer; set { - if ( m_initialData != null ) + if ( State == States.INITIALIZED ) Debug.LogError( "Setting material TerrainLayers during runtime is not supported!" ); else m_defaultLayer = value; @@ -78,7 +60,7 @@ public SerializableDictionary ExplicitM get => m_explicitMaterialRenderMap; set { - if ( m_initialData != null ) + if ( State == States.INITIALIZED ) Debug.LogError( "Setting material TerrainLayers during runtime is not supported!" ); else m_explicitMaterialRenderMap = value; @@ -110,37 +92,18 @@ public Dictionary MaterialRenderMap public TerrainMaterialPatch[] RenderedPatches => gameObject.GetComponentsInChildren(); - protected override bool Initialize() + private void InitializeTile( Terrain tile ) { - terrain = gameObject.GetInitializedComponent(); - if ( terrain is not DeformableTerrain ) { - Debug.LogError( "Terrain Patch Renderer currently only supports DeformableTerrain!", this ); - return false; - } - - // The patches need to be initialized before the initial update pass, otherwise the materials might not yet have been added. - foreach ( var patch in RenderedPatches ) - patch.GetInitialized(); - - var uTerr = GetComponent(); - var td = uTerr.terrainData; - - m_initialData = new InitialTerrainData( td ); + TileData tileData = new TileData(); + var td = tile.terrainData; var layers = td.terrainLayers.ToList(); - if ( DefaultLayer == null ) { - Debug.LogWarning( "No DefaultLayer provided. Using first layer present in terrain.", this ); - m_defaultLayer = td.terrainLayers[ 0 ]; - m_defaultLayerIndex = 0; - } - else { - if ( !layers.Contains( DefaultLayer ) ) - layers.Add( DefaultLayer ); - m_defaultLayerIndex = layers.IndexOf( DefaultLayer ); - } + if ( !layers.Contains( DefaultLayer ) ) + layers.Add( DefaultLayer ); + tileData.m_defaultLayerIndex = layers.IndexOf( DefaultLayer ); // Initialize terrain layers: 0 is default, 1+ are mapped. - m_materialMapping = new Dictionary(); + tileData.m_materialMapping = new Dictionary(); foreach ( var (mat, tl) in MaterialRenderMap ) { var terrMat = mat.GetInitialized().Native; if ( terrMat != null ) { @@ -151,34 +114,50 @@ protected override bool Initialize() if ( !layers.Contains( tl ) ) layers.Add( tl ); - m_materialMapping.Add( mat.GetInitialized().Native, layers.IndexOf( tl ) ); + tileData.m_materialMapping.Add( mat.GetInitialized().Native, layers.IndexOf( tl ) ); } } td.terrainLayers = layers.ToArray(); - m_layerCount = layers.Count; + tileData.m_layerCount = layers.Count; - alphamap = td.GetAlphamaps( 0, 0, td.alphamapWidth, td.alphamapHeight ); + tileData.alphamap = td.GetAlphamaps( 0, 0, td.alphamapWidth, td.alphamapHeight ); - Simulation.Instance.StepCallbacks.SimulationPost += PostStep; - terrain.OnModification += UpdateTextureAt; + m_perTileData.Add( tile, tileData ); + } - terrain.TriggerModifyAllCells(); + protected override bool Initialize() + { + terrain = gameObject.GetInitializedComponent(); + if ( terrain is MovableTerrain ) { + Debug.LogError( "Terrain Patch Renderer does not support MovableTerrain!", this ); + return false; + } - td.SetAlphamaps( 0, 0, alphamap ); + // The patches need to be initialized before the initial update pass, otherwise the materials might not yet have been added. + foreach ( var patch in RenderedPatches ) + patch.GetInitialized(); - return true; - } + var uTerr = GetComponent(); + var td = uTerr.terrainData; - protected override void OnApplicationQuit() - { - m_initialData?.Reset( GetComponent().terrainData ); - } + m_perTileData = new Dictionary(); + m_shouldUpdate = new List(); - protected override void OnDestroy() - { - m_initialData?.Reset( GetComponent().terrainData ); + if ( DefaultLayer == null ) { + Debug.LogWarning( "No DefaultLayer provided. Using first layer present in terrain.", this ); + m_defaultLayer = td.terrainLayers[ 0 ]; + } + + Simulation.Instance.StepCallbacks.SimulationPost += PostStep; + terrain.OnModification += UpdateTextureAt; + + if ( terrain is DeformableTerrain ) { + InitializeTile( uTerr ); + terrain.TriggerModifyAllCells(); + td.SetAlphamaps( 0, 0, m_perTileData[ uTerr ].alphamap ); + } - base.OnDestroy(); + return true; } protected override void OnDisable() @@ -202,13 +181,20 @@ protected override void OnEnable() private void PostStep() { - var td = GetComponent().terrainData; + foreach ( var terr in m_shouldUpdate ) + terr.terrainData.SetAlphamaps( 0, 0, m_perTileData[ terr ].alphamap ); - td.SetAlphamaps( 0, 0, alphamap ); + m_shouldUpdate.Clear(); } private void UpdateTextureAt( agxTerrain.Terrain aTerr, agx.Vec2i aIdx, Terrain uTerr, Vector2Int uIdx ) { + if ( !m_perTileData.ContainsKey( uTerr ) ) + InitializeTile( uTerr ); + + if ( !m_shouldUpdate.Contains( uTerr ) ) + m_shouldUpdate.Add( uTerr ); + var td = uTerr.terrainData; var alphamapRes = td.alphamapResolution; var heightsRes = td.heightmapResolution - 1; @@ -216,9 +202,11 @@ private void UpdateTextureAt( agxTerrain.Terrain aTerr, agx.Vec2i aIdx, Terrain var modPos = aTerr.getSurfacePositionWorld( aIdx ); var mat = aTerr.getTerrainMaterial( modPos ); - var index = m_materialMapping.GetValueOrDefault(mat,m_defaultLayerIndex); + var data = m_perTileData[uTerr]; + + var index = data.m_materialMapping.GetValueOrDefault(mat,data.m_defaultLayerIndex); - if ( index == m_defaultLayerIndex && State != States.INITIALIZED ) + if ( index == data.m_defaultLayerIndex && State != States.INITIALIZED ) return; var modAlphaX = Mathf.RoundToInt((uIdx.x - 0.5f)/heightsRes * alphamapRes); @@ -232,8 +220,8 @@ private void UpdateTextureAt( agxTerrain.Terrain aTerr, agx.Vec2i aIdx, Terrain for ( int x = modAlphaX; x < modAlphaXend; x++ ) { if ( x < 0 || x >= alphamapRes ) continue; - for ( int i = 0; i < m_layerCount; i++ ) - alphamap[ y, x, i ] = i == index ? 1.0f : 0.0f; + for ( int i = 0; i < data.m_layerCount; i++ ) + data.alphamap[ y, x, i ] = i == index ? 1.0f : 0.0f; } } } diff --git a/AGXUnity/RigidBody.cs b/AGXUnity/RigidBody.cs index 0fe697b2..b90b32dc 100644 --- a/AGXUnity/RigidBody.cs +++ b/AGXUnity/RigidBody.cs @@ -269,6 +269,7 @@ public void UpdateMassProperties() } MassProperties.SetDefaultCalculated( rb ); + PropertySynchronizer.Synchronize( MassProperties ); } ); } diff --git a/AGXUnity/ScriptAsset.cs b/AGXUnity/ScriptAsset.cs index 4dc288cc..2dc9f857 100644 --- a/AGXUnity/ScriptAsset.cs +++ b/AGXUnity/ScriptAsset.cs @@ -24,11 +24,9 @@ public abstract class ScriptAsset : ScriptableObject, IPropertySynchronizable, I private const int CurrentSerializationVersion = 2; // Serialization version is currently unused -#pragma warning disable 0414 [SerializeField] [HideInInspector] protected int m_serializationVersion = -1; -#pragma warning restore 0414 public void OnBeforeSerialize() { diff --git a/AGXUnity/ScriptComponent.cs b/AGXUnity/ScriptComponent.cs index d90df71e..e78906b5 100644 --- a/AGXUnity/ScriptComponent.cs +++ b/AGXUnity/ScriptComponent.cs @@ -40,6 +40,7 @@ protected ScriptComponent() // 1 - Migrate shovel cutting direction to tooth direction // 2 - Migrate Range, Beam Divergence, and Beam Exit Radius into the LiDAR model // - Migrate old tracks into FullDoF tracks + // - Add automatic positioning mode private const int CurrentSerializationVersion = 2; [SerializeField] diff --git a/Editor/AGXUnityEditor/Tools/BuiltInToolsTool.cs b/Editor/AGXUnityEditor/Tools/BuiltInToolsTool.cs index 324483c3..76a19033 100644 --- a/Editor/AGXUnityEditor/Tools/BuiltInToolsTool.cs +++ b/Editor/AGXUnityEditor/Tools/BuiltInToolsTool.cs @@ -83,7 +83,7 @@ public BuiltInToolsTool() AddKeyHandler( "SelectObject", SelectGameObjectKeyHandler ); AddKeyHandler( "SelectRigidBody", SelectRigidBodyKeyHandler ); AddKeyHandler( "PickHandler", PickHandlerKeyHandler ); -#if UNITY_6000_3_OR_NEWER +#if UNITY_6000_4_OR_NEWER EditorApplication.hierarchyWindowItemByEntityIdOnGUI += HandleHierarchyDragDrop; #else EditorApplication.hierarchyWindowItemOnGUI += HandleHierarchyDragDrop; @@ -227,7 +227,7 @@ private static void AssignMaterial( GameObject go, AGXUnity.ShapeMaterial materi assignAll(); } -#if UNITY_6000_3_OR_NEWER +#if UNITY_6000_4_OR_NEWER private void HandleHierarchyDragDrop( EntityId entityId, Rect pos ) { InspectorGUI.HandleDragDrop( pos, @@ -239,7 +239,8 @@ private void HandleHierarchyDragDrop( EntityId entityId, Rect pos ) material ); } ); } -#else +#else +#pragma warning disable CS0618 private void HandleHierarchyDragDrop( int instanceId, Rect pos ) { InspectorGUI.HandleDragDrop( pos, @@ -251,6 +252,7 @@ private void HandleHierarchyDragDrop( int instanceId, Rect pos ) material ); } ); } +#pragma warning restore #endif private void HandleSceneViewDragDrop( Event current, SceneView sceneView ) diff --git a/Editor/AGXUnityEditor/Tools/ContactMaterialManagerTool.cs b/Editor/AGXUnityEditor/Tools/ContactMaterialManagerTool.cs index 436ea79f..cf1369c0 100644 --- a/Editor/AGXUnityEditor/Tools/ContactMaterialManagerTool.cs +++ b/Editor/AGXUnityEditor/Tools/ContactMaterialManagerTool.cs @@ -53,7 +53,7 @@ private Action PreContactMaterialEditor( ContactMaterialEn entries[ index ].IsOriented ); if ( entries[ index ].IsOriented ) { using ( InspectorGUI.IndentScope.Single ) { - if ( entries[ index ].ContactMaterial.FrictionModel.TrackFrictionModel ) { + if ( entries[ index ].ContactMaterial.FrictionModel != null && entries[ index ].ContactMaterial.FrictionModel.TrackFrictionModel ) { EditorGUILayout.HelpBox( "Contact Materials with a Track friction model cannot be oriented. " + "These models automatically construct the friction frame from the tracks. " + "This option will be ignored", diff --git a/Editor/AGXUnityEditor/Tools/TrackTool.cs b/Editor/AGXUnityEditor/Tools/TrackTool.cs index 04156e89..fa769571 100644 --- a/Editor/AGXUnityEditor/Tools/TrackTool.cs +++ b/Editor/AGXUnityEditor/Tools/TrackTool.cs @@ -18,6 +18,7 @@ public TrackTool( Object[] targets ) public override void OnAdd() { Track.RemoveInvalidWheels(); + Track.RemoveInvalidSupportGeometries(); } public override void OnRemove() @@ -28,6 +29,7 @@ public override void OnRemove() public override void OnPreTargetMembersGUI() { Track.RemoveInvalidWheels(); + Track.RemoveInvalidSupportGeometries(); bool toggleSelectWheel = false; bool toggleDisableCollisions = false; @@ -78,6 +80,14 @@ public override void OnPostTargetMembersGUI() "Wheels", wheel => Track.Add( wheel ), wheel => Track.Remove( wheel ) ); + + if ( !Track.FullDoF ) { + InspectorGUI.ToolListGUI( this, + Track.SupportGeometries, + "Support Geometries", + geom => Track.Add( geom ), + geom => Track.Remove( geom ) ); + } } private TrackNodeVariation VariationGUI( string name, TrackNodeVariation variation ) diff --git a/Plugins/x86_64/agxDotNet.dll b/Plugins/x86_64/agxDotNet.dll index 51be13f1..42dc4f39 100644 Binary files a/Plugins/x86_64/agxDotNet.dll and b/Plugins/x86_64/agxDotNet.dll differ diff --git a/Tests/Runtime/ImuAndEncoderTests.cs b/Tests/Runtime/ImuAndEncoderTests.cs index 9d6223ac..ac6b2259 100644 --- a/Tests/Runtime/ImuAndEncoderTests.cs +++ b/Tests/Runtime/ImuAndEncoderTests.cs @@ -6,59 +6,15 @@ using UnityEngine; using UnityEngine.TestTools; -using GOList = System.Collections.Generic.List; - namespace AGXUnityTesting.Runtime { public class ImuAndEncoderTests : AGXUnityFixture { - private GOList m_keep = new GOList(); - - [OneTimeSetUp] + [SetUp] public void SetupSensorScene() { - Simulation.Instance.PreIntegratePositions = true; - m_keep.Add( Simulation.Instance.gameObject ); - SensorEnvironment.Instance.FieldType = SensorEnvironment.MagneticFieldType.Uniform; SensorEnvironment.Instance.MagneticFieldVector = Vector3.one; - m_keep.Add( SensorEnvironment.Instance.gameObject ); - } - - [UnityTearDown] - public IEnumerator CleanSensorScene() - { -#if UNITY_2022_2_OR_NEWER - var objects = Object.FindObjectsByType( FindObjectsSortMode.None ); -#else - var objects = Object.FindObjectsOfType( ); -#endif - GOList toDestroy = new GOList(); - - foreach ( var obj in objects ) { - var root = obj.gameObject; - while ( root.transform.parent != null ) - root = root.transform.parent.gameObject; - if ( !m_keep.Contains( root ) ) - toDestroy.Add( root ); - } - - yield return TestUtils.DestroyAndWait( toDestroy.ToArray() ); - } - - [OneTimeTearDown] - public void TearDownSensorScene() - { -#if UNITY_2022_2_OR_NEWER - var geoms = Object.FindObjectsByType( FindObjectsSortMode.None ); -#else - var geoms = Object.FindObjectsOfType( ); -#endif - - foreach ( var g in geoms ) - GameObject.Destroy( g.gameObject ); - - GameObject.Destroy( SensorEnvironment.Instance.gameObject ); } private (AGXUnity.RigidBody, ImuSensor) CreateDefaultTestImu( Vector3 position = default ) diff --git a/Tests/Runtime/LidarTests.cs b/Tests/Runtime/LidarTests.cs index c9cc6c27..0413d586 100644 --- a/Tests/Runtime/LidarTests.cs +++ b/Tests/Runtime/LidarTests.cs @@ -9,14 +9,11 @@ using UnityEngine; using UnityEngine.TestTools; -using GOList = System.Collections.Generic.List; - namespace AGXUnityTesting.Runtime { + [Ignore( "TEMP: No runners have sufficient GPU support currently" )] public class LidarTests : AGXUnityFixture { - private GOList m_keep = new GOList(); - private GameObject CreateShape( Vector3 transform = new Vector3() ) where T : Shape { @@ -43,57 +40,18 @@ private LidarSurfaceMaterialLambertianOpaque AddLambertianMaterial( GameObject o return material; } - [OneTimeSetUp] + [SetUp] public void SetupLidarScene() { - m_keep.Add( CreateShape( new Vector3( 3, 0, 3 ) ) ); - m_keep.Add( CreateShape( new Vector3( -3, 0, 3 ) ) ); - m_keep.Add( CreateShape( new Vector3( -3, 0, -3 ) ) ); - m_keep.Add( CreateShape( new Vector3( 3, 0, -3 ) ) ); - Simulation.Instance.PreIntegratePositions = true; - m_keep.Add( Simulation.Instance.gameObject ); - m_keep.Add( SensorEnvironment.Instance.gameObject ); + CreateShape( new Vector3( 3, 0, 3 ) ); + CreateShape( new Vector3( -3, 0, 3 ) ); + CreateShape( new Vector3( -3, 0, -3 ) ); + CreateShape( new Vector3( 3, 0, -3 ) ); // Lidar ray intervals are sensitive to time step so ensure that the timestep is exact here Simulation.Instance.Native.setTimeStep( 0.02 ); } - [UnityTearDown] - public IEnumerator CleanLidarScene() - { -#if UNITY_2022_2_OR_NEWER - var objects = Object.FindObjectsByType( FindObjectsSortMode.None ); -#else - var objects = Object.FindObjectsOfType( ); -#endif - GOList toDestroy = new GOList(); - - foreach ( var obj in objects ) { - var root = obj.gameObject; - while ( root.transform.parent != null ) - root = root.transform.parent.gameObject; - if ( !m_keep.Contains( root ) ) - toDestroy.Add( root ); - } - - yield return TestUtils.DestroyAndWait( toDestroy.ToArray() ); - } - - [OneTimeTearDown] - public void TearDownLidarScene() - { -#if UNITY_2022_2_OR_NEWER - var geoms = Object.FindObjectsByType( FindObjectsSortMode.None ); -#else - var geoms = Object.FindObjectsOfType( ); -#endif - - foreach ( var g in geoms ) - GameObject.Destroy( g.gameObject ); - - GameObject.Destroy( SensorEnvironment.Instance.gameObject ); - } - private (LidarSensor, GenericSweepData) CreateDefaultTestLidar( Vector3 position = default ) { var lidarGO = new GameObject("Lidar"); diff --git a/Tests/Runtime/MovableTerrainTests.cs b/Tests/Runtime/MovableTerrainTests.cs index d687f647..eeb002da 100644 --- a/Tests/Runtime/MovableTerrainTests.cs +++ b/Tests/Runtime/MovableTerrainTests.cs @@ -3,7 +3,6 @@ using AGXUnity.Model; using NUnit.Framework; using System.Collections; -using System.Linq; using System.Text.RegularExpressions; using UnityEngine; using UnityEngine.TestTools; @@ -12,21 +11,6 @@ namespace AGXUnityTesting.Runtime { public class MovableTerrainTests : AGXUnityFixture { - - [UnityTearDown] - public IEnumerator CleanScene() - { -#if UNITY_2022_2_OR_NEWER - var objects = Object.FindObjectsByType( FindObjectsSortMode.None ); -#else - var objects = Object.FindObjectsOfType( ); -#endif - - var toDelete = objects.Where(x => x is not Simulation).Select(x => x.gameObject).ToArray(); - - yield return TestUtils.DestroyAndWait( toDelete ); - } - [Test] public void TestCreateMovable() { diff --git a/Tests/Runtime/OpenPLXTests.cs b/Tests/Runtime/OpenPLXTests.cs index 30bf3189..bb3b4626 100644 --- a/Tests/Runtime/OpenPLXTests.cs +++ b/Tests/Runtime/OpenPLXTests.cs @@ -34,12 +34,6 @@ private void AssertAnglesEqual( double expected, double actual, double delta, st Assert.AreEqual( expected, normalized, delta, message ); } - [UnityTearDown] - public IEnumerator RemoveLoadedObjects() - { - yield return TestUtils.DestroyAndWait( Object.FindObjectsByType( FindObjectsSortMode.None ).Select( r => r.gameObject ).ToArray() ); - } - public OpenPLXRoot LoadOpenPLX( string source, string modelName = null ) { var openPLXObj = OpenPLXImporter.ImportOpenPLXFile( System.IO.Path.Combine( TestDataFolder, source ), default, null, modelName ); @@ -651,6 +645,7 @@ public void TestValidLidarOK() } [Test] + [Ignore( "TEMP: No runners have sufficient GPU support currently" )] public void TestDoubleDistanceDistortionLidarError() { var go = OpenPLXImporter.ImportOpenPLXFile( diff --git a/Tests/Runtime/OrientedFrictionTests.cs b/Tests/Runtime/OrientedFrictionTests.cs index 69f3b453..4334f7c3 100644 --- a/Tests/Runtime/OrientedFrictionTests.cs +++ b/Tests/Runtime/OrientedFrictionTests.cs @@ -2,7 +2,6 @@ using AGXUnity.Collide; using NUnit.Framework; using System.Collections; -using System.Linq; using UnityEngine; using UnityEngine.TestTools; @@ -38,16 +37,6 @@ public IEnumerator SetupFrictionScene() yield return TestUtils.WaitUntilLoaded(); } - [UnityTearDown] - public IEnumerator TearDownFrictionScene() - { - var objects = Object.FindObjectsByType( FindObjectsSortMode.None ); - - var toDelete = objects.Where(x => x is not Simulation).Select(x => x.gameObject).ToArray(); - - yield return TestUtils.DestroyAndWait( toDelete ); - } - private (ShapeMaterial, RigidBody, Shape, ObserverFrame) CreateSlidingBox() { var rbGO = new GameObject("Sliding box"); diff --git a/Tests/Runtime/RuntimeFixture.cs b/Tests/Runtime/RuntimeFixture.cs index 0b2481aa..d35113fa 100644 --- a/Tests/Runtime/RuntimeFixture.cs +++ b/Tests/Runtime/RuntimeFixture.cs @@ -1,5 +1,6 @@ using AGXUnity; -using NUnit.Framework; +using System.Collections; +using System.Linq; using UnityEngine; using UnityEngine.TestTools; @@ -9,8 +10,8 @@ namespace AGXUnityTesting.Runtime { public class AGXUnityFixture { - [OneTimeSetUp] - public void SetupSimulationInstance() + [UnitySetUp] + public IEnumerator SetupSimulationInstance() { Simulation.Instance.GetInitialized(); Simulation.Instance.PreIntegratePositions = true; @@ -18,13 +19,14 @@ public void SetupSimulationInstance() Simulation.Instance.AGXUnityLogLevel = LogLevel.Warning; Simulation.Instance.LogToUnityConsole = true; Simulation.Instance.AutoSteppingMode = Simulation.AutoSteppingModes.Disabled; + + yield return TestUtils.WaitUntilLoaded(); } - [OneTimeTearDown] - public void TeardownSimulationInstance() + [UnityTearDown] + public IEnumerator TeardownSimulationInstance() { - GameObject.Destroy( ContactMaterialManager.Instance.gameObject ); - GameObject.Destroy( Simulation.Instance.gameObject ); + yield return TestUtils.DestroyAndWait( Object.FindObjectsByType( FindObjectsSortMode.None ).Select( c => c.gameObject ).ToArray() ); } } } diff --git a/Tests/Runtime/TerrainTests.cs b/Tests/Runtime/TerrainTests.cs index 64a24278..97b9bf22 100644 --- a/Tests/Runtime/TerrainTests.cs +++ b/Tests/Runtime/TerrainTests.cs @@ -1,8 +1,10 @@ using AGXUnity; +using AGXUnity.Collide; using AGXUnity.Model; - using NUnit.Framework; +using System; using System.Collections; +using System.Reflection; using UnityEngine; using UnityEngine.TestTools; @@ -50,12 +52,6 @@ public IEnumerator SetupTerrainScene() yield return TestUtils.WaitUntilLoaded(); } - [UnityTearDown] - public IEnumerator TearDownTerrainScene() - { - yield return TestUtils.DestroyAndWait( unityTerrain.gameObject ); - } - [Test] public void TestTerrainGetSingleHeight() { @@ -158,9 +154,48 @@ public void TestClampedTerrainGivesWarning() LogAssert.Expect( LogType.Warning, $"Terrain heights were clamped! Max allowed: 10, Max Encountered: 15 and AGXUnity.Model.DeformableTerrain.MaximumDepth = 10. Resolve this by increasing max height and lower the terrain or decrease Maximum Depth." ); testTerrain.GetInitialized(); } + + [UnityTest] + [TestMustExpectAllLogs( false )] + public IEnumerator TestPagerDifferentMaterials() + { + var patchGO = new GameObject("Material Patch"); + var patch = patchGO.AddComponent(); + + var patchGeom = new GameObject("Patch Geometry"); + patchGeom.AddComponent().HalfExtents = new Vector3( 2.5f, 2, 2.5f ); + + patchGeom.transform.parent = patchGO.transform; + patchGO.transform.parent = testTerrain.transform; + patchGO.transform.position = new Vector3( 15, 2, 15 ); + + var patchTM = DeformableTerrainMaterial.CreateInstance(); + patchTM.name = "Patch terrain material"; + patchTM.GetInitialized().Native.setDescription( "Material patch Terrain Material" ); + var defaultTM = DeformableTerrainMaterial.CreateInstance(); + defaultTM.name = "Default terrain material"; + defaultTM.GetInitialized().Native.setDescription( "Default Terrain Material" ); + + patch.TerrainMaterial = patchTM; + testTerrain.DefaultTerrainMaterial = defaultTM; + + yield return TestUtils.SimulateSeconds( 0.5f ); + + var tile = testTerrain.Native; + + var probedMat = tile.getTerrainMaterial( tile.getWorldPositionFromVoxelIndex( tile.getSurfaceVoxelIndexFromTerrainIndex( new agx.Vec2i( 15, 15 ) ) ) ); + Assert.That( probedMat, Is.EqualTo( patchTM.Native ) ); + + probedMat = tile.getTerrainMaterial( tile.getWorldPositionFromVoxelIndex( tile.getSurfaceVoxelIndexFromTerrainIndex( new agx.Vec2i( 2, 2 ) ) ) ); + Assert.That( probedMat.getDescription(), Is.EqualTo( defaultTM.Native.getDescription() ) ); + } } + + public class PagerTests : AGXUnityFixture { + private class NoInitAttribute : Attribute { } + private DeformableTerrainPager testTerrain; private Terrain unityTerrain; private GameObject pagerProbe; @@ -203,16 +238,12 @@ public IEnumerator SetupTerrainScene() testTerrain.Add( pagerProbe.GetComponent() ); - // Ensure that the middle tile is paged in - yield return TestUtils.SimulateSeconds( 0.2f ); - } + var method = this.GetType().GetMethod( TestContext.CurrentContext.Test.MethodName ); - [UnityTearDown] - public IEnumerator TearDownTerrainScene() - { - GameObject.Destroy( unityTerrain.gameObject ); - GameObject.Destroy( pagerProbe ); - yield return null; + if ( method.GetCustomAttribute() == null ) { + // Ensure that the middle tile is paged in + yield return TestUtils.SimulateSeconds( 0.2f ); + } } [Test] @@ -316,5 +347,41 @@ public IEnumerator TestPagerReset() for ( int x = 0; x < 10; x++ ) Assert.AreEqual( initial[ y, x ], results[ y, x ], HEIGHT_DELTA ); } + + [UnityTest] + [NoInit] + [TestMustExpectAllLogs( false )] + public IEnumerator TestPagerDifferentMaterials() + { + var patchGO = new GameObject("Material Patch"); + var patch = patchGO.AddComponent(); + + var patchGeom = new GameObject("Patch Geometry"); + patchGeom.AddComponent().HalfExtents = new Vector3( 2.5f, 2, 2.5f ); + + patchGeom.transform.parent = patchGO.transform; + patchGO.transform.parent = testTerrain.transform; + patchGO.transform.position = new Vector3( 15, 2, 15 ); + + var patchTM = DeformableTerrainMaterial.CreateInstance(); + patchTM.name = "Patch terrain material"; + patchTM.GetInitialized().Native.setDescription( "Material patch Terrain Material" ); + var defaultTM = DeformableTerrainMaterial.CreateInstance(); + defaultTM.name = "Default terrain material"; + defaultTM.GetInitialized().Native.setDescription( "Default Terrain Material" ); + + patch.TerrainMaterial = patchTM; + testTerrain.DefaultTerrainMaterial = defaultTM; + + yield return TestUtils.SimulateSeconds( 0.5f ); + + var tile = testTerrain.Native.getActiveTileAttachments()[ 0 ].m_terrainTile; + + var probedMat = tile.getTerrainMaterial( tile.getWorldPositionFromVoxelIndex( tile.getSurfaceVoxelIndexFromTerrainIndex( new agx.Vec2i( 15, 15 ) ) ) ); + Assert.That( probedMat, Is.EqualTo( patchTM.Native ) ); + + probedMat = tile.getTerrainMaterial( tile.getWorldPositionFromVoxelIndex( tile.getSurfaceVoxelIndexFromTerrainIndex( new agx.Vec2i( 2, 2 ) ) ) ); + Assert.That( probedMat.getDescription(), Is.EqualTo( defaultTM.Native.getDescription() ) ); + } } } diff --git a/Tests/Runtime/VehicleTests.cs b/Tests/Runtime/VehicleTests.cs index eb09dc9d..85c7085e 100644 --- a/Tests/Runtime/VehicleTests.cs +++ b/Tests/Runtime/VehicleTests.cs @@ -5,12 +5,10 @@ using NUnit.Framework; using System; using System.Collections; -using System.Linq; using System.Text.RegularExpressions; using UnityEngine; using UnityEngine.TestTools; using UnityEngine.TestTools.Utils; -using Object = UnityEngine.Object; namespace AGXUnityTesting.Runtime { @@ -74,22 +72,6 @@ private BasicVehicle CreateBasicVehicle( System.Func( FindObjectsSortMode.None ); -#else - var objects = Object.FindObjectsOfType( ); -#endif - - var toDelete = objects.Where(x => x is not Simulation).Select(x => x.gameObject).ToArray(); - - yield return TestUtils.DestroyAndWait( toDelete ); - - yield return TestUtils.DestroyAndWait( GameObject.Find( "Simple Track" ) ); - } - [Test] public void TestBasicVehicleInitializes() { @@ -499,7 +481,48 @@ public IEnumerator TestBoxTrackFriction() yield return TestUtils.SimulateSeconds( 3f ); Assert.That( box.transform.position.y, Is.GreaterThan( 0 ), "Conveyor should carry the box" ); - Assert.That( box.transform.position.x, Is.LessThan( -0.5 ), "Conveyor should not move the box" ); + Assert.That( box.transform.position.x, Is.LessThan( -0.5 ), "Conveyor should move the box" ); + } + + [UnityTest] + public IEnumerator TestTrackSupportGeometry() + { + var track = CreateSimpleTrack(); + + yield return TestUtils.SimulateSeconds( 1f ); + + double distSum = 0; + foreach ( var node in track.Native.nodes() ) + distSum += node.getCenterPosition().length(); + + var box = CreateBox(Vector3.down * 0.24f); + box.GetComponent().MotionControl = agx.RigidBody.MotionControl.KINEMATICS; + + var boxGeom = box.GetComponentInChildren().HalfExtents = Vector3.one * 0.1f; + + track.Add( box.GetComponentInChildren() ); + + yield return TestUtils.SimulateSeconds( 1f ); + + double supportDistSum = 0.0f; + foreach ( var node in track.Native.nodes() ) + supportDistSum += node.getCenterPosition().length(); + + Assert.That( supportDistSum, Is.GreaterThan( distSum + 0.5f ) ); + + track.Remove( box.GetComponentInChildren() ); + + yield return TestUtils.SimulateSeconds( 1f ); + + double noSupportDistSum = 0.0f; + foreach ( var node in track.Native.nodes() ) + noSupportDistSum += node.getCenterPosition().length(); + + Assert.That( noSupportDistSum, Is.LessThan( distSum - 0.5f ) ); + + // Sanity check that tracks have not blown up + Assert.That( supportDistSum, Is.EqualTo( distSum ).Within( 1f ) ); + Assert.That( noSupportDistSum, Is.EqualTo( distSum ).Within( 1f ) ); } } } diff --git a/Tests/Runtime/WireCutMergeTests.cs b/Tests/Runtime/WireTests.cs similarity index 71% rename from Tests/Runtime/WireCutMergeTests.cs rename to Tests/Runtime/WireTests.cs index 5673c525..702ce2c7 100644 --- a/Tests/Runtime/WireCutMergeTests.cs +++ b/Tests/Runtime/WireTests.cs @@ -1,4 +1,6 @@ using AGXUnity; +using AGXUnity.Collide; +using AGXUnity.Rendering; using NUnit.Framework; using System.Collections; using System.Text.RegularExpressions; @@ -7,17 +9,63 @@ namespace AGXUnityTesting.Runtime { - public class WireCutMergeTests : AGXUnityFixture + public class PulleyTests : AGXUnityFixture { private Wire SourceWire; + private Cylinder Pulley; + private GameObject Weight1; + private GameObject Weight2; + + private GameObject CreateWeight( Vector3 pos ) + { + var weight = new GameObject( "Weight" ); + weight.AddComponent(); + var wireWeightBoxGO = new GameObject("Box"); + + var wireWeightBox = wireWeightBoxGO.AddComponent(); + wireWeightBox.HalfExtents = new Vector3( 0.1f, 0.1f, 0.1f ); + wireWeightBoxGO.transform.parent = weight.transform; + + weight.transform.position = pos; + + ShapeVisual.Create( wireWeightBox ); + + return weight; + } + [UnitySetUp] public IEnumerator SetupWireScene() { var wireObject = new GameObject("Source Wire"); SourceWire = wireObject.AddComponent(); - SourceWire.Route.Add( Wire.NodeType.WinchNode, null, Vector3.left ); - SourceWire.Route.Add( Wire.NodeType.WinchNode, null, Vector3.right ); + Weight1 = CreateWeight( Vector3.left ); + Weight2 = CreateWeight( Vector3.right ); + + SourceWire.Route.Add( Wire.NodeType.BodyFixedNode, Weight1, Vector3.right * 0.1f ); + SourceWire.Route.Add( Wire.NodeType.BodyFixedNode, Weight2, Vector3.left * 0.1f ); + + SourceWire.Diameter = 0.01f; + SourceWire.ResolutionPerUnitLength = 10; + SourceWire.gameObject.AddComponent(); + + SourceWire.Material = ShapeMaterial.CreateInstance(); + + var pulleyGO = new GameObject( "Pulley" ); + Pulley = pulleyGO.AddComponent(); + pulleyGO.transform.rotation = Quaternion.AngleAxis( 70, Vector3.left ); + pulleyGO.transform.position = Vector3.down * 0.2f; + Pulley.Radius = 0.2f; + Pulley.Material = ShapeMaterial.CreateInstance(); + + var cm = ContactMaterial.CreateInstance(); + cm.Material1 = SourceWire.Material; + cm.Material2 = Pulley.Material; + cm.FrictionCoefficients = Vector2.zero; + cm.WireFrictionCoefficients = Vector2.zero; + ContactMaterialManager.Instance.Add( cm ); + + ShapeVisual.Create( Pulley ); yield return TestUtils.WaitUntilLoaded(); } @@ -31,11 +79,47 @@ private Wire[] FindWires() #endif } - [TearDown] - public void TearDownWireScene() + [UnityTest] + public IEnumerator SlipsWithoutPulleyProperty() + { + yield return TestUtils.SimulateSeconds( 5 ); + + Assert.That( Weight1.transform.position.y, Is.LessThan( -2 ) ); + } + + [UnityTest] + public IEnumerator SticksWithPulleyProperty() + { + Pulley.IsPulley = true; + + yield return TestUtils.SimulateSeconds( 5 ); + + Assert.That( Weight1.transform.position.y, Is.GreaterThan( -2 ) ); + } + } + + public class WireCutMergeTests : AGXUnityFixture + { + private Wire SourceWire; + [UnitySetUp] + public IEnumerator SetupWireScene() { - foreach ( var wire in FindWires() ) - Object.Destroy( wire.gameObject ); + var wireObject = new GameObject("Source Wire"); + SourceWire = wireObject.AddComponent(); + + SourceWire.Route.Add( Wire.NodeType.WinchNode, null, Vector3.left ); + SourceWire.Route.Add( Wire.NodeType.WinchNode, null, Vector3.right ); + + yield return TestUtils.WaitUntilLoaded(); + } + + private Wire[] FindWires() + { +#if UNITY_2022_2_OR_NEWER + return UnityEngine.Object.FindObjectsByType( FindObjectsSortMode.None ); +#else + return UnityEngine.Object.FindObjectsOfType(); +#endif } [Test] diff --git a/Tests/Runtime/WireCutMergeTests.cs.meta b/Tests/Runtime/WireTests.cs.meta similarity index 100% rename from Tests/Runtime/WireCutMergeTests.cs.meta rename to Tests/Runtime/WireTests.cs.meta diff --git a/package.json b/package.json index c5f7df14..4de4408c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "com.algoryx.agxunity", - "version": "5.5.1+7ff02d5", + "version": "5.6.0-beta+3+39db3a4", "platform": "windows", "platformName": "Microsoft Windows", "displayName": "AGX Dynamics for Unity",