Why Gemfury? Push, build, and install  RubyGems npm packages Python packages Maven artifacts PHP packages Go Modules Debian packages RPM packages NuGet packages

Repository URL to install this package:

Details    
Size: Mime:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using Fluctio.FluctioSim.Common.Configuration;
using Fluctio.FluctioSim.Core.Components.Base;
using Fluctio.FluctioSim.Core.Components.MujocoGeom;
using Fluctio.FluctioSim.Core.MujocoExtensions;
using Fluctio.FluctioSim.Core.MujocoExtensions.MujocoDataProxy;
using Fluctio.FluctioSim.Utils.General;
using JetBrains.Annotations;
using Mujoco;
using UnityEngine;
using Object = UnityEngine.Object;

#if UNITY_EDITOR
using Fluctio.FluctioSim.Common.Icons;
using UnityEditor;
using UnityEditor.EditorTools;
using UnityEditor.ShortcutManagement;
#endif

namespace Fluctio.FluctioSim.Core.Components.Prefabs.Primitives.Base
{
	[RequireComponent(typeof(MujocoStructure))]
	[AddComponentMenu(Config.PrefixedName+"/Physics/Joint", Config.ComponentMenuOrder + 10)]
	public class PrimitiveJoint : InternalsEditorComponent
	{
		
		[SerializeField]
		public JointType jointType = JointType.Free;
		private JointType _previousJointType = JointType.None;
		
		[field: SerializeField, HideInInspector]
		public GameObject JointObject { get; private set; }
		
		[field: SerializeField, HideInInspector]
		[CanBeNull]
		public MjBaseJoint JointComponent { get; private set; }

		public ComponentData Data { get; private set; }

		public T GetJointComponent<T>() where T: MjBaseJoint
		{
			if (JointComponent == null)
			{
				throw new InvalidOperationException("Requested joint is null");
			}
			
			if (JointComponent is not T typedJointComponent)
			{
				throw new InvalidCastException($"Tried to get joint of type {typeof(T).Name}, but it is actually {JointComponent.GetType().Name}");
			}
			
			return typedJointComponent;
		}

		public override void InitializePrefab()
		{
			var parent = transform.parent;
			var parentBody = (parent != null) ? parent.GetComponentInParent<MjBody>(true) : null;
			if (jointType == JointType.Free && parentBody != null)
			{
				jointType = JointType.None;
			}
		}
		
		protected override void InitializeOnce()
		{
			base.InitializeOnce();
			JointObject = new GameObject("Joint");
			JointObject.transform.SetParent(transform);
			JointObject.transform.localPosition = Vector3.zero;
			AddJoint();
		}

		protected override IEnumerable<GameObject> GetInternalObjects() => new[] {JointObject};
		protected override IEnumerable<Object> GetCreatedInternals() => new[] {JointObject};

		protected override void Initialize()
		{
			base.Initialize();
			_previousJointType = jointType;
			Data = JointComponent.GetData();
		}

		public override void OnSelfChanged()
		{
			base.OnSelfChanged();
			if (jointType == _previousJointType)
			{
				return;
			}
			EditorStubs.DelayIfEditMode(() =>
			{
				DeleteJoint();
				AddJoint();
			});
		}

		private void AddJoint()
		{
			var jointComponentType = jointType switch
			{
				JointType.Free => typeof(MjFreeJoint),
				JointType.None => null,
				JointType.Hinge => typeof(MjHingeJoint),
				JointType.Ball => typeof(MjBallJoint),
				JointType.Slide => typeof(MjSlideJoint),
				_ => throw new InvalidEnumArgumentException(
					nameof(jointType),
					(int)jointType,
					jointType.GetType())
			};
			if (jointComponentType == null)
			{
				return;
			}
			
			JointComponent = JointObject.AddComponent(jointComponentType) as MjBaseJoint;
			Data = JointComponent.GetData();
			_previousJointType = jointType;
		}

		private void DeleteJoint()
		{
			EditorStubs.Destroy(JointComponent);
			JointComponent = null;
			Data = null;
			_previousJointType = JointType.None;
		}

		#if UNITY_EDITOR
		private void OnDrawGizmosSelected()
		{
			if (!Selection.gameObjects.Contains(gameObject)) {
				// exclude children
				return;
			}
			if (JointComponent == null || !JointComponent.isActiveAndEnabled)
			{
				return;
			}
			Gizmos.DrawWireSphere(JointComponent.transform.position, 0.5f);
			Handles.Label(JointComponent.transform.position, $"{jointType.ToString()} joint");
		}
		#endif
		
	}

	public enum JointType
	{
		Free,
		None,
		Hinge,
		Ball,
		Slide,
	}

	#if UNITY_EDITOR
	[EditorTool("Joints Tool", typeof(PrimitiveJoint))]
	internal class PrimitiveJointTool : EditorTool
	{
		private GUIContent _iconContent;
		public override GUIContent toolbarIcon => _iconContent;

		private void OnEnable()
		{
			_iconContent = new GUIContent
			{
				image = DefinedIcons.ComponentJoint,
				text = "Joints Tool",
				tooltip = "(J) Selecting this tool shows position/rotation handles for joints",
			};
		}

		[Shortcut("Activate Joints Tool", typeof(SceneView), KeyCode.J)]
	    static void PlatformToolShortcut()
	    {
		    ToolManager.SetActiveTool<PrimitiveJointTool>();
	    }

	    public override void OnToolGUI(EditorWindow window)
	    {
		    ForEachJoint((joint, internalJoint) =>
		    {
			    PositionHandle(internalJoint);
			    RotationHandle(internalJoint);
		    });
	    }

	    private void ForEachJoint(Action<PrimitiveJoint, MjBaseJoint> action)
	    {
		    foreach (var obj in targets)
		    {
			    var joint = obj as PrimitiveJoint;
			    if (joint == null)
			    {
				    return;
			    }
			    
			    var internalJoint = joint.JointComponent;
			    if (internalJoint == null)
			    {
				    return;
			    }
			    if (!internalJoint.isActiveAndEnabled)
			    {
				    return;
			    }

			    action(joint, internalJoint);
		    }
	    }
	    
	    private void PositionHandle(MjBaseJoint internalJoint)
	    {
		    using var changeScope = new EditorGUI.ChangeCheckScope();
		    var newPosition = Handles.PositionHandle(internalJoint.transform.position, internalJoint.transform.rotation);
		    if (changeScope.changed)
		    {
			    Undo.RecordObject(internalJoint.transform, "Move joint");
			    internalJoint.transform.position = newPosition;
		    }
	    }

	    private void RotationHandle(MjBaseJoint internalJoint)
	    {
		    using var changeScope = new EditorGUI.ChangeCheckScope();
		    var newRotation = Handles.RotationHandle(internalJoint.transform.rotation, internalJoint.transform.position);
		    if (changeScope.changed)
		    {
			    Undo.RecordObject(internalJoint.transform, "Rotate joint");
			    internalJoint.transform.rotation = newRotation;
		    }
	    }
	}
	#endif
}