├── README.md.meta ├── Unity Code.meta ├── Unity Code ├── Component Based Model │ ├── README.md.meta │ ├── Editor.meta │ ├── Aero Components │ │ ├── README.md.meta │ │ ├── AerodynamicComponent.cs.meta │ │ ├── CustomAirfoilComponent.cs.meta │ │ ├── MagnusEffectComponent.cs.meta │ │ ├── ThinAerofoilComponent.cs.meta │ │ ├── RotationalDragComponent.cs.meta │ │ ├── TranslationalDragComponent.cs.meta │ │ ├── README.md │ │ ├── MagnusEffectComponent.cs │ │ ├── CustomAirfoilComponent.cs │ │ ├── TranslationalDragComponent.cs │ │ ├── RotationalDragComponent.cs │ │ ├── AerodynamicComponent.cs │ │ └── ThinAerofoilComponent.cs │ ├── Aero Components.meta │ ├── Visualisation Tools.meta │ ├── Visualisation Tools │ │ ├── Editor.meta │ │ ├── Prefabs.meta │ │ ├── AngleSegment.cs.meta │ │ ├── ArrowFader.cs.meta │ │ ├── ArrowSettings.cs.meta │ │ ├── WindArrow.cs.meta │ │ ├── WindRotation.cs.meta │ │ ├── ComponentArrows.cs.meta │ │ ├── LiftOnlyArrows.cs.meta │ │ ├── ThinAerofoilArrows.cs.meta │ │ ├── TotalDragArrows.cs.meta │ │ ├── TranslationalDragArrows.cs.meta │ │ ├── Editor │ │ │ ├── ArrowLayerCreation.cs.meta │ │ │ └── ArrowLayerCreation.cs │ │ ├── ArrowFader.cs │ │ ├── WindRotation.cs │ │ ├── WindArrow.cs │ │ ├── LiftOnlyArrows.cs │ │ ├── TranslationalDragArrows.cs │ │ ├── ThinAerofoilArrows.cs │ │ ├── TotalDragArrows.cs │ │ ├── AngleSegment.cs │ │ ├── ArrowSettings.cs │ │ └── ComponentArrows.cs │ ├── AeroBody.cs.meta │ ├── AeroGroup.cs.meta │ ├── ReferenceFrame.cs.meta │ ├── Editor │ │ ├── AeroGroupEditor.cs.meta │ │ ├── AeroComponentAdder.cs.meta │ │ ├── AeroGroupEditor.cs │ │ └── AeroComponentAdder.cs │ ├── README.md │ ├── AeroGroup.cs │ ├── ReferenceFrame.cs │ └── AeroBody.cs ├── Controllers.meta ├── Resources.meta ├── Controllers │ ├── Editor.meta │ ├── ControlSurface.cs.meta │ ├── Editor │ │ ├── ControlSurfaceEditor.cs.meta │ │ └── ControlSurfaceEditor.cs │ └── ControlSurface.cs ├── Component Based Model.meta └── Resources │ ├── Sphere.obj.meta │ ├── Arrow Body.obj.meta │ ├── Arrow Head.obj.meta │ ├── Arrow Head.obj │ ├── Arrow Body.obj │ └── Sphere.obj └── README.md /README.md.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: df51ad1d82bfcbb4c8a50b3e6d640adf 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Unity Code.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: f29f0b8ef2dd3094f9621250bfba3126 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Unity Code/Component Based Model/README.md.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: c62d68493bc024a44b80c569e7a2162c 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Unity Code/Controllers.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: ea8deb1205bda6743838a3b6f517b051 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Unity Code/Resources.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: c2c23661fda412146b7b468be43d541f 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Unity Code/Controllers/Editor.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 4d57bac19c7479247949749036cc428b 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Unity Code/Component Based Model.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: ed4168b793da5424fa940d12913ca921 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Unity Code/Component Based Model/Editor.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 82d0475a832ca34499e16c06f85078d1 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Unity Code/Component Based Model/Aero Components/README.md.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 87a883abe9d68b74ba6f9b33ae37b018 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Unity Code/Component Based Model/Aero Components.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: f08490f7a2ad3f84e8f2146f63dabda5 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Unity Code/Component Based Model/Visualisation Tools.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 7980edc09ab6c6a4081d54f488662dfd 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Unity Code/Component Based Model/Visualisation Tools/Editor.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 7fc7bc1b25fce5e4e8a7bbaa4fff0edc 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Unity Code/Component Based Model/Visualisation Tools/Prefabs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 936ee0961348008408f969707ce0f371 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Unity Code/Component Based Model/AeroBody.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: b97eae35e5f0cdc4183414709d314fce 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Unity Code/Controllers/ControlSurface.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 8159b0f2b156b43449121f2f6d6d4c08 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Unity Code/Component Based Model/AeroGroup.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 1d9517a1868bbbe45946e6480c578553 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Unity Code/Component Based Model/ReferenceFrame.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: f5fb8467d4bba0c48b4d25e6afc9f44c 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Unity Code/Controllers/Editor/ControlSurfaceEditor.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: e787aac10196cba4882f8df6d981dd6a 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Unity Code/Component Based Model/Editor/AeroGroupEditor.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 8bed900a377788849b6287f3a40371f8 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Unity Code/Component Based Model/Editor/AeroComponentAdder.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 230ed9281cfca7d41bea3e3dfef3505a 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Unity Code/Component Based Model/Visualisation Tools/AngleSegment.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: aabc5584e5f5c7644b432ef3b2c825c4 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Unity Code/Component Based Model/Visualisation Tools/ArrowFader.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 032e0d2709a111a4692fa3f62129ada4 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Unity Code/Component Based Model/Visualisation Tools/ArrowSettings.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 73dc48a78a8b1cc4bafd6ab816312a56 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Unity Code/Component Based Model/Visualisation Tools/WindArrow.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 3e8fd8dfa7629c24b8b9418543c10898 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Unity Code/Component Based Model/Visualisation Tools/WindRotation.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 80a9e859f590fe44c888ad6f3523962e 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Unity Code/Component Based Model/Aero Components/AerodynamicComponent.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 02df48409ff815348a8de9c8ab0603da 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Unity Code/Component Based Model/Aero Components/CustomAirfoilComponent.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 13e9f63e63cda58469318236bd2ac402 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Unity Code/Component Based Model/Aero Components/MagnusEffectComponent.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: d01a787ecf2dc604ebbce0f482c0a958 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Unity Code/Component Based Model/Aero Components/ThinAerofoilComponent.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 65f81b964e7c26d4fba44f2e6d45d263 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Unity Code/Component Based Model/Visualisation Tools/ComponentArrows.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: adeb7ade4b2d441469ff25f37991b4de 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Unity Code/Component Based Model/Visualisation Tools/LiftOnlyArrows.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: d0f21091910c5944099e92f71fecd53b 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Unity Code/Component Based Model/Visualisation Tools/ThinAerofoilArrows.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 7d9eccc9252215f4e9fe618c5030fc90 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Unity Code/Component Based Model/Visualisation Tools/TotalDragArrows.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: e463b9332cee2454389e0aba0dfe6705 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Unity Code/Component Based Model/Aero Components/RotationalDragComponent.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: ed8f330a7c30a6747a76b9e06aeb5591 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Unity Code/Component Based Model/Aero Components/TranslationalDragComponent.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 7f35c399e8587a540840429808eb2ffb 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Unity Code/Component Based Model/Visualisation Tools/TranslationalDragArrows.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: d144ed43d82935f418b32dcc40e5d98e 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Unity Code/Component Based Model/Visualisation Tools/Editor/ArrowLayerCreation.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 3c4f6895a0f0f704f8f1864e9ad86427 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Unity Code/Component Based Model/Editor/AeroGroupEditor.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | using UnityEditor; 5 | 6 | [CustomEditor(typeof(AeroGroup))] 7 | public class AeroGroupEditor : Editor 8 | { 9 | public override void OnInspectorGUI() 10 | { 11 | AeroGroup aeroGroup = (AeroGroup)target; 12 | DrawDefaultInspector(); 13 | 14 | if(GUILayout.Button("Get Child AeroBody Components")){ 15 | aeroGroup.GetChildBodies(); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Unity Code/Component Based Model/Aero Components/README.md: -------------------------------------------------------------------------------- 1 | # Aero Components 2 | Components derive from an aerodynamic component class which implements a RunModel function. RunModel is called by the main AeroBody in runtime for all attached components. The AeroBody then sums the resulting forces and moments for each component and applies them to the attached rigid body. 3 | 4 | The current components available in the model are: 5 | - Linear bluff body drag 6 | - Rotational bluff body drag 7 | - Thin aerofoil for lift, centre of pressure motion and induced drag 8 | - Magnus effect for lift generated by rotating bodies 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Aerodynamic Objects 2 | > **Warning** 3 | > This is a preview of the work. Please expect major changes to structure and naming conventions as development progresses. The latest version of Aerodynamic Objects is now available [on the unity asset store](https://assetstore.unity.com/packages/tools/physics/aerodynamic-objects-core-251378). More information is available on our website [https://www.aerodynamicobjects.com/](https://www.aerodynamicobjects.com/) 4 | 5 | A low order, approximate aerodynamics model for rigid bodies simulated in Unity. Click the video link below to find out more. 6 | 7 | [![Click to view an introductory demonstration](https://img.youtube.com/vi/Ys2wj1iIz4U/0.jpg)](https://www.youtube.com/watch?v=Ys2wj1iIz4U) 8 | -------------------------------------------------------------------------------- /Unity Code/Component Based Model/Visualisation Tools/ArrowFader.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | public static class ArrowFader 6 | { 7 | public static void ToFadeMode(this Material material) 8 | { 9 | material.SetOverrideTag("RenderType", "Transparent"); 10 | material.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.SrcAlpha); 11 | material.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha); 12 | material.SetInt("_ZWrite", 0); 13 | material.DisableKeyword("_ALPHATEST_ON"); 14 | material.EnableKeyword("_ALPHABLEND_ON"); 15 | material.DisableKeyword("_ALPHAPREMULTIPLY_ON"); 16 | material.renderQueue = (int)UnityEngine.Rendering.RenderQueue.Transparent; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Unity Code/Component Based Model/Visualisation Tools/WindRotation.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | public class WindRotation : MonoBehaviour 6 | { 7 | [Tooltip("Degrees per second")] 8 | public float rotationSpeed = 5f; 9 | public float initialWindSpeed = 2f; 10 | AeroBody aeroBody; 11 | // Start is called before the first frame update 12 | void Start() 13 | { 14 | aeroBody = GetComponent(); 15 | aeroBody.externalFlowVelocity_inEarthFrame = new Vector3(0, 0, initialWindSpeed); 16 | } 17 | 18 | // Update is called once per frame 19 | void FixedUpdate() 20 | { 21 | aeroBody.externalFlowVelocity_inEarthFrame = Quaternion.Euler(0, -rotationSpeed * Time.fixedDeltaTime, 0) * aeroBody.externalFlowVelocity_inEarthFrame; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Unity Code/Controllers/Editor/ControlSurfaceEditor.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | using UnityEditor; 5 | 6 | [CustomEditor(typeof(ControlSurface))] 7 | public class ControlSurfaceEditor : Editor 8 | { 9 | Color leftColour = Color.red; 10 | Color rightColour = Color.green; 11 | 12 | public void OnSceneGUI() 13 | { 14 | ControlSurface controlSurface = (ControlSurface)target; 15 | 16 | // Draw the handles for each hinge point 17 | 18 | // Left 19 | Handles.Label(controlSurface.HingeStart, "Port"); 20 | controlSurface.HingeStart = Handles.PositionHandle(controlSurface.HingeStart, Quaternion.identity); 21 | 22 | // Right 23 | Handles.Label(controlSurface.HingeEnd, "Starboard"); 24 | controlSurface.HingeEnd = Handles.PositionHandle(controlSurface.HingeEnd, Quaternion.identity); 25 | 26 | // Connect the points 27 | Handles.DrawLine(controlSurface.HingeStart, controlSurface.HingeEnd); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Unity Code/Component Based Model/Visualisation Tools/WindArrow.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | public class WindArrow : ComponentArrows 6 | { 7 | // Wind arrow is just the wind on the aero body 8 | 9 | Arrow windArrow; 10 | AeroBody aeroBody; 11 | 12 | void Awake() 13 | { 14 | if (this.enabled) 15 | { 16 | pointAtPoint = true; 17 | aeroBody = GetComponent(); 18 | windArrow = new Arrow(ArrowSettings.Singleton().windColour, "Wind Arrow", transform); 19 | } 20 | } 21 | 22 | private void OnDisable() 23 | { 24 | if(windArrow != null) 25 | { 26 | Destroy(windArrow.head.gameObject); 27 | Destroy(windArrow.body.gameObject); 28 | } 29 | } 30 | 31 | private void Reset() 32 | { 33 | pointAtPoint = true; 34 | } 35 | 36 | 37 | void Update() 38 | { 39 | aeroBody.ResolveWindAndDimensions_1_to_6(); 40 | 41 | if (useCoefficientForScale) 42 | { 43 | // Wind just uses normalised vector instead 44 | SetArrowPositionAndRotationFromVector(windArrow, -aeroBody.earthFrame.windVelocity.normalized, aeroBody.transform.position); 45 | } 46 | else 47 | { 48 | SetArrowPositionAndRotationFromVector(windArrow, -aeroBody.earthFrame.windVelocity, aeroBody.transform.position); 49 | } 50 | 51 | 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /Unity Code/Component Based Model/Aero Components/MagnusEffectComponent.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | public class MagnusEffectComponent : AerodynamicComponent 6 | { 7 | // Lift generated due to rotation of the body 8 | 9 | Vector3 RHO; 10 | Vector3 CLr; 11 | float vSquared; 12 | 13 | public override void RunModel(AeroBody aeroBody) 14 | { 15 | // I think, in the future this should just be 2*pi*alpha_m where alpha_m is the apparent alpha due to rotation 16 | 17 | // Magnus effect coefficient 18 | vSquared = aeroBody.aeroBodyFrame.windVelocity.sqrMagnitude; 19 | if (vSquared == 0f) 20 | { 21 | resultantForce_bodyFrame = Vector3.zero; 22 | resultantMoment_bodyFrame = Vector3.zero; 23 | } 24 | else 25 | { 26 | RHO = Vector3.Scale(aeroBody.volumeVector, aeroBody.aeroBodyFrame.angularWindVelocity); 27 | CLr = 4f * Vector3.Cross(aeroBody.aeroBodyFrame.windVelocity, RHO) / (aeroBody.aeroBodyFrame.windVelocity.sqrMagnitude * aeroBody.planformArea); 28 | 29 | // Proper hacky way to clamp the size of the magnus coefficient - without this CLr can reach 40+ 30 | if (CLr.magnitude > 3f) 31 | { 32 | CLr = 3f * CLr.normalized; 33 | } 34 | 35 | resultantForce_bodyFrame = -CLr * aeroBody.qS; 36 | resultantMoment_bodyFrame = Vector3.zero; 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Unity Code/Component Based Model/Visualisation Tools/LiftOnlyArrows.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | public class LiftOnlyArrows : ComponentArrows 6 | { 7 | Arrow LiftArrow; 8 | 9 | ThinAerofoilComponent component; 10 | AeroBody aeroBody; 11 | 12 | void Awake() 13 | { 14 | if (this.enabled) 15 | { 16 | component = GetComponent(); 17 | aeroBody = GetComponent(); 18 | 19 | LiftArrow = new Arrow(ArrowSettings.Singleton().liftColour, "Lift Arrow", transform); 20 | } 21 | } 22 | 23 | private void OnDisable() 24 | { 25 | if (LiftArrow != null) 26 | { 27 | Destroy(LiftArrow.head.gameObject); 28 | Destroy(LiftArrow.body.gameObject); 29 | } 30 | } 31 | 32 | 33 | void Update() 34 | { 35 | // Taking the computational hit to get the up to date values 36 | aeroBody.ResolveWindAndDimensions_1_to_6(); 37 | component.RunModel(aeroBody); 38 | 39 | // Get the separate lift and induced drag force vectors in earth frame 40 | Vector3 lift_earthFrame = aeroBody.TransformDirectionBodyToEarth(component.lift_bodyFrame); 41 | 42 | if (useCoefficientForScale) 43 | { 44 | // Need to use the absolute value of the coefficient because we already have the direction from the force 45 | SetArrowPositionAndRotationFromVector(LiftArrow, Mathf.Abs(component.CL) * lift_earthFrame.normalized, component.forcePointOfAction_earthFrame); 46 | } 47 | else 48 | { 49 | SetArrowPositionAndRotationFromVector(LiftArrow, lift_earthFrame, component.forcePointOfAction_earthFrame); 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /Unity Code/Component Based Model/Visualisation Tools/TranslationalDragArrows.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | public class TranslationalDragArrows : ComponentArrows 6 | { 7 | 8 | // Translational drag is an easy one, the drag acts at the aero body position 9 | 10 | Arrow DragArrow; 11 | 12 | TranslationalDragComponent component; 13 | AeroBody aeroBody; 14 | 15 | void Awake() 16 | { 17 | if (this.enabled) 18 | { 19 | component = GetComponent(); 20 | aeroBody = GetComponent(); 21 | 22 | DragArrow = new Arrow(ArrowSettings.Singleton().dragColour, "Drag Arrow", transform); 23 | } 24 | } 25 | 26 | private void OnDisable() 27 | { 28 | if (DragArrow != null) 29 | { 30 | Destroy(DragArrow.head.gameObject); 31 | Destroy(DragArrow.body.gameObject); 32 | } 33 | } 34 | 35 | 36 | void Update() 37 | { 38 | 39 | 40 | // Taking the computational hit to get the up to date values 41 | aeroBody.ResolveWindAndDimensions_1_to_6(); 42 | component.RunModel(aeroBody); 43 | 44 | if (useCoefficientForScale) 45 | { 46 | // Draw the arrow using the normalised force vector, scaled up by the drag coefficient 47 | // Need to use the absolute value of the coefficient because we already have the direction from the force 48 | SetArrowPositionAndRotationFromVector(DragArrow, Mathf.Abs(component.CD) * component.resultantForce_earthFrame.normalized, component.forcePointOfAction_earthFrame); 49 | } 50 | else 51 | { 52 | // Draw the arrow 53 | SetArrowPositionAndRotationFromVector(DragArrow, component.resultantForce_earthFrame, component.forcePointOfAction_earthFrame); 54 | } 55 | 56 | 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /Unity Code/Component Based Model/README.md: -------------------------------------------------------------------------------- 1 | # AeroBody 2 | The AeroBody class is the main class for this implementation. It is responsible for 3 | - Coordinate transformations 4 | - Resolution of the wind velocity 5 | - (ToDo) Tracking velocity of the body when a rigid body is not provided 6 | 7 | The AeroBody maintains: 8 | - Dimensions of the object 9 | - Reference frames: Earth, Object, AeroBody, Equivalent AeroBody 10 | - Resolved dimensions of the equivalent aerodynamic body 11 | 12 | Aerodynamics Components are added to the same GameObject as an AeroBody, the Aerodynamics Components are responsible for the computation and application of aerodynamic forces. 13 | 14 | For testing purposes all variables and functions are made public. This allows for easy probing of the aerodynamic model during testing and validation. 15 | 16 | # Component-Based Aerodynamic Objects 17 | Aerodynamic objects uses separate components for each part of the aerodynamics model. Some applications will not require full fidelity aerodynamics and so it would be efficient to only add the necessary components. Similarly, some components may not be suitable for the desired use if the user wants to implement their own dynamics. 18 | 19 | Components derive from the base AerodynamicComponent class. AerodynamicComponent contains two virtual void functions: 20 | - RunModel 21 | - ApplyForces 22 | 23 | Generally a component will only need to override the RunModel function to implement the computation of their resultant aerodynamic force and its point of action. However, if forces need to be applied in a different manner, then the ApplyForces function may also be overridden. 24 | 25 | Components subscribe to two events on the AeroBody: 26 | - runModelEvent 27 | - applyForcesEvent 28 | 29 | The computation of the forces and moments for each aerodynamic component is done separately to the application of the forces - this allows for validation tests to be performed without the need to apply the forces to the rigid body afterwards. 30 | -------------------------------------------------------------------------------- /Unity Code/Component Based Model/Visualisation Tools/ThinAerofoilArrows.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | public class ThinAerofoilArrows : ComponentArrows 6 | { 7 | 8 | /* Thin aerofoil has the following forces, moments: 9 | * - Lift 10 | * - Induced Drag 11 | * - Pitching moment 12 | */ 13 | 14 | Arrow LiftArrow; 15 | Arrow InducedDragArrow; 16 | 17 | ThinAerofoilComponent component; 18 | AeroBody aeroBody; 19 | 20 | void Awake() 21 | { 22 | component = GetComponent(); 23 | aeroBody = GetComponent(); 24 | 25 | LiftArrow = new Arrow(ArrowSettings.Singleton().liftColour, "Lift Arrow", transform); 26 | InducedDragArrow = new Arrow(ArrowSettings.Singleton().dragColour, "Induced Drag Arrow", transform); 27 | } 28 | 29 | 30 | void Update() 31 | { 32 | // Taking the computational hit to get the up to date values 33 | aeroBody.ResolveWindAndDimensions_1_to_6(); 34 | component.RunModel(aeroBody); 35 | 36 | // Get the separate lift and induced drag force vectors in earth frame 37 | Vector3 lift_earthFrame = aeroBody.TransformDirectionBodyToEarth(component.lift_bodyFrame); 38 | Vector3 inducedDrag_earthFrame = aeroBody.TransformDirectionBodyToEarth(component.inducedDrag_bodyFrame); 39 | 40 | if (useCoefficientForScale) 41 | { 42 | // Need to use the absolute value of the coefficients because we already have the direction from the forces 43 | SetArrowPositionAndRotationFromVector(LiftArrow, Mathf.Abs(component.CL) * lift_earthFrame.normalized, component.forcePointOfAction_earthFrame); 44 | SetArrowPositionAndRotationFromVector(InducedDragArrow, Mathf.Abs(component.CD_induced) * inducedDrag_earthFrame.normalized, component.forcePointOfAction_earthFrame); 45 | } 46 | else 47 | { 48 | SetArrowPositionAndRotationFromVector(LiftArrow, lift_earthFrame, component.forcePointOfAction_earthFrame); 49 | SetArrowPositionAndRotationFromVector(InducedDragArrow, inducedDrag_earthFrame, component.forcePointOfAction_earthFrame); 50 | } 51 | 52 | 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Unity Code/Component Based Model/Aero Components/CustomAirfoilComponent.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | public class CustomAirfoilComponent : AerodynamicComponent 6 | { 7 | // Thin Aero Foil aerodynamics uses a moving centre of pressure 8 | // and a normal force model to determine the lift and moment due to lift 9 | // Only induced drag is added by this component! 10 | 11 | public AnimationCurve liftCurve; 12 | public AnimationCurve dragCurve; 13 | public AnimationCurve pitchingMomentCurve; 14 | 15 | public float alpha0, effectiveAlpha; 16 | 17 | // Coefficients 18 | public float CL, CD, CM; 19 | 20 | public Vector3 lift_bodyFrame; // (Nm) 21 | public Vector3 inducedDrag_bodyFrame; // (Nm) 22 | public Vector3 centreOfPressure_earth; // (m) 23 | 24 | public override void RunModel(AeroBody aeroBody) 25 | { 26 | // Zero lift angle is set based on the amount of camber. This is physics based 27 | alpha0 = -aeroBody.EAB.camberRatio; 28 | effectiveAlpha = aeroBody.alpha - alpha0; 29 | 30 | CL = liftCurve.Evaluate(effectiveAlpha); 31 | CD = dragCurve.Evaluate(effectiveAlpha); 32 | CM = pitchingMomentCurve.Evaluate(effectiveAlpha); 33 | 34 | // Convert coefficients to forces and moments 35 | float qS = aeroBody.dynamicPressure * aeroBody.planformArea; 36 | Vector3 liftDirection = Vector3.Cross(aeroBody.aeroBodyFrame.windVelocity_normalised, aeroBody.angleOfAttackRotationVector).normalized; 37 | lift_bodyFrame = qS * CL * liftDirection; 38 | inducedDrag_bodyFrame = -CD * qS * aeroBody.aeroBodyFrame.windVelocity_normalised; 39 | resultantForce_bodyFrame = lift_bodyFrame + inducedDrag_bodyFrame; 40 | 41 | // Forces are applied at the centre of the wing - we have a pitching moment coefficient too 42 | forcePointOfAction_earthFrame = aeroBody.transform.position; 43 | resultantForce_earthFrame = aeroBody.TransformDirectionBodyToEarth(resultantForce_bodyFrame); 44 | 45 | resultantMoment_bodyFrame = new Vector3(CM * qS * aeroBody.EAB.chord_c, 0, 0); 46 | resultantMoment_bodyFrame = aeroBody.TransformDirectionEABToBody(resultantMoment_bodyFrame); 47 | resultantMoment_earthFrame = aeroBody.TransformDirectionBodyToEarth(resultantMoment_bodyFrame); 48 | } 49 | } 50 | 51 | -------------------------------------------------------------------------------- /Unity Code/Component Based Model/AeroGroup.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using UnityEngine; 5 | 6 | public class AeroGroup : MonoBehaviour 7 | { 8 | public AeroBody[] aeroBodies = new AeroBody[0]; 9 | 10 | public float planformArea; 11 | public float scaledArea; 12 | public float aspectRatio; 13 | public float bodyTotalArea; 14 | 15 | public float areaScale; 16 | 17 | private void OnValidate() 18 | { 19 | if (aeroBodies.Length > 0) 20 | { 21 | AssignAeroBodiesToGroup(); 22 | GetAreaScales(); 23 | } 24 | } 25 | 26 | private void GetAreaScales() 27 | { 28 | bodyTotalArea = 0f; 29 | // Get total areas for individual bodies 30 | for (int i = 0; i < aeroBodies.Length; i++) 31 | { 32 | bodyTotalArea += aeroBodies[i].bodyPlanformArea; 33 | } 34 | 35 | // areaScale tells us how much the aero body planform areas need to be scaled by to 36 | // make their total area equal to the wing's planform area 37 | areaScale = planformArea / bodyTotalArea; 38 | 39 | 40 | scaledArea = 0f; 41 | for (int i = 0; i < aeroBodies.Length; i++) 42 | { 43 | scaledArea += areaScale * aeroBodies[i].bodyPlanformArea; 44 | } 45 | } 46 | 47 | void AssignAeroBodiesToGroup() 48 | { 49 | for (int i = 0; i < aeroBodies.Length; i++) 50 | { 51 | aeroBodies[i].myGroup = this; 52 | aeroBodies[i].GetEllipsoid_1_to_2(); 53 | } 54 | } 55 | 56 | public void GetChildBodies() 57 | { 58 | aeroBodies = GetComponentsInChildren(); 59 | 60 | int length = aeroBodies.Length; 61 | switch (length) 62 | { 63 | case 0: 64 | Debug.Log("No aero bodies were found in children of " + gameObject.name); 65 | break; 66 | case 1: 67 | Debug.Log("1 aero body found and added to group."); 68 | AssignAeroBodiesToGroup(); 69 | GetAreaScales(); 70 | break; 71 | default: 72 | Debug.Log(aeroBodies.Length + " aero bodies found and added to group."); 73 | AssignAeroBodiesToGroup(); 74 | GetAreaScales(); 75 | break; 76 | } 77 | 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /Unity Code/Component Based Model/Visualisation Tools/Editor/ArrowLayerCreation.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | using UnityEditor; 5 | 6 | [InitializeOnLoad] 7 | public class ArrowLayerCreation 8 | { 9 | static ArrowLayerCreation() 10 | { 11 | // ==================== 12 | // The code below was taken from: https://forum.unity.com/threads/create-tags-and-layers-in-the-editor-using-script-both-edit-and-runtime-modes.732119/ 13 | 14 | // Create the arrow layer 15 | string layerName = "Arrows"; 16 | int maxLayers = 31; 17 | 18 | // Open tag manager 19 | SerializedObject tagManager = new SerializedObject(AssetDatabase.LoadAllAssetsAtPath("ProjectSettings/TagManager.asset")[0]); 20 | // Layers Property 21 | SerializedProperty layersProp = tagManager.FindProperty("layers"); 22 | if (!PropertyExists(layersProp, 0, maxLayers, layerName)) 23 | { 24 | SerializedProperty sp; 25 | // Start at layer 9th index -> 8 (zero based) => first 8 reserved for unity / greyed out 26 | for (int i = 8, j = maxLayers; i < j; i++) 27 | { 28 | sp = layersProp.GetArrayElementAtIndex(i); 29 | if (sp.stringValue == "") 30 | { 31 | // Assign string value to layer 32 | sp.stringValue = layerName; 33 | Debug.Log("Layer: " + layerName + " has been added for aerodynamic force visualisation tools"); 34 | // Save settings 35 | tagManager.ApplyModifiedProperties(); 36 | break; 37 | } 38 | if (i == j) 39 | Debug.LogWarning("All allowed layers have been assigned. Please change one to: \'" + layerName + "\' so that aerodynamic force visualisation tools can operate correctly"); 40 | } 41 | } 42 | // ===================== 43 | } 44 | 45 | // ================= 46 | // This function also came from: https://forum.unity.com/threads/create-tags-and-layers-in-the-editor-using-script-both-edit-and-runtime-modes.732119/ 47 | private static bool PropertyExists(SerializedProperty property, int start, int end, string value) 48 | { 49 | for (int i = start; i < end; i++) 50 | { 51 | SerializedProperty t = property.GetArrayElementAtIndex(i); 52 | if (t.stringValue.Equals(value)) 53 | { 54 | return true; 55 | } 56 | } 57 | return false; 58 | } 59 | // =================== 60 | } 61 | -------------------------------------------------------------------------------- /Unity Code/Component Based Model/Aero Components/TranslationalDragComponent.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | public class TranslationalDragComponent : AerodynamicComponent 6 | { 7 | // Drag 8 | public float CD; // (dimensionless) 9 | public float CD_profile; // (dimensionless) 10 | public float CD_pressure_0aoa, CD_pressure_90aoa; // (dimensionless) 11 | public float CD_shear_0aoa, CD_shear_90aoa; // (dimensionless) 12 | public float CD_normalFlatPlate = 1.2f; // (dimensionless) 13 | public float CD_roughSphere = 0.5f; // (dimensionless) 14 | public float reynoldsNum_linear, Cf_linear; // (dimensionless) 15 | 16 | public override void RunModel(AeroBody aeroBody) 17 | { 18 | // Linear - only care about the direction of flow, not resolving into axes 19 | // Bill says that really we should be looking at linear reynolds number in each axis separately 20 | // Linear uses diameter of the body - note we use the EAB chord as wind is resolved along this direction 21 | reynoldsNum_linear = aeroBody.rho * aeroBody.aeroBodyFrame.windVelocity.magnitude * aeroBody.EAB.chord_c / aeroBody.mu; 22 | 23 | // Shear coefficient 24 | Cf_linear = reynoldsNum_linear == 0 ? 0 : 0.027f / Mathf.Pow(reynoldsNum_linear, 1f / 7f); 25 | 26 | // Shear stress coefficients 27 | CD_shear_0aoa = 2f * Cf_linear; 28 | CD_shear_90aoa = aeroBody.EAB.thicknessToChordRatio_bOverc * 2f * Cf_linear; 29 | 30 | // Pressure coefficients 31 | CD_pressure_0aoa = aeroBody.EAB.thicknessToChordRatio_bOverc * CD_roughSphere; 32 | CD_pressure_90aoa = CD_normalFlatPlate - aeroBody.EAB.thicknessToChordRatio_bOverc * (CD_normalFlatPlate - CD_roughSphere); 33 | 34 | // An area correction factor is included for the pressure coefficient but is ommitted for the shear coefficient 35 | // This is because CD_shear_90aoa << CD_pressure_90aoa 36 | CD_profile = CD_shear_0aoa + aeroBody.EAB.thicknessToChordRatio_bOverc * CD_pressure_0aoa + (CD_shear_90aoa + CD_pressure_90aoa - CD_shear_0aoa - aeroBody.EAB.thicknessToChordRatio_bOverc * CD_pressure_0aoa) * aeroBody.sinAlpha * aeroBody.sinAlpha; 37 | CD = CD_profile; 38 | 39 | resultantForce_bodyFrame = -CD * aeroBody.dynamicPressure * aeroBody.planformArea * aeroBody.aeroBodyFrame.windVelocity_normalised; 40 | resultantMoment_bodyFrame = Vector3.zero; 41 | 42 | resultantForce_earthFrame = aeroBody.TransformDirectionBodyToEarth(resultantForce_bodyFrame); 43 | // Drag is applied at the geometric centre of the aerodynamic body 44 | forcePointOfAction_earthFrame = aeroBody.transform.position; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Unity Code/Component Based Model/Visualisation Tools/TotalDragArrows.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | public class TotalDragArrows : ComponentArrows 6 | { 7 | ThinAerofoilComponent inducedDragComponent; 8 | TranslationalDragComponent translationalDragComponent; 9 | AeroBody aeroBody; 10 | 11 | Arrow DragArrow; 12 | 13 | void Awake() 14 | { 15 | if (this.enabled) 16 | { 17 | inducedDragComponent = GetComponent(); 18 | translationalDragComponent = GetComponent(); 19 | aeroBody = GetComponent(); 20 | 21 | DragArrow = new Arrow(ArrowSettings.Singleton().dragColour, "Drag Arrow", transform); 22 | } 23 | } 24 | 25 | private void OnDisable() 26 | { 27 | if (DragArrow != null) 28 | { 29 | Destroy(DragArrow.head.gameObject); 30 | Destroy(DragArrow.body.gameObject); 31 | } 32 | } 33 | 34 | 35 | void Update() 36 | { 37 | // Taking the computational hit to get the up to date values 38 | aeroBody.ResolveWindAndDimensions_1_to_6(); 39 | inducedDragComponent.RunModel(aeroBody); 40 | translationalDragComponent.RunModel(aeroBody); 41 | 42 | // Get the separate lift and induced drag force vectors in earth frame 43 | Vector3 inducedDrag_earthFrame = aeroBody.TransformDirectionBodyToEarth(inducedDragComponent.inducedDrag_bodyFrame); 44 | 45 | if (useCoefficientForScale) 46 | { 47 | // Calculate the overall drag coefficient 48 | Vector3 dragForce = translationalDragComponent.resultantForce_earthFrame + inducedDrag_earthFrame; 49 | 50 | // Pretty lazy way to get the coefficient in the same direction as the force 51 | Vector3 CD = (Mathf.Abs(inducedDragComponent.CD_induced) + Mathf.Abs(translationalDragComponent.CD)) * dragForce.normalized; 52 | 53 | // This isn't completely accurate as the induced drag acts at the centre of pressure while translational 54 | // drag acts at the geometric centre in the AO model. But it's a happy medium for now. 55 | SetArrowPositionAndRotationFromVector(DragArrow, CD, translationalDragComponent.forcePointOfAction_earthFrame); 56 | } 57 | else 58 | { 59 | // This isn't completely accurate as the induced drag acts at the centre of pressure while translational 60 | // drag acts at the geometric centre in the AO model. But it's a happy medium for now. 61 | SetArrowPositionAndRotationFromVector(DragArrow, translationalDragComponent.resultantForce_earthFrame + inducedDrag_earthFrame, translationalDragComponent.forcePointOfAction_earthFrame); 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /Unity Code/Controllers/ControlSurface.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | public class ControlSurface : MonoBehaviour 6 | { 7 | /* Working on a generic control surface script which can automatically generate a hinge object 8 | * Need to clarify things like where this script should go in the hierarchy 9 | * 10 | * Also need to test which way around the hinge should be defined to follow convention 11 | */ 12 | 13 | 14 | public AeroBody aeroBody; 15 | public Transform moveableSurface; 16 | public Transform hinge; 17 | 18 | public float Angle = 0f; 19 | public float Trim = 0f; 20 | 21 | Quaternion defaultHingeRotation; 22 | 23 | public Vector3 HingeStart { get { return transform.TransformPoint(hingeStart); } set { hingeStart = transform.InverseTransformPoint(value); } } 24 | public Vector3 HingeEnd { get { return transform.TransformPoint(hingeEnd); } set { hingeEnd = transform.InverseTransformPoint(value); } } 25 | 26 | private Vector3 hingeStart = Vector3.left; 27 | private Vector3 hingeEnd = Vector3.right; 28 | 29 | public float minControlThrow = -15f; 30 | public float maxControlThrow = 15f; 31 | 32 | public float camberScale = 0.05f; 33 | 34 | 35 | void Awake() 36 | { 37 | if (moveableSurface && aeroBody) 38 | { 39 | UpdateHinge(); 40 | } 41 | else 42 | { 43 | Debug.LogWarning(name + " is missing required components. Disabling for now."); 44 | } 45 | } 46 | 47 | 48 | void FixedUpdate() 49 | { 50 | //controlHinge.localRotation = trim * Quaternion.Euler(delta, 0, 0); 51 | 52 | float clampedAngle = Mathf.Clamp(Angle + Trim, minControlThrow, maxControlThrow); 53 | 54 | hinge.localRotation = defaultHingeRotation * Quaternion.Euler(0, 0, clampedAngle); 55 | 56 | //hinge.localEulerAngles = new Vector3(0, 0, delta); 57 | float camber = clampedAngle; 58 | if (camber > 180) camber -= 360; 59 | //if (camber < -180) camber += 360; 60 | 61 | // Minus sign here, not sure who's got things the wrong way around... 62 | camber = -camber * Mathf.Deg2Rad; 63 | aeroBody.SetFlapCamber(camberScale * camber); 64 | } 65 | 66 | public void UpdateHinge() 67 | { 68 | if(hinge == null) 69 | { 70 | GameObject go = new GameObject(name + " hinge"); 71 | go.transform.parent = transform; 72 | hinge = go.transform; 73 | } 74 | 75 | moveableSurface.SetParent(null); 76 | 77 | // Align and position the hinge object 78 | hinge.forward = (HingeEnd - HingeStart).normalized; 79 | hinge.position = (HingeStart + HingeEnd) / 2f; 80 | 81 | moveableSurface.SetParent(hinge); 82 | 83 | defaultHingeRotation = hinge.localRotation; 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /Unity Code/Component Based Model/ReferenceFrame.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | public class ReferenceFrame 6 | { 7 | // Useful directions - I don't think they get used at all actually... 8 | public Vector3 xDirection, yDirection, zDirection; // (unit vector) 9 | 10 | // The rotation from the previous frame of reference to this frame of reference 11 | public Quaternion objectToFrameRotation = Quaternion.identity; 12 | 13 | // The rotation from the current frame of reference back to the previous frame 14 | public Quaternion inverseObjectToFrameRotation = Quaternion.identity; 15 | 16 | public void SetDirectionVectors(Vector3 x, Vector3 y, Vector3 z) 17 | { 18 | xDirection = x; 19 | yDirection = y; 20 | zDirection = z; 21 | } 22 | 23 | public void SetFrameRotation(Quaternion rotation) 24 | { 25 | // This was the logical approach to me... but it seems they need to be reverse 26 | //objectToFrameRotation = rotation; 27 | //inverseObjectToFrameRotation = Quaternion.Inverse(rotation); 28 | 29 | objectToFrameRotation = Quaternion.Inverse(rotation); 30 | inverseObjectToFrameRotation = rotation; 31 | } 32 | 33 | 34 | // ==================== Needs updating =============================== 35 | // This all needs changing so that we're not considering the wind in all 36 | // the reference frames. For starters, we need to take out "wind velocity" 37 | // from the coordinate frames. We can have the velocity of the frame itself 38 | // which makes much more sense than embedding a negative in there somewhere. 39 | // The confusing part then becomes figuring out where the wind velocity comes 40 | // from and how we can use it in the rest of the model. Sounds like a big job. 41 | // 42 | // I think we should look at something like: 43 | // 1. Earth axes 44 | // 2. Wind axes 45 | // 3. Local axes 46 | // 4. EAB axes 47 | // Where local and EAB axes are derived from the wind axes, meaning that they inherit 48 | // the velocity of those axes 49 | 50 | // Wind resolved into this coordinate frame 51 | public Vector3 windVelocity; // (m/s) 52 | public Vector3 windVelocity_normalised; // (unit vector 53 | public Vector3 angularWindVelocity; // (rad/s) 54 | public Vector3 angularWindVelocity_normalised; // (unit vector) 55 | 56 | public void SetResolvedWind(Vector3 linearWind, Vector3 angularWind) 57 | { 58 | // The normalisations here are probably wasted computation... it's an 59 | // optimisation incase the normalised vector is needed more than once 60 | windVelocity = objectToFrameRotation * linearWind; 61 | windVelocity_normalised = windVelocity.normalized; 62 | 63 | angularWindVelocity = objectToFrameRotation * angularWind; 64 | angularWindVelocity_normalised = angularWindVelocity.normalized; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /Unity Code/Resources/Sphere.obj.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 596b02c1e35f0d5418d76112d7d4b1a8 3 | ModelImporter: 4 | serializedVersion: 20200 5 | internalIDToNameTable: [] 6 | externalObjects: {} 7 | materials: 8 | materialImportMode: 2 9 | materialName: 0 10 | materialSearch: 1 11 | materialLocation: 1 12 | animations: 13 | legacyGenerateAnimations: 4 14 | bakeSimulation: 0 15 | resampleCurves: 1 16 | optimizeGameObjects: 0 17 | motionNodeName: 18 | rigImportErrors: 19 | rigImportWarnings: 20 | animationImportErrors: 21 | animationImportWarnings: 22 | animationRetargetingWarnings: 23 | animationDoRetargetingWarnings: 0 24 | importAnimatedCustomProperties: 0 25 | importConstraints: 0 26 | animationCompression: 1 27 | animationRotationError: 0.5 28 | animationPositionError: 0.5 29 | animationScaleError: 0.5 30 | animationWrapMode: 0 31 | extraExposedTransformPaths: [] 32 | extraUserProperties: [] 33 | clipAnimations: [] 34 | isReadable: 0 35 | meshes: 36 | lODScreenPercentages: [] 37 | globalScale: 1 38 | meshCompression: 0 39 | addColliders: 0 40 | useSRGBMaterialColor: 1 41 | sortHierarchyByName: 1 42 | importVisibility: 1 43 | importBlendShapes: 1 44 | importCameras: 1 45 | importLights: 1 46 | fileIdsGeneration: 2 47 | swapUVChannels: 0 48 | generateSecondaryUV: 0 49 | useFileUnits: 1 50 | keepQuads: 0 51 | weldVertices: 1 52 | bakeAxisConversion: 0 53 | preserveHierarchy: 0 54 | skinWeightsMode: 0 55 | maxBonesPerVertex: 4 56 | minBoneWeight: 0.001 57 | meshOptimizationFlags: -1 58 | indexFormat: 0 59 | secondaryUVAngleDistortion: 8 60 | secondaryUVAreaDistortion: 15.000001 61 | secondaryUVHardAngle: 88 62 | secondaryUVMarginMethod: 1 63 | secondaryUVMinLightmapResolution: 40 64 | secondaryUVMinObjectScale: 1 65 | secondaryUVPackMargin: 4 66 | useFileScale: 1 67 | tangentSpace: 68 | normalSmoothAngle: 60 69 | normalImportMode: 0 70 | tangentImportMode: 3 71 | normalCalculationMode: 4 72 | legacyComputeAllNormalsFromSmoothingGroupsWhenMeshHasBlendShapes: 0 73 | blendShapeNormalImportMode: 1 74 | normalSmoothingSource: 0 75 | referencedClips: [] 76 | importAnimation: 1 77 | humanDescription: 78 | serializedVersion: 3 79 | human: [] 80 | skeleton: [] 81 | armTwist: 0.5 82 | foreArmTwist: 0.5 83 | upperLegTwist: 0.5 84 | legTwist: 0.5 85 | armStretch: 0.05 86 | legStretch: 0.05 87 | feetSpacing: 0 88 | globalScale: 1 89 | rootMotionBoneName: 90 | hasTranslationDoF: 0 91 | hasExtraRoot: 0 92 | skeletonHasParents: 1 93 | lastHumanDescriptionAvatarSource: {instanceID: 0} 94 | autoGenerateAvatarMappingIfUnspecified: 1 95 | animationType: 2 96 | humanoidOversampling: 1 97 | avatarSetup: 0 98 | addHumanoidExtraRootOnlyWhenUsingAvatar: 1 99 | additionalBone: 0 100 | userData: 101 | assetBundleName: 102 | assetBundleVariant: 103 | -------------------------------------------------------------------------------- /Unity Code/Resources/Arrow Body.obj.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 9ea5cafe7cf0baa46a344161d05316b9 3 | ModelImporter: 4 | serializedVersion: 20200 5 | internalIDToNameTable: [] 6 | externalObjects: {} 7 | materials: 8 | materialImportMode: 2 9 | materialName: 0 10 | materialSearch: 1 11 | materialLocation: 1 12 | animations: 13 | legacyGenerateAnimations: 4 14 | bakeSimulation: 0 15 | resampleCurves: 1 16 | optimizeGameObjects: 0 17 | motionNodeName: 18 | rigImportErrors: 19 | rigImportWarnings: 20 | animationImportErrors: 21 | animationImportWarnings: 22 | animationRetargetingWarnings: 23 | animationDoRetargetingWarnings: 0 24 | importAnimatedCustomProperties: 0 25 | importConstraints: 0 26 | animationCompression: 1 27 | animationRotationError: 0.5 28 | animationPositionError: 0.5 29 | animationScaleError: 0.5 30 | animationWrapMode: 0 31 | extraExposedTransformPaths: [] 32 | extraUserProperties: [] 33 | clipAnimations: [] 34 | isReadable: 0 35 | meshes: 36 | lODScreenPercentages: [] 37 | globalScale: 1 38 | meshCompression: 0 39 | addColliders: 0 40 | useSRGBMaterialColor: 1 41 | sortHierarchyByName: 1 42 | importVisibility: 1 43 | importBlendShapes: 1 44 | importCameras: 1 45 | importLights: 1 46 | fileIdsGeneration: 2 47 | swapUVChannels: 0 48 | generateSecondaryUV: 0 49 | useFileUnits: 1 50 | keepQuads: 0 51 | weldVertices: 1 52 | bakeAxisConversion: 0 53 | preserveHierarchy: 0 54 | skinWeightsMode: 0 55 | maxBonesPerVertex: 4 56 | minBoneWeight: 0.001 57 | meshOptimizationFlags: -1 58 | indexFormat: 0 59 | secondaryUVAngleDistortion: 8 60 | secondaryUVAreaDistortion: 15.000001 61 | secondaryUVHardAngle: 88 62 | secondaryUVMarginMethod: 1 63 | secondaryUVMinLightmapResolution: 40 64 | secondaryUVMinObjectScale: 1 65 | secondaryUVPackMargin: 4 66 | useFileScale: 1 67 | tangentSpace: 68 | normalSmoothAngle: 60 69 | normalImportMode: 0 70 | tangentImportMode: 3 71 | normalCalculationMode: 4 72 | legacyComputeAllNormalsFromSmoothingGroupsWhenMeshHasBlendShapes: 0 73 | blendShapeNormalImportMode: 1 74 | normalSmoothingSource: 0 75 | referencedClips: [] 76 | importAnimation: 1 77 | humanDescription: 78 | serializedVersion: 3 79 | human: [] 80 | skeleton: [] 81 | armTwist: 0.5 82 | foreArmTwist: 0.5 83 | upperLegTwist: 0.5 84 | legTwist: 0.5 85 | armStretch: 0.05 86 | legStretch: 0.05 87 | feetSpacing: 0 88 | globalScale: 1 89 | rootMotionBoneName: 90 | hasTranslationDoF: 0 91 | hasExtraRoot: 0 92 | skeletonHasParents: 1 93 | lastHumanDescriptionAvatarSource: {instanceID: 0} 94 | autoGenerateAvatarMappingIfUnspecified: 1 95 | animationType: 2 96 | humanoidOversampling: 1 97 | avatarSetup: 0 98 | addHumanoidExtraRootOnlyWhenUsingAvatar: 1 99 | additionalBone: 0 100 | userData: 101 | assetBundleName: 102 | assetBundleVariant: 103 | -------------------------------------------------------------------------------- /Unity Code/Resources/Arrow Head.obj.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 0b350b8bf27a4a64281d294d8ab35b2d 3 | ModelImporter: 4 | serializedVersion: 20200 5 | internalIDToNameTable: [] 6 | externalObjects: {} 7 | materials: 8 | materialImportMode: 2 9 | materialName: 0 10 | materialSearch: 1 11 | materialLocation: 1 12 | animations: 13 | legacyGenerateAnimations: 4 14 | bakeSimulation: 0 15 | resampleCurves: 1 16 | optimizeGameObjects: 0 17 | motionNodeName: 18 | rigImportErrors: 19 | rigImportWarnings: 20 | animationImportErrors: 21 | animationImportWarnings: 22 | animationRetargetingWarnings: 23 | animationDoRetargetingWarnings: 0 24 | importAnimatedCustomProperties: 0 25 | importConstraints: 0 26 | animationCompression: 1 27 | animationRotationError: 0.5 28 | animationPositionError: 0.5 29 | animationScaleError: 0.5 30 | animationWrapMode: 0 31 | extraExposedTransformPaths: [] 32 | extraUserProperties: [] 33 | clipAnimations: [] 34 | isReadable: 0 35 | meshes: 36 | lODScreenPercentages: [] 37 | globalScale: 1 38 | meshCompression: 0 39 | addColliders: 0 40 | useSRGBMaterialColor: 1 41 | sortHierarchyByName: 1 42 | importVisibility: 1 43 | importBlendShapes: 1 44 | importCameras: 1 45 | importLights: 1 46 | fileIdsGeneration: 2 47 | swapUVChannels: 0 48 | generateSecondaryUV: 0 49 | useFileUnits: 1 50 | keepQuads: 0 51 | weldVertices: 1 52 | bakeAxisConversion: 0 53 | preserveHierarchy: 0 54 | skinWeightsMode: 0 55 | maxBonesPerVertex: 4 56 | minBoneWeight: 0.001 57 | meshOptimizationFlags: -1 58 | indexFormat: 0 59 | secondaryUVAngleDistortion: 8 60 | secondaryUVAreaDistortion: 15.000001 61 | secondaryUVHardAngle: 88 62 | secondaryUVMarginMethod: 1 63 | secondaryUVMinLightmapResolution: 40 64 | secondaryUVMinObjectScale: 1 65 | secondaryUVPackMargin: 4 66 | useFileScale: 0 67 | tangentSpace: 68 | normalSmoothAngle: 60 69 | normalImportMode: 0 70 | tangentImportMode: 3 71 | normalCalculationMode: 4 72 | legacyComputeAllNormalsFromSmoothingGroupsWhenMeshHasBlendShapes: 0 73 | blendShapeNormalImportMode: 1 74 | normalSmoothingSource: 0 75 | referencedClips: [] 76 | importAnimation: 1 77 | humanDescription: 78 | serializedVersion: 3 79 | human: [] 80 | skeleton: [] 81 | armTwist: 0.5 82 | foreArmTwist: 0.5 83 | upperLegTwist: 0.5 84 | legTwist: 0.5 85 | armStretch: 0.05 86 | legStretch: 0.05 87 | feetSpacing: 0 88 | globalScale: 1 89 | rootMotionBoneName: 90 | hasTranslationDoF: 0 91 | hasExtraRoot: 0 92 | skeletonHasParents: 1 93 | lastHumanDescriptionAvatarSource: {instanceID: 0} 94 | autoGenerateAvatarMappingIfUnspecified: 1 95 | animationType: 2 96 | humanoidOversampling: 1 97 | avatarSetup: 0 98 | addHumanoidExtraRootOnlyWhenUsingAvatar: 1 99 | additionalBone: 0 100 | userData: 101 | assetBundleName: 102 | assetBundleVariant: 103 | -------------------------------------------------------------------------------- /Unity Code/Component Based Model/Editor/AeroComponentAdder.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | using UnityEditor; 5 | 6 | public class AeroComponentAdder : MonoBehaviour 7 | { 8 | // Add a menu to the Aerobody script 9 | [MenuItem("CONTEXT/AeroBody/Add All Aerodynamics")] 10 | static void AddAllAerodynamics(MenuCommand command) 11 | { 12 | AeroBody aeroBody = (AeroBody)command.context; 13 | GameObject go = aeroBody.gameObject; 14 | 15 | // Add all the components if they aren't there already 16 | 17 | if (!go.GetComponent()) 18 | { 19 | go.AddComponent(); 20 | } 21 | 22 | if (!go.GetComponent()) 23 | { 24 | go.AddComponent(); 25 | } 26 | 27 | if (!go.GetComponent()) 28 | { 29 | go.AddComponent(); 30 | } 31 | 32 | if (!go.GetComponent()) 33 | { 34 | go.AddComponent(); 35 | } 36 | } 37 | 38 | // Add a menu to the Aerobody script 39 | [MenuItem("CONTEXT/AeroBody/Add Arrows")] 40 | static void AddArrows(MenuCommand command) 41 | { 42 | AeroBody aeroBody = (AeroBody)command.context; 43 | GameObject go = aeroBody.gameObject; 44 | 45 | // Add all the components if they aren't there already 46 | 47 | 48 | ThinAerofoilComponent thinAerofoil = go.GetComponent(); 49 | TranslationalDragComponent translationalDrag = go.GetComponent(); 50 | 51 | if (thinAerofoil) 52 | { 53 | // If we have both lift and drag models attached 54 | if (translationalDrag) 55 | { 56 | // Add the total arrow components which require both components 57 | if (!go.GetComponent()) 58 | { 59 | go.AddComponent(); 60 | } 61 | if (!go.GetComponent()) 62 | { 63 | go.AddComponent(); 64 | } 65 | } 66 | else 67 | { 68 | // Otherwise, we just have the lift component, so add the arrows for that component 69 | if (!go.GetComponent()) 70 | { 71 | go.AddComponent(); 72 | } 73 | } 74 | } 75 | else if (translationalDrag) 76 | { 77 | // Also need to check incase we have just the translational drag component attached 78 | if (!go.GetComponent()) 79 | { 80 | go.AddComponent(); 81 | } 82 | } 83 | 84 | if (!go.GetComponent()) 85 | { 86 | go.AddComponent(); 87 | } 88 | 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /Unity Code/Component Based Model/Aero Components/RotationalDragComponent.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | public class RotationalDragComponent : AerodynamicComponent 6 | { 7 | // Drag due to the angular velocity of the body 8 | public Vector3 dampingTorque_bodyFrame, pressureTorque_bodyFrame, shearStressTorque_bodyFrame, momentDueToLift_bodyFrame; // (Nm) 9 | public float CD_normalFlatPlate = 1.2f; // (dimensionless) 10 | public float reynoldsNum_x_rotational, reynoldsNum_y_rotational, reynoldsNum_z_rotational; // (dimensionless) 11 | public float Cf_x_rotational, Cf_y_rotational, Cf_z_rotational; // (dimensionless) 12 | public Vector3 directionalAngularVelocitySquared_bodyFrame; 13 | public float rotationalPressure_x, rotationalPressure_y, rotationalPressure_z; 14 | 15 | public override void RunModel(AeroBody aeroBody) 16 | { 17 | // Rotational uses the circumference of the body 18 | reynoldsNum_x_rotational = Mathf.Abs(Mathf.PI * aeroBody.rho * aeroBody.aeroBodyFrame.angularWindVelocity.x * aeroBody.aeroBody.midAxis * aeroBody.aeroBody.majorAxis / aeroBody.mu); 19 | reynoldsNum_y_rotational = Mathf.Abs(Mathf.PI * aeroBody.rho * aeroBody.aeroBodyFrame.angularWindVelocity.y * aeroBody.aeroBody.majorAxis * aeroBody.aeroBody.majorAxis / aeroBody.mu); 20 | reynoldsNum_z_rotational = Mathf.Abs(Mathf.PI * aeroBody.rho * aeroBody.aeroBodyFrame.angularWindVelocity.z * aeroBody.aeroBody.majorAxis * aeroBody.aeroBody.majorAxis / aeroBody.mu); 21 | 22 | // Shear coefficient 23 | Cf_x_rotational = reynoldsNum_x_rotational == 0 ? 0 : 0.027f / Mathf.Pow(reynoldsNum_x_rotational, 1f / 7f); 24 | Cf_y_rotational = reynoldsNum_y_rotational == 0 ? 0 : 0.027f / Mathf.Pow(reynoldsNum_y_rotational, 1f / 7f); 25 | Cf_z_rotational = reynoldsNum_z_rotational == 0 ? 0 : 0.027f / Mathf.Pow(reynoldsNum_z_rotational, 1f / 7f); 26 | 27 | // Elementwise square of the angular velocity - I don't really get this but okay 28 | // Scale by the direction here to preserve the signs of the angular velocity 29 | directionalAngularVelocitySquared_bodyFrame = Vector3.Scale(Vector3.Scale(aeroBody.aeroBodyFrame.angularWindVelocity, aeroBody.aeroBodyFrame.angularWindVelocity), aeroBody.aeroBodyFrame.angularWindVelocity_normalised); 30 | 31 | rotationalPressure_x = 0.5f * aeroBody.rho * aeroBody.aeroBody.midAxis * aeroBody.aeroBody.midAxis * aeroBody.aeroBody.midAxis * directionalAngularVelocitySquared_bodyFrame.x; 32 | rotationalPressure_y = 0.5f * aeroBody.rho * aeroBody.aeroBody.majorAxis * aeroBody.aeroBody.majorAxis * aeroBody.aeroBody.majorAxis * directionalAngularVelocitySquared_bodyFrame.y; 33 | rotationalPressure_z = 0.5f * aeroBody.rho * aeroBody.aeroBody.majorAxis * aeroBody.aeroBody.majorAxis * aeroBody.aeroBody.majorAxis * directionalAngularVelocitySquared_bodyFrame.z; 34 | 35 | // Pressure torques 36 | pressureTorque_bodyFrame.x = CD_normalFlatPlate * (aeroBody.areaVector.y / 2f) * rotationalPressure_x; 37 | pressureTorque_bodyFrame.y = CD_normalFlatPlate * (aeroBody.areaVector.z / 2f) * rotationalPressure_y; 38 | pressureTorque_bodyFrame.z = CD_normalFlatPlate * (aeroBody.areaVector.y / 2f) * rotationalPressure_z; 39 | 40 | // Shear stress torques 41 | shearStressTorque_bodyFrame.x = Cf_x_rotational * aeroBody.ellipsoidSurfaceArea * rotationalPressure_x; 42 | shearStressTorque_bodyFrame.y = Cf_y_rotational * aeroBody.ellipsoidSurfaceArea * rotationalPressure_y; 43 | shearStressTorque_bodyFrame.z = Cf_z_rotational * aeroBody.ellipsoidSurfaceArea * rotationalPressure_z; 44 | 45 | // Blending drag effects based on axis ratios 46 | dampingTorque_bodyFrame.x = (1 - aeroBody.lambda_x) * pressureTorque_bodyFrame.x + aeroBody.lambda_x * shearStressTorque_bodyFrame.x; 47 | dampingTorque_bodyFrame.y = (1 - aeroBody.lambda_y) * pressureTorque_bodyFrame.y + aeroBody.lambda_y * shearStressTorque_bodyFrame.y; 48 | dampingTorque_bodyFrame.z = (1 - aeroBody.lambda_z) * pressureTorque_bodyFrame.z + aeroBody.lambda_z * shearStressTorque_bodyFrame.z; 49 | 50 | // Compute the resulting force and moment 51 | resultantForce_bodyFrame = Vector3.zero; 52 | resultantMoment_bodyFrame = -dampingTorque_bodyFrame; 53 | resultantMoment_earthFrame = aeroBody.TransformDirectionBodyToEarth(resultantMoment_bodyFrame); 54 | } 55 | } -------------------------------------------------------------------------------- /Unity Code/Component Based Model/Visualisation Tools/AngleSegment.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | public class AngleSegment : MonoBehaviour 6 | { 7 | //MeshFilter mf; 8 | [Range(-180, 180)] 9 | public float includedAngleDeg; 10 | [Range(1, 5)] 11 | public float radius = 1; 12 | [Range(2, 10)] 13 | public int numberOfPoints = 5; 14 | 15 | Vector3[] vertices; 16 | Mesh mesh; 17 | 18 | Transform rootTransform; 19 | AeroBody aeroBody; 20 | 21 | void CreateWedgeMesh(Color color) 22 | { 23 | // Create a new game object to store the wedge on 24 | GameObject angleOfAttackWedge = new GameObject("Angle of Attack Wedge"); 25 | angleOfAttackWedge.transform.parent = transform; 26 | rootTransform = angleOfAttackWedge.transform; 27 | rootTransform.localPosition = Vector3.zero; 28 | rootTransform.localRotation = Quaternion.identity; 29 | rootTransform.localScale = new Vector3(1f / rootTransform.parent.localScale.x, 1f / rootTransform.parent.localScale.y, 1f / rootTransform.parent.localScale.z); 30 | 31 | MeshFilter mf = angleOfAttackWedge.AddComponent(); 32 | MeshRenderer mr = angleOfAttackWedge.AddComponent(); 33 | mesh = new Mesh(); 34 | mf.mesh = mesh; 35 | mr.materials[0].color = color; 36 | mr.materials[0].ToFadeMode(); 37 | 38 | // Add vertices to mesh 39 | List verticesList = new List(); 40 | 41 | float angle = 0; 42 | // Add zero point 43 | verticesList.Add(Vector3.zero); 44 | // Add radial points 45 | for (int i = 0; i < numberOfPoints; i++) 46 | { 47 | angle = ((float)i / (numberOfPoints - 1)) * includedAngleDeg * Mathf.Deg2Rad; 48 | float z = radius * Mathf.Cos(angle); 49 | float y = radius * Mathf.Sin(angle); 50 | verticesList.Add(new Vector3(0, y, z)); 51 | } 52 | 53 | vertices = verticesList.ToArray(); 54 | 55 | 56 | // Triangles 57 | List trianglesList = new List(); 58 | // Front face 59 | for (int i = 0; i < (numberOfPoints - 1); i++) 60 | { 61 | trianglesList.Add(0); 62 | trianglesList.Add(i + 1); 63 | trianglesList.Add(i + 2); 64 | } 65 | // Back face 66 | for (int i = 0; i < (numberOfPoints - 1); i++) 67 | { 68 | trianglesList.Add(i + 2); 69 | trianglesList.Add(i + 1); 70 | trianglesList.Add(0); 71 | } 72 | 73 | 74 | int[] triangles = trianglesList.ToArray(); 75 | 76 | // Normals 77 | List normalsList = new List(); 78 | 79 | for (int i = 0; i < vertices.Length; i++) 80 | { 81 | normalsList.Add(Vector3.forward); 82 | } 83 | 84 | Vector3[] normals = normalsList.ToArray(); 85 | 86 | // Initialise 87 | mesh.vertices = vertices; 88 | mesh.triangles = triangles; 89 | mesh.normals = normals; 90 | } 91 | 92 | void SetWedgeAngle(float angle_radians) 93 | { 94 | float angle = 0; 95 | for (int i = 0; i < numberOfPoints; i++) 96 | { 97 | angle = -((float)i / (numberOfPoints - 1)) * angle_radians; 98 | float z = radius * Mathf.Cos(angle); 99 | float y = radius * Mathf.Sin(angle); 100 | vertices[i + 1] = new Vector3(0, y, z); 101 | } 102 | 103 | mesh.vertices = vertices; 104 | } 105 | 106 | void SetWedgeLocalRotation(Quaternion rotation) 107 | { 108 | rootTransform.localRotation = rotation; 109 | } 110 | 111 | void SetWedgeRotation(Quaternion rotation) 112 | { 113 | rootTransform.rotation = rotation; 114 | } 115 | 116 | // Start is called before the first frame update 117 | void Awake() 118 | { 119 | aeroBody = GetComponent(); 120 | 121 | CreateWedgeMesh(ArrowSettings.Singleton().alphaColour); 122 | 123 | } 124 | 125 | // Update is called once per frame 126 | void Update() 127 | { 128 | // Get the current state of the aerobody - don't worry this is overwritten again in fixedupdate 129 | aeroBody.ResolveWindAndDimensions_1_to_6(); 130 | SetWedgeAngle(aeroBody.alpha); 131 | SetWedgeLocalRotation(Quaternion.Euler(new Vector3(0, aeroBody.beta_deg))); 132 | } 133 | } -------------------------------------------------------------------------------- /Unity Code/Component Based Model/Aero Components/AerodynamicComponent.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | [System.Serializable] 6 | [RequireComponent(typeof(AeroBody))] 7 | public class AerodynamicComponent : MonoBehaviour 8 | { 9 | /* This component is the base class for all of the aerodynamics 10 | * models that are included in the Aerodynamic Objects package 11 | * 12 | * Components can be added to an AeroBody, increasing the 13 | * fidelity of the overall aerodynamics model used to simulate the 14 | * AeroBody's motion. Note that including components like this means 15 | * the models cannot interact with each other, a component must include 16 | * all interactions within itself. For example, the lift induced drag 17 | * must be included in the lift model component, not the drag component 18 | */ 19 | 20 | // The resultant forces and moments of an aerodynamic component should be 21 | // provided in the body reference frame so we only need to transform the entire sum 22 | // of forces and moments into earth coordinates once, instead of doing a transform for each component 23 | public Vector3 resultantForce_bodyFrame; 24 | public Vector3 resultantForce_earthFrame; 25 | public Vector3 forcePointOfAction_earthFrame; 26 | public Vector3 resultantMoment_bodyFrame; 27 | public Vector3 resultantMoment_earthFrame; 28 | 29 | public virtual void RunModel(AeroBody aeroBody) 30 | { 31 | // This function will use the wind and dimensions of the AeroBody to 32 | // compute the relevant forces and moments for the component 33 | } 34 | 35 | public virtual void ApplyForces(Rigidbody rigidbody) 36 | { 37 | // This function allows for centre of pressure force application 38 | // Not sure how to account for moment due to camber though... 39 | 40 | if (Vector3ContainsNaN(resultantForce_earthFrame)) 41 | { 42 | Debug.LogWarning("Resultant force contains NaN, applying no force instead"); 43 | } 44 | else 45 | { 46 | rigidbody.AddForceAtPosition(resultantForce_earthFrame, forcePointOfAction_earthFrame); 47 | } 48 | 49 | if (Vector3ContainsNaN(resultantMoment_earthFrame)) 50 | { 51 | Debug.LogWarning("Resultant torque contains NaN, applying no torque instead"); 52 | } 53 | else 54 | { 55 | // I need to make sure the body and rigid body frames line up, if not then I need to correct for this 56 | rigidbody.AddTorque(resultantMoment_earthFrame); 57 | } 58 | } 59 | 60 | // ================================ EXPERIMENTAL =============================================== 61 | 62 | //public float delta = 0.9f; 63 | //public void SaturateForces(AeroBody body) 64 | //{ 65 | // // Acceleration due to the force (a = F/m) 66 | // float acceleration = resultantForce_earthFrame.magnitude / body.rb.mass; 67 | 68 | // // Assuming forward Euler integration, get the velocity change due to "impulse" 69 | // float deltaVelocity = acceleration * Time.fixedDeltaTime; 70 | 71 | // // If that velocity is going to change the direction... 72 | // if (deltaVelocity > body.aeroBodyFrame.windVelocity.magnitude) 73 | // { 74 | // // Set force to be just the delta velocity which can't overshoot 75 | // resultantForce_earthFrame = delta * (body.aeroBodyFrame.windVelocity.magnitude / Time.fixedDeltaTime) * body.rb.mass * resultantForce_earthFrame.normalized; 76 | // } 77 | //} 78 | 79 | // ============================================================================================== 80 | 81 | private void OnEnable() 82 | { 83 | SubscribeToAeroEvents(); 84 | } 85 | 86 | private void OnDisable() 87 | { 88 | UnSubscribeFromAeroEvents(); 89 | } 90 | 91 | void SubscribeToAeroEvents() 92 | { 93 | // Don't need to check for null reference here as these components require an AeroBody 94 | GetComponent().runModelEvent += RunModel; 95 | GetComponent().applyForcesEvent += ApplyForces; 96 | } 97 | 98 | void UnSubscribeFromAeroEvents() 99 | { 100 | // Don't need to check for null reference here as these components require an AeroBody 101 | GetComponent().runModelEvent -= RunModel; 102 | GetComponent().applyForcesEvent -= ApplyForces; 103 | } 104 | 105 | public static bool Vector3ContainsNaN(Vector3 vector) 106 | { 107 | if (float.IsNaN(vector.x)) 108 | return true; 109 | if (float.IsNaN(vector.y)) 110 | return true; 111 | if (float.IsNaN(vector.z)) 112 | return true; 113 | 114 | return false; 115 | } 116 | } -------------------------------------------------------------------------------- /Unity Code/Resources/Arrow Head.obj: -------------------------------------------------------------------------------- 1 | # Blender v2.93.5 OBJ File: '' 2 | # www.blender.org 3 | mtllib arrow head.mtl 4 | o Cone 5 | v 0.000000 0.000000 -0.500000 6 | v 0.097545 0.000000 -0.490393 7 | v 0.191342 0.000000 -0.461940 8 | v 0.277785 0.000000 -0.415735 9 | v 0.353553 0.000000 -0.353553 10 | v 0.415735 0.000000 -0.277785 11 | v 0.461940 0.000000 -0.191342 12 | v 0.490393 0.000000 -0.097545 13 | v 0.500000 0.000000 0.000000 14 | v 0.490393 0.000000 0.097545 15 | v 0.461940 0.000000 0.191342 16 | v 0.415735 0.000000 0.277785 17 | v 0.353553 0.000000 0.353553 18 | v 0.277785 0.000000 0.415735 19 | v 0.191342 0.000000 0.461940 20 | v 0.097545 0.000000 0.490393 21 | v -0.000000 0.000000 0.500000 22 | v -0.097545 0.000000 0.490393 23 | v -0.191342 0.000000 0.461940 24 | v -0.277785 0.000000 0.415735 25 | v -0.353553 0.000000 0.353553 26 | v -0.415735 0.000000 0.277785 27 | v -0.461940 0.000000 0.191342 28 | v -0.490393 0.000000 0.097545 29 | v -0.500000 0.000000 -0.000000 30 | v -0.490393 0.000000 -0.097545 31 | v -0.461940 0.000000 -0.191342 32 | v -0.415735 0.000000 -0.277785 33 | v -0.353553 0.000000 -0.353553 34 | v -0.277785 0.000000 -0.415735 35 | v -0.191342 0.000000 -0.461940 36 | v -0.097545 0.000000 -0.490393 37 | v 0.000000 1.000000 0.000000 38 | vt 0.250000 0.490000 39 | vt 0.250000 0.250000 40 | vt 0.296822 0.485388 41 | vt 0.341844 0.471731 42 | vt 0.383337 0.449553 43 | vt 0.419706 0.419706 44 | vt 0.449553 0.383337 45 | vt 0.471731 0.341844 46 | vt 0.485388 0.296822 47 | vt 0.490000 0.250000 48 | vt 0.485388 0.203178 49 | vt 0.471731 0.158156 50 | vt 0.449553 0.116663 51 | vt 0.419706 0.080294 52 | vt 0.383337 0.050447 53 | vt 0.341844 0.028269 54 | vt 0.296822 0.014612 55 | vt 0.250000 0.010000 56 | vt 0.203178 0.014612 57 | vt 0.158156 0.028269 58 | vt 0.116663 0.050447 59 | vt 0.080294 0.080294 60 | vt 0.050447 0.116663 61 | vt 0.028269 0.158156 62 | vt 0.014612 0.203178 63 | vt 0.010000 0.250000 64 | vt 0.014612 0.296822 65 | vt 0.028269 0.341844 66 | vt 0.050447 0.383337 67 | vt 0.080294 0.419706 68 | vt 0.116663 0.449553 69 | vt 0.158156 0.471731 70 | vt 0.750000 0.490000 71 | vt 0.796822 0.485388 72 | vt 0.841844 0.471731 73 | vt 0.883337 0.449553 74 | vt 0.919706 0.419706 75 | vt 0.949553 0.383337 76 | vt 0.971731 0.341844 77 | vt 0.985388 0.296822 78 | vt 0.990000 0.250000 79 | vt 0.985388 0.203178 80 | vt 0.971731 0.158156 81 | vt 0.949553 0.116663 82 | vt 0.919706 0.080294 83 | vt 0.883337 0.050447 84 | vt 0.841844 0.028269 85 | vt 0.796822 0.014612 86 | vt 0.750000 0.010000 87 | vt 0.703178 0.014612 88 | vt 0.658156 0.028269 89 | vt 0.616663 0.050447 90 | vt 0.580294 0.080294 91 | vt 0.550447 0.116663 92 | vt 0.528269 0.158156 93 | vt 0.514612 0.203178 94 | vt 0.510000 0.250000 95 | vt 0.514612 0.296822 96 | vt 0.528269 0.341844 97 | vt 0.550447 0.383337 98 | vt 0.580294 0.419706 99 | vt 0.616663 0.449553 100 | vt 0.658156 0.471731 101 | vt 0.703178 0.485388 102 | vt 0.203178 0.485388 103 | vn 0.0000 -0.5033 -0.8641 104 | vn 0.0000 1.0000 0.0000 105 | vn 0.1686 -0.5033 -0.8475 106 | vn 0.3307 -0.5033 -0.7983 107 | vn 0.4801 -0.5033 -0.7185 108 | vn 0.6110 -0.5033 -0.6110 109 | vn 0.7185 -0.5033 -0.4801 110 | vn 0.7983 -0.5033 -0.3307 111 | vn 0.8475 -0.5033 -0.1686 112 | vn 0.8641 -0.5033 0.0000 113 | vn 0.8475 -0.5033 0.1686 114 | vn 0.7983 -0.5033 0.3307 115 | vn 0.7185 -0.5033 0.4801 116 | vn 0.6110 -0.5033 0.6110 117 | vn 0.4801 -0.5033 0.7185 118 | vn 0.3307 -0.5033 0.7983 119 | vn 0.1686 -0.5033 0.8475 120 | vn 0.0000 -0.5033 0.8641 121 | vn -0.1686 -0.5033 0.8475 122 | vn -0.3307 -0.5033 0.7983 123 | vn -0.4801 -0.5033 0.7185 124 | vn -0.6110 -0.5033 0.6110 125 | vn -0.7185 -0.5033 0.4801 126 | vn -0.7983 -0.5033 0.3307 127 | vn -0.8475 -0.5033 0.1686 128 | vn -0.8641 -0.5033 0.0000 129 | vn -0.8475 -0.5033 -0.1686 130 | vn -0.7983 -0.5033 -0.3307 131 | vn -0.7185 -0.5033 -0.4801 132 | vn -0.6110 -0.5033 -0.6110 133 | vn -0.4801 -0.5033 -0.7185 134 | vn -0.3307 -0.5033 -0.7983 135 | vn -0.1686 -0.5033 -0.8475 136 | usemtl None 137 | s 1 138 | f 1/1/1 33/2/2 2/3/3 139 | f 2/3/3 33/2/2 3/4/4 140 | f 3/4/4 33/2/2 4/5/5 141 | f 4/5/5 33/2/2 5/6/6 142 | f 5/6/6 33/2/2 6/7/7 143 | f 6/7/7 33/2/2 7/8/8 144 | f 7/8/8 33/2/2 8/9/9 145 | f 8/9/9 33/2/2 9/10/10 146 | f 9/10/10 33/2/2 10/11/11 147 | f 10/11/11 33/2/2 11/12/12 148 | f 11/12/12 33/2/2 12/13/13 149 | f 12/13/13 33/2/2 13/14/14 150 | f 13/14/14 33/2/2 14/15/15 151 | f 14/15/15 33/2/2 15/16/16 152 | f 15/16/16 33/2/2 16/17/17 153 | f 16/17/17 33/2/2 17/18/18 154 | f 17/18/18 33/2/2 18/19/19 155 | f 18/19/19 33/2/2 19/20/20 156 | f 19/20/20 33/2/2 20/21/21 157 | f 20/21/21 33/2/2 21/22/22 158 | f 21/22/22 33/2/2 22/23/23 159 | f 22/23/23 33/2/2 23/24/24 160 | f 23/24/24 33/2/2 24/25/25 161 | f 24/25/25 33/2/2 25/26/26 162 | f 25/26/26 33/2/2 26/27/27 163 | f 26/27/27 33/2/2 27/28/28 164 | f 27/28/28 33/2/2 28/29/29 165 | f 28/29/29 33/2/2 29/30/30 166 | f 29/30/30 33/2/2 30/31/31 167 | f 30/31/31 33/2/2 31/32/32 168 | f 1/33/1 2/34/3 3/35/4 4/36/5 5/37/6 6/38/7 7/39/8 8/40/9 9/41/10 10/42/11 11/43/12 12/44/13 13/45/14 14/46/15 15/47/16 16/48/17 17/49/18 18/50/19 19/51/20 20/52/21 21/53/22 22/54/23 23/55/24 24/56/25 25/57/26 26/58/27 27/59/28 28/60/29 29/61/30 30/62/31 31/63/32 32/64/33 169 | f 31/32/32 33/2/2 32/65/33 170 | f 32/65/33 33/2/2 1/1/1 171 | -------------------------------------------------------------------------------- /Unity Code/Component Based Model/Visualisation Tools/ArrowSettings.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | using UnityEditor; 5 | 6 | class ArrowSettings : ScriptableObject 7 | { 8 | // Using a horrible serialisation method here so that we can have our settings 9 | // appear in the project settings window. It is ugly as hell. 10 | public const string k_ArrowSettingsPath = "Assets/ArrowSettings.asset"; 11 | 12 | static ArrowSettings singleton; 13 | public static ArrowSettings Singleton() 14 | { 15 | if (singleton == null) 16 | GetSerializedSettings(); 17 | 18 | return singleton; 19 | } 20 | 21 | // New values 22 | [SerializeField, Tooltip("The overall size of the arrow")] 23 | public float scale = 1f; 24 | [SerializeField, Tooltip("Multiplier of the arrow's total length based on the value it represents. Length (m) = sensitivty * value")] 25 | public float sensitivity = 1f; 26 | 27 | // Old values 28 | [SerializeField] 29 | public float arrowAspectRatio = 0.05f; 30 | [SerializeField] 31 | public float arrowHeadFractionOfTotalLength = 0.2f; 32 | [SerializeField] 33 | public float arrowAlpha = 0.5f; 34 | [SerializeField] 35 | public float wedgeAlpha = 0.5f; 36 | [SerializeField] 37 | public Color liftColour = new Color(0, 1, 0, 1); 38 | [SerializeField] 39 | public Color dragColour = new Color(1, 0, 0, 1); 40 | [SerializeField] 41 | public Color windColour = new Color(0, 1, 1, 1); 42 | [SerializeField] 43 | public Color alphaColour = new Color(1, 1, 0, 1); 44 | [SerializeField] 45 | public Color betaColour = new Color(1, 0, 1, 1); 46 | 47 | internal static ArrowSettings GetOrCreateSettings() 48 | { 49 | ArrowSettings settings = AssetDatabase.LoadAssetAtPath(k_ArrowSettingsPath); 50 | if (settings == null) 51 | { 52 | settings = ScriptableObject.CreateInstance(); 53 | 54 | // Default new values 55 | settings.scale = 1f; 56 | settings.sensitivity = 1f; 57 | 58 | // Default old values 59 | settings.arrowAspectRatio = 0.05f; 60 | settings.arrowHeadFractionOfTotalLength = 0.2f; 61 | settings.arrowAlpha = 0.5f; 62 | settings.wedgeAlpha = 0.4f; 63 | settings.liftColour = new Color(0, 1, 0, settings.arrowAlpha); 64 | settings.dragColour = new Color(1, 0, 0, settings.arrowAlpha); 65 | settings.windColour = new Color(0, 1, 1, settings.arrowAlpha); 66 | settings.alphaColour = new Color(1, 1, 0, settings.wedgeAlpha); 67 | settings.betaColour = new Color(1, 0, 1, settings.wedgeAlpha); 68 | 69 | AssetDatabase.CreateAsset(settings, k_ArrowSettingsPath); 70 | AssetDatabase.SaveAssets(); 71 | } 72 | 73 | ArrowSettings.singleton = settings; 74 | 75 | return settings; 76 | } 77 | 78 | 79 | 80 | internal static SerializedObject GetSerializedSettings() 81 | { 82 | return new SerializedObject(GetOrCreateSettings()); 83 | } 84 | } 85 | 86 | // Register a SettingsProvider using IMGUI for the drawing framework: 87 | static class MyCustomSettingsIMGUIRegister 88 | { 89 | [SettingsProvider] 90 | public static SettingsProvider CreateMyCustomSettingsProvider() 91 | { 92 | // First parameter is the path in the Settings window. 93 | // Second parameter is the scope of this setting: it only appears in the Project Settings window. 94 | var provider = new SettingsProvider("Project/AerodynamicArrows", SettingsScope.Project) 95 | { 96 | // By default the last token of the path is used as display name if no label is provided. 97 | label = "Aerodynamic Arrows", 98 | // Create the SettingsProvider and initialize its drawing (IMGUI) function in place: 99 | guiHandler = (searchContext) => 100 | { 101 | var settings = ArrowSettings.GetSerializedSettings(); 102 | 103 | // New values 104 | EditorGUILayout.PropertyField(settings.FindProperty("scale"), new GUIContent("Scale")); 105 | EditorGUILayout.PropertyField(settings.FindProperty("sensitivity"), new GUIContent("Sensitivity")); 106 | 107 | 108 | // Old values 109 | EditorGUILayout.PropertyField(settings.FindProperty("arrowAspectRatio"), new GUIContent("Aspect Ratio")); 110 | EditorGUILayout.PropertyField(settings.FindProperty("arrowHeadFractionOfTotalLength"), new GUIContent("Head length as a fraction of total length")); 111 | //EditorGUILayout.PropertyField(settings.FindProperty("arrowAlpha"), new GUIContent("Alpha")); 112 | EditorGUILayout.PropertyField(settings.FindProperty("liftColour"), new GUIContent("Lift Colour")); 113 | EditorGUILayout.PropertyField(settings.FindProperty("dragColour"), new GUIContent("Drag Colour")); 114 | EditorGUILayout.PropertyField(settings.FindProperty("windColour"), new GUIContent("Wind Colour")); 115 | EditorGUILayout.PropertyField(settings.FindProperty("alphaColour"), new GUIContent("Angle of Attack Colour")); 116 | EditorGUILayout.PropertyField(settings.FindProperty("betaColour"), new GUIContent("Sideslip Angle Colour")); 117 | 118 | settings.ApplyModifiedPropertiesWithoutUndo(); 119 | }, 120 | 121 | // Populate the search keywords to enable smart search filtering and label highlighting: 122 | keywords = new HashSet(new[] { "Arrow" }) 123 | }; 124 | 125 | return provider; 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /Unity Code/Component Based Model/Aero Components/ThinAerofoilComponent.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | public class ThinAerofoilComponent : AerodynamicComponent 6 | { 7 | // Thin Aero Foil aerodynamics uses a moving centre of pressure 8 | // and a normal force model to determine the lift and moment due to lift 9 | // Only induced drag is added by this component! 10 | 11 | public float aspectRatioCorrection_kAR, thicknessCorrection_kt; // (dimensionless) 12 | const float thicknessCorrection_labdat = 6f; // (dimensionless) 13 | // Blend between pre and post stall 14 | public float stallAngle, alpha_0, effective_alpha; // (rad) 15 | public float upperSigmoid, lowerSigmoid; // (dimensionless) 16 | public float preStallFilter; // (dimensionless) 17 | public float stallAngleMin = 15f; // (deg) 18 | public float stallAngleMax = 35f; // (deg) 19 | public float stallSharpness = 0.75f; // (dimensionless) 20 | 21 | // Lift 22 | public float CL; // (dimensionless) 23 | public float CZmax = 1f; // (dimensionless) 24 | public float liftCurveSlope; // (dimensionless) 25 | public float CL_preStall, CL_postStall; // (dimensionless) 26 | public float CD_induced; // (dimensionless) 27 | 28 | // Moment due to lift and camber 29 | public float CM; // (dimensionless) 30 | public float CM_0, CM_delta; // (dimensionless) 31 | public float CoP_z; // (m) 32 | 33 | public Vector3 lift_bodyFrame; // (Nm) 34 | public Vector3 inducedDrag_bodyFrame; // (Nm) 35 | public Vector3 centreOfPressure_earth; // (m) 36 | 37 | public override void RunModel(AeroBody aeroBody) 38 | { 39 | // THIS IS AN AWFUL FIX BUT IT WILL DO FOR NOW 40 | // Get the aspect ratio of the body, if it is part of a group/wing then use the AR of the group instead 41 | float aspectRatio = (aeroBody.myGroup == null) ? aeroBody.EAB.aspectRatio : aeroBody.myGroup.aspectRatio; 42 | 43 | // Prandtyl Theory 44 | // Clamp lower value to min AR of 2. Otherwise lift curve slope gets lower than sin 2 alpha which is non physical 45 | 46 | // ============ Might be no need to check for this ================= 47 | // Check for the divide by zero, although I think a zero AR means bigger problems anyway 48 | aspectRatioCorrection_kAR = (aspectRatio == 0f) ? 0f : Mathf.Clamp(aspectRatio / (2f + aspectRatio), 0f, 1f); 49 | // ================================================================= 50 | //aspectRatioCorrection_kAR = Mathf.Clamp(aspectRatio / (2f + aspectRatio), 0f, 1f); 51 | 52 | 53 | // This value needs checking for thickness to chord ratio of 1 54 | // Empirical correction to account for viscous effects across all thickness to chord ratios 55 | thicknessCorrection_kt = Mathf.Exp(-thicknessCorrection_labdat * aeroBody.EAB.thicknessToChordRatio_bOverc * aeroBody.EAB.thicknessToChordRatio_bOverc); 56 | 57 | // Emperical relation to allow for viscous effects 58 | // This could do with being in radians! 59 | stallAngle = stallAngleMin + (stallAngleMax - stallAngleMin) * Mathf.Exp(-aspectRatio / 2f); 60 | 61 | // Lifting line theory 62 | liftCurveSlope = 2f * Mathf.PI * aspectRatioCorrection_kAR * thicknessCorrection_kt; 63 | 64 | // Zero lift angle is set based on the amount of camber. This is physics based 65 | alpha_0 = -aeroBody.EAB.camberRatio; 66 | effective_alpha = aeroBody.alpha - alpha_0; 67 | 68 | float cosAlpha = Mathf.Cos(effective_alpha); 69 | 70 | // Lift before and after stall 71 | CL_preStall = liftCurveSlope * effective_alpha; 72 | 73 | // This could be simplified to (Vx * Vy) / V^2 74 | // Based on sin(2a) == sin(a)cos(a) 75 | // sin(a) = Vy/V 76 | // cos(a) = Vx/V 77 | // This simplification would remove the need for a trig function! 78 | CL_postStall = 0.5f * CZmax * thicknessCorrection_kt * Mathf.Sin(2f * effective_alpha); 79 | 80 | // Sigmoid function for blending between pre and post stall 81 | // Wasting some calulcations here by converting to degrees... 82 | 83 | // REMOVED EFFECTIVE ALPHA HERE 84 | upperSigmoid = 1f / (1f + Mathf.Exp((stallAngle + Mathf.Rad2Deg * aeroBody.alpha) * stallSharpness)); 85 | lowerSigmoid = 1f / (1f + Mathf.Exp((-stallAngle + Mathf.Rad2Deg * aeroBody.alpha) * stallSharpness)); 86 | preStallFilter = lowerSigmoid - upperSigmoid; 87 | 88 | CL = preStallFilter * CL_preStall + (1 - preStallFilter) * CL_postStall; 89 | 90 | // Induced drag 91 | CD_induced = (1f / (Mathf.PI * aspectRatio)) * CL * CL; 92 | 93 | // Pitching moment at mid chord due to camber only active pre stall 94 | CM_0 = 0.25f * -liftCurveSlope * alpha_0 * preStallFilter; 95 | 96 | // Original equation for this is: z_cop = c/8 * (cos(2a) + 1) 97 | // Using trig identity for cos(2a) = 2*cos^2(a) - 1 to save on extra trig computations 98 | // Also, ax_c is the axis, not diameter so we x2 again 99 | CoP_z = 0.5f * aeroBody.EAB.midAxis * cosAlpha * cosAlpha; 100 | 101 | // Pitching moment because lift is applied at the centre of the body 102 | CM_delta = CL * CoP_z * cosAlpha / aeroBody.EAB.midAxis; 103 | CM = CM_0 + CM_delta; 104 | 105 | // Convert coefficients to forces and moments 106 | float qS = aeroBody.dynamicPressure * aeroBody.planformArea; 107 | Vector3 liftDirection = Vector3.Cross(aeroBody.aeroBodyFrame.windVelocity_normalised, aeroBody.angleOfAttackRotationVector).normalized; 108 | lift_bodyFrame = qS * CL * liftDirection; 109 | inducedDrag_bodyFrame = -CD_induced * qS * aeroBody.aeroBodyFrame.windVelocity_normalised; 110 | resultantForce_bodyFrame = lift_bodyFrame + inducedDrag_bodyFrame; 111 | 112 | // New addition here... 113 | forcePointOfAction_earthFrame = aeroBody.transform.position + aeroBody.TransformDirectionEABToEarth(new Vector3(0, 0, CoP_z)); 114 | resultantForce_earthFrame = aeroBody.TransformDirectionBodyToEarth(resultantForce_bodyFrame); 115 | 116 | // We won't need to compute the moments anymore - just leaving it in for now so nothing else breaks! 117 | 118 | // The minus sign here is dirty but I can't figure out why the pitching moment is always in the wrong direction?! 119 | //resultantMoment_bodyFrame = new Vector3(-CM * qS * aeroBody.EAB.chord_c, 0, 0); 120 | //resultantMoment_bodyFrame = aeroBody.TransformDirectionEABToBody(resultantMoment_bodyFrame); 121 | 122 | 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /Unity Code/Component Based Model/Visualisation Tools/ComponentArrows.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | public class ComponentArrows : MonoBehaviour 6 | { 7 | // This script will be responsible for adding arrows to the various areodynamic components 8 | // each arrow will need to be properly scaled, coloured and faded 9 | // For this there will be a static ArrowSettings class which has all the colour information 10 | // as well as scaling rules and such 11 | 12 | // Scale, sensitivity and offset should be up here but also need to be accessible to the Arrow class 13 | // and I can't be bothered just passing those values in every time, maybe the functions for arrows 14 | // should be out here too and the Arrow class can just be a data structure. 15 | 16 | [Tooltip("Distance between the arrow head and the point of action")] 17 | public float offset = 0f; 18 | [Tooltip("Does the arrow point towards the point of action or away from it")] 19 | public bool pointAtPoint; 20 | [Tooltip("Use the coefficient for the aerodynamic force to scale the length of the arrow?" + 21 | "If false then the force is used to scale the arrow length. Wind arrows will be normalised to a direction vector is this is true.")] 22 | public bool useCoefficientForScale; 23 | 24 | public float scale { get { return ArrowSettings.Singleton().scale; } } 25 | public float sensitivity { get { return ArrowSettings.Singleton().sensitivity; } } 26 | public float arrowHeadFractionOfTotalLength { get { return ArrowSettings.Singleton().arrowHeadFractionOfTotalLength; } } 27 | 28 | 29 | 30 | public void SetArrowPositionAndRotation(Arrow arrow, float length, Vector3 rootPosition, Vector3 direction) 31 | { 32 | // Direction MUST BE NORMALISED 33 | direction.Normalize(); 34 | 35 | length = length * sensitivity; 36 | 37 | if (pointAtPoint) 38 | { 39 | rootPosition = rootPosition - length * direction; 40 | } 41 | 42 | // All the offsetting is handled in here so the higher up scripts just need to specify the point of action for the arrow 43 | arrow.body.position = rootPosition + offset*direction; 44 | arrow.body.up = direction; 45 | 46 | // This might become a fixed value rather than scaling the head proportionally like this 47 | arrow.body.localScale = new Vector3(scale, (1f - arrowHeadFractionOfTotalLength) * length, scale); 48 | 49 | arrow.head.position = arrow.body.position + direction * ((1f - arrowHeadFractionOfTotalLength) * length); 50 | arrow.head.up = direction; 51 | // This might become a fixed value rather than scaling the head proportionally like this 52 | arrow.head.localScale = new Vector3(2 * scale, arrowHeadFractionOfTotalLength * length, 2 * scale); 53 | 54 | } 55 | 56 | public void SetArrowPositionAndRotationFromVector(Arrow arrow, Vector3 vector, Vector3 rootPosition) 57 | { 58 | // This is just lazy so that we can pass in the initial vector and get the length and direction here instead 59 | // of having to do it in every function in the higher upss 60 | 61 | Vector3 direction = vector.normalized; 62 | float length = vector.magnitude * sensitivity; 63 | 64 | if (pointAtPoint) 65 | { 66 | rootPosition = rootPosition - length * direction; 67 | } 68 | 69 | // All the offsetting is handled in here so the higher up scripts just need to specify the point of action for the arrow 70 | arrow.body.position = rootPosition; 71 | arrow.body.up = direction; 72 | 73 | // This might become a fixed value rather than scaling the head proportionally like this 74 | arrow.body.localScale = new Vector3(scale, (1f - arrowHeadFractionOfTotalLength) * length, scale); 75 | 76 | arrow.head.position = arrow.body.position + direction * ((1f - arrowHeadFractionOfTotalLength) * length); 77 | arrow.head.up = direction; 78 | // This might become a fixed value rather than scaling the head proportionally like this 79 | arrow.head.localScale = new Vector3(2 * scale, arrowHeadFractionOfTotalLength * length, 2 * scale); 80 | } 81 | 82 | 83 | public class Arrow 84 | { 85 | // Need this class because a component can have more than one arrow attached to it. 86 | 87 | public Transform body; 88 | public Transform head; 89 | 90 | public Arrow(Color color) 91 | { 92 | GameObject bodyGO = Resources.Load("Arrow Body") as GameObject; 93 | GameObject headGO = Resources.Load("Arrow Head") as GameObject; 94 | 95 | int layerID = LayerMask.NameToLayer("Arrows"); 96 | if(layerID == -1) 97 | { 98 | Debug.LogError("No layer for Arrows was found. Please add a new layer named: Arrows"); 99 | } 100 | 101 | body = Instantiate(bodyGO).transform; 102 | head = Instantiate(headGO).transform; 103 | 104 | SetLayerRecursively(body.gameObject, layerID); 105 | SetLayerRecursively(head.gameObject, layerID); 106 | 107 | // Set colour and shader to fade 108 | body.GetChild(0).GetComponent().materials[0].color = color; 109 | head.GetChild(0).GetComponent().materials[0].color = color; 110 | body.GetChild(0).GetComponent().materials[0].ToFadeMode(); 111 | head.GetChild(0).GetComponent().materials[0].ToFadeMode(); 112 | } 113 | 114 | public Arrow(Color color, string name, Transform parent) 115 | { 116 | GameObject bodyGO = Resources.Load("Arrow Body") as GameObject; 117 | GameObject headGO = Resources.Load("Arrow Head") as GameObject; 118 | body = Instantiate(bodyGO).transform; 119 | head = Instantiate(headGO).transform; 120 | body.name = "Arrow Body"; 121 | head.name = "Arrow Head"; 122 | 123 | // Set colour and shader to fade 124 | body.GetChild(0).GetComponent().materials[0].color = color; 125 | head.GetChild(0).GetComponent().materials[0].color = color; 126 | body.GetChild(0).GetComponent().materials[0].ToFadeMode(); 127 | head.GetChild(0).GetComponent().materials[0].ToFadeMode(); 128 | 129 | // Create a root gameobject which holds both parts of the arrow, makes it neater in scene hierarchy 130 | GameObject arrow = new GameObject(name); 131 | Transform arrowT = arrow.transform; 132 | arrowT.position = parent.position; 133 | arrowT.rotation = parent.rotation; 134 | 135 | arrowT.SetParent(parent); 136 | 137 | body.SetParent(arrowT); 138 | head.SetParent(arrowT); 139 | 140 | int layerID = LayerMask.NameToLayer("Arrows"); 141 | if (layerID == -1) 142 | { 143 | Debug.LogError("No layer for Arrows was found. Please add a new layer named: Arrows"); 144 | } 145 | 146 | SetLayerRecursively(arrow, layerID); 147 | } 148 | 149 | void SetLayerRecursively(GameObject obj, int layer) 150 | { 151 | obj.layer = layer; 152 | 153 | foreach (Transform child in obj.transform) 154 | { 155 | SetLayerRecursively(child.gameObject, layer); 156 | } 157 | } 158 | } 159 | } -------------------------------------------------------------------------------- /Unity Code/Resources/Arrow Body.obj: -------------------------------------------------------------------------------- 1 | # Blender v2.93.5 OBJ File: '' 2 | # www.blender.org 3 | mtllib arrow body.mtl 4 | o Cylinder 5 | v 0.000000 0.000000 -0.500000 6 | v 0.000000 1.000000 -0.500000 7 | v 0.097545 0.000000 -0.490393 8 | v 0.097545 1.000000 -0.490393 9 | v 0.191342 0.000000 -0.461940 10 | v 0.191342 1.000000 -0.461940 11 | v 0.277785 0.000000 -0.415735 12 | v 0.277785 1.000000 -0.415735 13 | v 0.353553 0.000000 -0.353553 14 | v 0.353553 1.000000 -0.353553 15 | v 0.415735 0.000000 -0.277785 16 | v 0.415735 1.000000 -0.277785 17 | v 0.461940 0.000000 -0.191342 18 | v 0.461940 1.000000 -0.191342 19 | v 0.490393 0.000000 -0.097545 20 | v 0.490393 1.000000 -0.097545 21 | v 0.500000 0.000000 0.000000 22 | v 0.500000 1.000000 0.000000 23 | v 0.490393 0.000000 0.097545 24 | v 0.490393 1.000000 0.097545 25 | v 0.461940 0.000000 0.191342 26 | v 0.461940 1.000000 0.191342 27 | v 0.415735 0.000000 0.277785 28 | v 0.415735 1.000000 0.277785 29 | v 0.353553 0.000000 0.353553 30 | v 0.353553 1.000000 0.353553 31 | v 0.277785 0.000000 0.415735 32 | v 0.277785 1.000000 0.415735 33 | v 0.191342 0.000000 0.461940 34 | v 0.191342 1.000000 0.461940 35 | v 0.097545 0.000000 0.490393 36 | v 0.097545 1.000000 0.490393 37 | v -0.000000 0.000000 0.500000 38 | v -0.000000 1.000000 0.500000 39 | v -0.097545 0.000000 0.490393 40 | v -0.097545 1.000000 0.490393 41 | v -0.191342 0.000000 0.461940 42 | v -0.191342 1.000000 0.461940 43 | v -0.277785 0.000000 0.415735 44 | v -0.277785 1.000000 0.415735 45 | v -0.353553 0.000000 0.353553 46 | v -0.353553 1.000000 0.353553 47 | v -0.415735 0.000000 0.277785 48 | v -0.415735 1.000000 0.277785 49 | v -0.461940 0.000000 0.191342 50 | v -0.461940 1.000000 0.191342 51 | v -0.490393 0.000000 0.097545 52 | v -0.490393 1.000000 0.097545 53 | v -0.500000 0.000000 -0.000000 54 | v -0.500000 1.000000 -0.000000 55 | v -0.490393 0.000000 -0.097545 56 | v -0.490393 1.000000 -0.097545 57 | v -0.461940 0.000000 -0.191342 58 | v -0.461940 1.000000 -0.191342 59 | v -0.415735 0.000000 -0.277785 60 | v -0.415735 1.000000 -0.277785 61 | v -0.353553 0.000000 -0.353553 62 | v -0.353553 1.000000 -0.353553 63 | v -0.277785 0.000000 -0.415735 64 | v -0.277785 1.000000 -0.415735 65 | v -0.191342 0.000000 -0.461940 66 | v -0.191342 1.000000 -0.461940 67 | v -0.097545 0.000000 -0.490393 68 | v -0.097545 1.000000 -0.490393 69 | vt 1.000000 0.500000 70 | vt 1.000000 1.000000 71 | vt 0.968750 1.000000 72 | vt 0.968750 0.500000 73 | vt 0.937500 1.000000 74 | vt 0.937500 0.500000 75 | vt 0.906250 1.000000 76 | vt 0.906250 0.500000 77 | vt 0.875000 1.000000 78 | vt 0.875000 0.500000 79 | vt 0.843750 1.000000 80 | vt 0.843750 0.500000 81 | vt 0.812500 1.000000 82 | vt 0.812500 0.500000 83 | vt 0.781250 1.000000 84 | vt 0.781250 0.500000 85 | vt 0.750000 1.000000 86 | vt 0.750000 0.500000 87 | vt 0.718750 1.000000 88 | vt 0.718750 0.500000 89 | vt 0.687500 1.000000 90 | vt 0.687500 0.500000 91 | vt 0.656250 1.000000 92 | vt 0.656250 0.500000 93 | vt 0.625000 1.000000 94 | vt 0.625000 0.500000 95 | vt 0.593750 1.000000 96 | vt 0.593750 0.500000 97 | vt 0.562500 1.000000 98 | vt 0.562500 0.500000 99 | vt 0.531250 1.000000 100 | vt 0.531250 0.500000 101 | vt 0.500000 1.000000 102 | vt 0.500000 0.500000 103 | vt 0.468750 1.000000 104 | vt 0.468750 0.500000 105 | vt 0.437500 1.000000 106 | vt 0.437500 0.500000 107 | vt 0.406250 1.000000 108 | vt 0.406250 0.500000 109 | vt 0.375000 1.000000 110 | vt 0.375000 0.500000 111 | vt 0.343750 1.000000 112 | vt 0.343750 0.500000 113 | vt 0.312500 1.000000 114 | vt 0.312500 0.500000 115 | vt 0.281250 1.000000 116 | vt 0.281250 0.500000 117 | vt 0.250000 1.000000 118 | vt 0.250000 0.500000 119 | vt 0.218750 1.000000 120 | vt 0.218750 0.500000 121 | vt 0.187500 1.000000 122 | vt 0.187500 0.500000 123 | vt 0.156250 1.000000 124 | vt 0.156250 0.500000 125 | vt 0.125000 1.000000 126 | vt 0.125000 0.500000 127 | vt 0.093750 1.000000 128 | vt 0.093750 0.500000 129 | vt 0.062500 1.000000 130 | vt 0.062500 0.500000 131 | vt 0.031250 1.000000 132 | vt 0.031250 0.500000 133 | vt 0.000000 1.000000 134 | vt 0.000000 0.500000 135 | vt 0.750000 0.490000 136 | vt 0.796822 0.485388 137 | vt 0.841844 0.471731 138 | vt 0.883337 0.449553 139 | vt 0.919706 0.419706 140 | vt 0.949553 0.383337 141 | vt 0.971731 0.341844 142 | vt 0.985388 0.296822 143 | vt 0.990000 0.250000 144 | vt 0.985388 0.203178 145 | vt 0.971731 0.158156 146 | vt 0.949553 0.116663 147 | vt 0.919706 0.080294 148 | vt 0.883337 0.050447 149 | vt 0.841844 0.028269 150 | vt 0.796822 0.014612 151 | vt 0.750000 0.010000 152 | vt 0.703178 0.014612 153 | vt 0.658156 0.028269 154 | vt 0.616663 0.050447 155 | vt 0.580294 0.080294 156 | vt 0.550447 0.116663 157 | vt 0.528269 0.158156 158 | vt 0.514612 0.203178 159 | vt 0.510000 0.250000 160 | vt 0.514612 0.296822 161 | vt 0.528269 0.341844 162 | vt 0.550447 0.383337 163 | vt 0.580294 0.419706 164 | vt 0.616663 0.449553 165 | vt 0.658156 0.471731 166 | vt 0.703178 0.485388 167 | vn 0.0000 -0.6857 -0.7279 168 | vn 0.0000 0.0000 -1.0000 169 | vn 0.1951 0.0000 -0.9808 170 | vn 0.1420 -0.6857 -0.7139 171 | vn 0.3827 0.0000 -0.9239 172 | vn 0.2785 -0.6857 -0.6725 173 | vn 0.5556 0.0000 -0.8314 174 | vn 0.4044 -0.6857 -0.6052 175 | vn 0.7071 0.0000 -0.7071 176 | vn 0.5147 -0.6857 -0.5147 177 | vn 0.8314 0.0000 -0.5556 178 | vn 0.6052 -0.6857 -0.4044 179 | vn 0.9239 0.0000 -0.3827 180 | vn 0.6725 -0.6857 -0.2785 181 | vn 0.9808 0.0000 -0.1951 182 | vn 0.7139 -0.6857 -0.1420 183 | vn 1.0000 0.0000 0.0000 184 | vn 0.7279 -0.6857 0.0000 185 | vn 0.9808 0.0000 0.1951 186 | vn 0.7139 -0.6857 0.1420 187 | vn 0.9239 0.0000 0.3827 188 | vn 0.6725 -0.6857 0.2785 189 | vn 0.8314 0.0000 0.5556 190 | vn 0.6052 -0.6857 0.4044 191 | vn 0.7071 0.0000 0.7071 192 | vn 0.5147 -0.6857 0.5147 193 | vn 0.5556 0.0000 0.8314 194 | vn 0.4044 -0.6857 0.6052 195 | vn 0.3827 0.0000 0.9239 196 | vn 0.2785 -0.6857 0.6725 197 | vn 0.1951 0.0000 0.9808 198 | vn 0.1420 -0.6857 0.7139 199 | vn 0.0000 0.0000 1.0000 200 | vn 0.0000 -0.6857 0.7279 201 | vn -0.1951 0.0000 0.9808 202 | vn -0.1420 -0.6857 0.7139 203 | vn -0.3827 0.0000 0.9239 204 | vn -0.2785 -0.6857 0.6725 205 | vn -0.5556 0.0000 0.8314 206 | vn -0.4044 -0.6857 0.6052 207 | vn -0.7071 0.0000 0.7071 208 | vn -0.5147 -0.6857 0.5147 209 | vn -0.8314 0.0000 0.5556 210 | vn -0.6052 -0.6857 0.4044 211 | vn -0.9239 0.0000 0.3827 212 | vn -0.6725 -0.6857 0.2785 213 | vn -0.9808 0.0000 0.1951 214 | vn -0.7139 -0.6857 0.1420 215 | vn -1.0000 0.0000 0.0000 216 | vn -0.7279 -0.6857 0.0000 217 | vn -0.9808 0.0000 -0.1951 218 | vn -0.7139 -0.6857 -0.1420 219 | vn -0.9239 0.0000 -0.3827 220 | vn -0.6725 -0.6857 -0.2785 221 | vn -0.8314 0.0000 -0.5556 222 | vn -0.6052 -0.6857 -0.4044 223 | vn -0.7071 0.0000 -0.7071 224 | vn -0.5147 -0.6857 -0.5147 225 | vn -0.5556 0.0000 -0.8314 226 | vn -0.4044 -0.6857 -0.6052 227 | vn -0.3827 0.0000 -0.9239 228 | vn -0.2785 -0.6857 -0.6725 229 | vn -0.1951 0.0000 -0.9808 230 | vn -0.1420 -0.6857 -0.7139 231 | usemtl None 232 | s 1 233 | f 1/1/1 2/2/2 4/3/3 3/4/4 234 | f 3/4/4 4/3/3 6/5/5 5/6/6 235 | f 5/6/6 6/5/5 8/7/7 7/8/8 236 | f 7/8/8 8/7/7 10/9/9 9/10/10 237 | f 9/10/10 10/9/9 12/11/11 11/12/12 238 | f 11/12/12 12/11/11 14/13/13 13/14/14 239 | f 13/14/14 14/13/13 16/15/15 15/16/16 240 | f 15/16/16 16/15/15 18/17/17 17/18/18 241 | f 17/18/18 18/17/17 20/19/19 19/20/20 242 | f 19/20/20 20/19/19 22/21/21 21/22/22 243 | f 21/22/22 22/21/21 24/23/23 23/24/24 244 | f 23/24/24 24/23/23 26/25/25 25/26/26 245 | f 25/26/26 26/25/25 28/27/27 27/28/28 246 | f 27/28/28 28/27/27 30/29/29 29/30/30 247 | f 29/30/30 30/29/29 32/31/31 31/32/32 248 | f 31/32/32 32/31/31 34/33/33 33/34/34 249 | f 33/34/34 34/33/33 36/35/35 35/36/36 250 | f 35/36/36 36/35/35 38/37/37 37/38/38 251 | f 37/38/38 38/37/37 40/39/39 39/40/40 252 | f 39/40/40 40/39/39 42/41/41 41/42/42 253 | f 41/42/42 42/41/41 44/43/43 43/44/44 254 | f 43/44/44 44/43/43 46/45/45 45/46/46 255 | f 45/46/46 46/45/45 48/47/47 47/48/48 256 | f 47/48/48 48/47/47 50/49/49 49/50/50 257 | f 49/50/50 50/49/49 52/51/51 51/52/52 258 | f 51/52/52 52/51/51 54/53/53 53/54/54 259 | f 53/54/54 54/53/53 56/55/55 55/56/56 260 | f 55/56/56 56/55/55 58/57/57 57/58/58 261 | f 57/58/58 58/57/57 60/59/59 59/60/60 262 | f 59/60/60 60/59/59 62/61/61 61/62/62 263 | f 61/62/62 62/61/61 64/63/63 63/64/64 264 | f 63/64/64 64/63/63 2/65/2 1/66/1 265 | f 1/67/1 3/68/4 5/69/6 7/70/8 9/71/10 11/72/12 13/73/14 15/74/16 17/75/18 19/76/20 21/77/22 23/78/24 25/79/26 27/80/28 29/81/30 31/82/32 33/83/34 35/84/36 37/85/38 39/86/40 41/87/42 43/88/44 45/89/46 47/90/48 49/91/50 51/92/52 53/93/54 55/94/56 57/95/58 59/96/60 61/97/62 63/98/64 266 | -------------------------------------------------------------------------------- /Unity Code/Resources/Sphere.obj: -------------------------------------------------------------------------------- 1 | # Blender v2.93.5 OBJ File: 'Sphere.blend' 2 | # www.blender.org 3 | mtllib Sphere.mtl 4 | o Sphere_Sphere.001 5 | v 0.000000 0.461940 -0.191342 6 | v 0.000000 0.353553 -0.353553 7 | v 0.000000 0.191342 -0.461940 8 | v 0.000000 -0.000000 -0.500000 9 | v 0.000000 -0.191342 -0.461940 10 | v 0.000000 -0.353553 -0.353553 11 | v 0.000000 -0.461940 -0.191342 12 | v 0.135299 0.461940 -0.135299 13 | v 0.250000 0.353553 -0.250000 14 | v 0.326641 0.191342 -0.326641 15 | v 0.353553 -0.000000 -0.353553 16 | v 0.326641 -0.191342 -0.326641 17 | v 0.250000 -0.353553 -0.250000 18 | v 0.135299 -0.461940 -0.135299 19 | v 0.191342 0.461940 0.000000 20 | v 0.353553 0.353553 0.000000 21 | v 0.461940 0.191342 0.000000 22 | v 0.500000 -0.000000 0.000000 23 | v 0.461940 -0.191342 0.000000 24 | v 0.353553 -0.353553 0.000000 25 | v 0.191342 -0.461940 0.000000 26 | v 0.135299 0.461940 0.135299 27 | v 0.250000 0.353553 0.250000 28 | v 0.326641 0.191342 0.326641 29 | v 0.353553 -0.000000 0.353553 30 | v 0.326641 -0.191342 0.326641 31 | v 0.250000 -0.353553 0.250000 32 | v 0.135299 -0.461940 0.135299 33 | v 0.000000 0.461940 0.191342 34 | v 0.000000 0.353553 0.353553 35 | v 0.000000 0.191342 0.461940 36 | v 0.000000 -0.000000 0.500000 37 | v 0.000000 -0.191342 0.461940 38 | v 0.000000 -0.353553 0.353553 39 | v 0.000000 -0.461940 0.191342 40 | v -0.000000 0.500000 0.000000 41 | v -0.135299 0.461940 0.135299 42 | v -0.250000 0.353553 0.250000 43 | v -0.326641 0.191342 0.326641 44 | v -0.353553 -0.000000 0.353553 45 | v -0.326641 -0.191342 0.326641 46 | v -0.250000 -0.353553 0.250000 47 | v -0.135299 -0.461940 0.135299 48 | v -0.191342 0.461940 0.000000 49 | v -0.353553 0.353553 0.000000 50 | v -0.461940 0.191342 0.000000 51 | v -0.500000 -0.000000 0.000000 52 | v -0.461940 -0.191342 0.000000 53 | v -0.353553 -0.353553 0.000000 54 | v -0.191342 -0.461940 0.000000 55 | v 0.000000 -0.500000 0.000000 56 | v -0.135299 0.461940 -0.135299 57 | v -0.250000 0.353553 -0.250000 58 | v -0.326641 0.191342 -0.326641 59 | v -0.353553 -0.000000 -0.353553 60 | v -0.326641 -0.191342 -0.326641 61 | v -0.250000 -0.353553 -0.250000 62 | v -0.135299 -0.461940 -0.135299 63 | vt 0.750000 0.500000 64 | vt 0.750000 0.625000 65 | vt 0.625000 0.625000 66 | vt 0.625000 0.500000 67 | vt 0.750000 0.750000 68 | vt 0.750000 0.875000 69 | vt 0.625000 0.875000 70 | vt 0.625000 0.750000 71 | vt 0.750000 0.125000 72 | vt 0.750000 0.250000 73 | vt 0.625000 0.250000 74 | vt 0.625000 0.125000 75 | vt 0.750000 0.375000 76 | vt 0.625000 0.375000 77 | vt 0.687500 1.000000 78 | vt 0.687500 0.000000 79 | vt 0.500000 0.250000 80 | vt 0.500000 0.125000 81 | vt 0.500000 0.500000 82 | vt 0.500000 0.375000 83 | vt 0.500000 0.750000 84 | vt 0.500000 0.625000 85 | vt 0.562500 1.000000 86 | vt 0.500000 0.875000 87 | vt 0.562500 0.000000 88 | vt 0.437500 1.000000 89 | vt 0.375000 0.875000 90 | vt 0.437500 0.000000 91 | vt 0.375000 0.125000 92 | vt 0.375000 0.375000 93 | vt 0.375000 0.250000 94 | vt 0.375000 0.625000 95 | vt 0.375000 0.500000 96 | vt 0.375000 0.750000 97 | vt 0.250000 0.375000 98 | vt 0.250000 0.250000 99 | vt 0.250000 0.625000 100 | vt 0.250000 0.500000 101 | vt 0.250000 0.875000 102 | vt 0.250000 0.750000 103 | vt 0.250000 0.125000 104 | vt 0.312500 1.000000 105 | vt 0.312500 0.000000 106 | vt 0.125000 0.250000 107 | vt 0.125000 0.125000 108 | vt 0.125000 0.500000 109 | vt 0.125000 0.375000 110 | vt 0.125000 0.750000 111 | vt 0.125000 0.625000 112 | vt 0.187500 1.000000 113 | vt 0.125000 0.875000 114 | vt 0.187500 0.000000 115 | vt 0.000000 0.500000 116 | vt 0.000000 0.375000 117 | vt 0.000000 0.750000 118 | vt 0.000000 0.625000 119 | vt 0.062500 1.000000 120 | vt 0.000000 0.875000 121 | vt 0.062500 0.000000 122 | vt 0.000000 0.125000 123 | vt 0.000000 0.250000 124 | vt 0.937500 0.000000 125 | vt 1.000000 0.125000 126 | vt 0.875000 0.125000 127 | vt 1.000000 0.250000 128 | vt 1.000000 0.375000 129 | vt 0.875000 0.375000 130 | vt 0.875000 0.250000 131 | vt 1.000000 0.500000 132 | vt 1.000000 0.625000 133 | vt 0.875000 0.625000 134 | vt 0.875000 0.500000 135 | vt 1.000000 0.750000 136 | vt 1.000000 0.875000 137 | vt 0.875000 0.875000 138 | vt 0.875000 0.750000 139 | vt 0.937500 1.000000 140 | vt 0.812500 1.000000 141 | vt 0.812500 0.000000 142 | vn 0.3764 0.1807 -0.9087 143 | vn 0.2243 0.8103 -0.5414 144 | vn 0.2243 -0.8103 -0.5414 145 | vn 0.3764 -0.1807 -0.9087 146 | vn 0.3256 0.5253 -0.7862 147 | vn 0.0805 0.9776 -0.1945 148 | vn 0.0805 -0.9776 -0.1945 149 | vn 0.3256 -0.5253 -0.7862 150 | vn 0.5414 -0.8103 -0.2243 151 | vn 0.9087 -0.1807 -0.3764 152 | vn 0.7862 0.5253 -0.3256 153 | vn 0.1945 0.9776 -0.0805 154 | vn 0.1945 -0.9776 -0.0805 155 | vn 0.7862 -0.5253 -0.3256 156 | vn 0.9087 0.1807 -0.3764 157 | vn 0.5414 0.8103 -0.2243 158 | vn 0.1945 0.9776 0.0805 159 | vn 0.1945 -0.9776 0.0805 160 | vn 0.7862 -0.5253 0.3256 161 | vn 0.9087 0.1807 0.3764 162 | vn 0.5414 0.8103 0.2243 163 | vn 0.5414 -0.8103 0.2243 164 | vn 0.9087 -0.1807 0.3764 165 | vn 0.7862 0.5253 0.3256 166 | vn 0.3256 -0.5253 0.7862 167 | vn 0.3764 0.1807 0.9087 168 | vn 0.2243 0.8103 0.5414 169 | vn 0.2243 -0.8103 0.5414 170 | vn 0.3764 -0.1807 0.9087 171 | vn 0.3256 0.5253 0.7862 172 | vn 0.0805 0.9776 0.1945 173 | vn 0.0805 -0.9776 0.1945 174 | vn -0.2243 -0.8103 0.5414 175 | vn -0.3764 -0.1807 0.9087 176 | vn -0.3256 0.5253 0.7862 177 | vn -0.0805 0.9776 0.1945 178 | vn -0.0805 -0.9776 0.1945 179 | vn -0.3256 -0.5253 0.7862 180 | vn -0.3764 0.1807 0.9087 181 | vn -0.2243 0.8103 0.5414 182 | vn -0.9087 -0.1807 0.3764 183 | vn -0.7862 0.5253 0.3256 184 | vn -0.1945 0.9776 0.0805 185 | vn -0.1945 -0.9776 0.0805 186 | vn -0.7862 -0.5253 0.3256 187 | vn -0.9087 0.1807 0.3764 188 | vn -0.5414 0.8103 0.2243 189 | vn -0.5414 -0.8103 0.2243 190 | vn -0.1945 -0.9776 -0.0805 191 | vn -0.7862 -0.5253 -0.3256 192 | vn -0.9087 0.1807 -0.3764 193 | vn -0.5414 0.8103 -0.2243 194 | vn -0.5414 -0.8103 -0.2243 195 | vn -0.9087 -0.1807 -0.3764 196 | vn -0.7862 0.5253 -0.3256 197 | vn -0.1945 0.9776 -0.0805 198 | vn -0.3764 0.1807 -0.9087 199 | vn -0.2243 0.8103 -0.5414 200 | vn -0.2243 -0.8103 -0.5414 201 | vn -0.3764 -0.1807 -0.9087 202 | vn -0.3256 0.5253 -0.7862 203 | vn -0.0805 0.9776 -0.1945 204 | vn -0.0805 -0.9776 -0.1945 205 | vn -0.3256 -0.5253 -0.7862 206 | usemtl None 207 | s off 208 | f 4/1/1 3/2/1 10/3/1 11/4/1 209 | f 2/5/2 1/6/2 8/7/2 9/8/2 210 | f 7/9/3 6/10/3 13/11/3 14/12/3 211 | f 5/13/4 4/1/4 11/4/4 12/14/4 212 | f 3/2/5 2/5/5 9/8/5 10/3/5 213 | f 1/6/6 36/15/6 8/7/6 214 | f 51/16/7 7/9/7 14/12/7 215 | f 6/10/8 5/13/8 12/14/8 13/11/8 216 | f 14/12/9 13/11/9 20/17/9 21/18/9 217 | f 12/14/10 11/4/10 18/19/10 19/20/10 218 | f 10/3/11 9/8/11 16/21/11 17/22/11 219 | f 8/7/12 36/23/12 15/24/12 220 | f 51/25/13 14/12/13 21/18/13 221 | f 13/11/14 12/14/14 19/20/14 20/17/14 222 | f 11/4/15 10/3/15 17/22/15 18/19/15 223 | f 9/8/16 8/7/16 15/24/16 16/21/16 224 | f 15/24/17 36/26/17 22/27/17 225 | f 51/28/18 21/18/18 28/29/18 226 | f 20/17/19 19/20/19 26/30/19 27/31/19 227 | f 18/19/20 17/22/20 24/32/20 25/33/20 228 | f 16/21/21 15/24/21 22/27/21 23/34/21 229 | f 21/18/22 20/17/22 27/31/22 28/29/22 230 | f 19/20/23 18/19/23 25/33/23 26/30/23 231 | f 17/22/24 16/21/24 23/34/24 24/32/24 232 | f 27/31/25 26/30/25 33/35/25 34/36/25 233 | f 25/33/26 24/32/26 31/37/26 32/38/26 234 | f 23/34/27 22/27/27 29/39/27 30/40/27 235 | f 28/29/28 27/31/28 34/36/28 35/41/28 236 | f 26/30/29 25/33/29 32/38/29 33/35/29 237 | f 24/32/30 23/34/30 30/40/30 31/37/30 238 | f 22/27/31 36/42/31 29/39/31 239 | f 51/43/32 28/29/32 35/41/32 240 | f 35/41/33 34/36/33 42/44/33 43/45/33 241 | f 33/35/34 32/38/34 40/46/34 41/47/34 242 | f 31/37/35 30/40/35 38/48/35 39/49/35 243 | f 29/39/36 36/50/36 37/51/36 244 | f 51/52/37 35/41/37 43/45/37 245 | f 34/36/38 33/35/38 41/47/38 42/44/38 246 | f 32/38/39 31/37/39 39/49/39 40/46/39 247 | f 30/40/40 29/39/40 37/51/40 38/48/40 248 | f 41/47/41 40/46/41 47/53/41 48/54/41 249 | f 39/49/42 38/48/42 45/55/42 46/56/42 250 | f 37/51/43 36/57/43 44/58/43 251 | f 51/59/44 43/45/44 50/60/44 252 | f 42/44/45 41/47/45 48/54/45 49/61/45 253 | f 40/46/46 39/49/46 46/56/46 47/53/46 254 | f 38/48/47 37/51/47 44/58/47 45/55/47 255 | f 43/45/48 42/44/48 49/61/48 50/60/48 256 | f 51/62/49 50/63/49 58/64/49 257 | f 49/65/50 48/66/50 56/67/50 57/68/50 258 | f 47/69/51 46/70/51 54/71/51 55/72/51 259 | f 45/73/52 44/74/52 52/75/52 53/76/52 260 | f 50/63/53 49/65/53 57/68/53 58/64/53 261 | f 48/66/54 47/69/54 55/72/54 56/67/54 262 | f 46/70/55 45/73/55 53/76/55 54/71/55 263 | f 44/74/56 36/77/56 52/75/56 264 | f 55/72/57 54/71/57 3/2/57 4/1/57 265 | f 53/76/58 52/75/58 1/6/58 2/5/58 266 | f 58/64/59 57/68/59 6/10/59 7/9/59 267 | f 56/67/60 55/72/60 4/1/60 5/13/60 268 | f 54/71/61 53/76/61 2/5/61 3/2/61 269 | f 52/75/62 36/78/62 1/6/62 270 | f 51/79/63 58/64/63 7/9/63 271 | f 57/68/64 56/67/64 5/13/64 6/10/64 272 | -------------------------------------------------------------------------------- /Unity Code/Component Based Model/AeroBody.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | public class AeroBody : MonoBehaviour 6 | { 7 | /* The AeroBody class is responsible for: 8 | * - coordinate transformations 9 | * - resolution of the wind velocity 10 | * - tracking velocity of the body when a rigid body is not provided 11 | * 12 | * The AeroBody maintains: 13 | * - dimensions of the object 14 | * - resolved dimensions of the equivalent aerodynamic body 15 | * 16 | * Aerodynamics Components are added to an AeroBody to apply forces and moments to 17 | * the body based on its dimensions and the wind properties according to the component model 18 | * 19 | * For testing purposes all variables and functions are made public 20 | * This allows for easy probing of the aerodynamic model 21 | * 22 | */ 23 | 24 | // Debugging Values 25 | public Vector3 EAB_windVel; 26 | public Vector3 earthVelocity; 27 | 28 | // The fluid flow velocity which is not due to the object's velocity 29 | public Vector3 externalFlowVelocity_inEarthFrame; // (m/s) 30 | 31 | 32 | // Quick and dirty fix for grouping 33 | public AeroGroup myGroup = null; 34 | 35 | 36 | #region Component Computation and Apply Force Events 37 | 38 | /* The AeroBody has two events: 39 | * - Run Model Computations 40 | * - Apply Model Forces 41 | * 42 | * Run Model Computations is called after all the ellipsoid properties are computed and the 43 | * wind has been resolved. Essentially, all the information needed by each aerodynamic component 44 | * is ready to be used. 45 | * 46 | * Apply Model Forces should be called straight after the model computations are complete, 47 | * then each aerodynamic component applies its computed forces to the rigid body 48 | * 49 | * This method is implemented to avoid management of all the aerodynamic components attached to the 50 | * object - we can also handle the addition and subtraction of components in runtime by using events 51 | */ 52 | 53 | // The computation of aerodynamic forces and moments requires the information from the aero body 54 | public delegate void RunModelComputations(AeroBody aeroBody); 55 | public RunModelComputations runModelEvent; 56 | 57 | // After the forces and moments are computed, they need to be applied to the appropriate rigid body 58 | public delegate void ApplyModelForces(Rigidbody rb); 59 | public ApplyModelForces applyForcesEvent; 60 | 61 | #endregion Component Computation and Apply Force Events 62 | 63 | #region Reference Frames 64 | 65 | // ------------------------------------------------------------------------------------------- 66 | // Reference Frames 67 | // ------------------------------------------------------------------------------------------- 68 | /* Using a class for reference frames, this gives us a clear way to 69 | * differentiate between wind velocity in the earth or body frame etc. 70 | * The ReferenceFrame class holds the rotation between the previous frame 71 | * and the current frame. This is done because all frames are in a 72 | * hierarchy: 73 | * 1. Earth 74 | * 2. Object 75 | * 3. Body 76 | * 4. Equivalent Aerodynamic Body (EAB) 77 | * 78 | * There are helper functions in the Aerodynamics script which are used to 79 | * transform directions from Body and EAB frames to the Earth frame 80 | */ 81 | 82 | 83 | 84 | 85 | // Converts a vector in EAB frame to earth frame 86 | public Vector3 TransformDirectionEABToEarth(Vector3 vector) 87 | { 88 | return TransformDirectionBodyToEarth(TransformDirectionEABToBody(vector)); 89 | } 90 | 91 | // Converts a vector in EAB frame to body frame 92 | public Vector3 TransformDirectionEABToBody(Vector3 vector) 93 | { 94 | return equivAerobodyFrame.inverseObjectToFrameRotation * vector; 95 | } 96 | 97 | // Converts a vector in aeroBody frame to earth frame 98 | public Vector3 TransformDirectionBodyToEarth(Vector3 vector) 99 | { 100 | return unityObjectFrame.inverseObjectToFrameRotation * (aeroBodyFrame.inverseObjectToFrameRotation * vector); 101 | } 102 | 103 | // Update the objectFrame rotation based on the current rotation of the Transform component 104 | private void GetObjectAxisRotation() 105 | { 106 | unityObjectFrame.SetFrameRotation(transform.rotation); 107 | } 108 | 109 | // Earth is just Unity's global coordinates, the instance is only included here for clarity 110 | public ReferenceFrame earthFrame = new ReferenceFrame(); 111 | 112 | // Object is the frame of reference defined by the GameObject's Transform component in Unity 113 | // it has no notion of span, chord or thickness and the rotation of the frame is effectively arbitrary 114 | // We need to keep track of this however as the dynamic motion of the object will generally 115 | // rotate the frame 116 | public ReferenceFrame unityObjectFrame = new ReferenceFrame(); 117 | 118 | // AeroBodyFrame is the rotation of the object frame such that (x, y, z) align with (span, thickness, chord) 119 | public ReferenceFrame aeroBodyFrame = new ReferenceFrame(); 120 | 121 | // Equivalent Aerodynamic Body is the rotation and projection of the AeroBody frame and dimensions 122 | // into the wind direction so that no sideslip is present 123 | public ReferenceFrame equivAerobodyFrame = new ReferenceFrame(); 124 | 125 | #endregion Reference Frames 126 | 127 | #region Aerodynamic Bodies 128 | // ------------------------------------------------------------------------------------------- 129 | // Aerodynamic Bodies 130 | // ------------------------------------------------------------------------------------------- 131 | /* There are two ellipsoid bodies used for the aerodynamics simulation: 132 | * 1. The Aerodynamic Body (AeroBody) 133 | * 2. The Equivalent Aerodynamic Body (EAB) 134 | * The AeroBody holds the aerodynamic properties (e.g. aspect ratio, 135 | * chord, span etc) for the object being simulated. The EAB holds the 136 | * aerodynamic properties of the body when resolved into the wind such 137 | * that the body sees zero sideslip in the wind. 138 | */ 139 | public class AerodynamicBody 140 | { 141 | // Diameters of the ellipsoid body, using aerodynamic notation 142 | public float span_a, thickness_b, chord_c; // (m) 143 | 144 | // Radii of the ellipsoid body - for convenience so that multiples of two are neglected 145 | public float minorAxis, midAxis, majorAxis; // (m) 146 | 147 | // Ratios 148 | public float aspectRatio; // (dimensionless) 149 | public float thicknessToChordRatio_bOverc; // (dimensionless) 150 | public float camberRatio; // (dimensionless) 151 | 152 | 153 | public void SetDimensions(float span, float thickness, float chord) 154 | { 155 | span_a = span; 156 | majorAxis = span / 2f; 157 | 158 | chord_c = chord; 159 | midAxis = chord / 2f; 160 | 161 | thickness_b = thickness; 162 | minorAxis = thickness / 2f; 163 | } 164 | 165 | public void SetAerodynamicRatios(float _camber) 166 | { 167 | // The ellipse is awkward and may need to be scaled larger so that the area of the ellipse 168 | // and the usual cuboid is the same 169 | // We might not show the same ellipsoid body in gizmos as what the code uses 170 | aspectRatio = span_a / (Mathf.PI * chord_c); 171 | camberRatio = _camber / chord_c; 172 | thicknessToChordRatio_bOverc = thickness_b / chord_c; 173 | } 174 | } 175 | 176 | public AerodynamicBody aeroBody = new AerodynamicBody(); 177 | public AerodynamicBody EAB = new AerodynamicBody(); 178 | 179 | #endregion Aerodynamic Bodies 180 | 181 | #region General Properties 182 | // ------------------------------------------------------------------------------------------- 183 | // Unity Related Components 184 | // ------------------------------------------------------------------------------------------- 185 | 186 | // Unity's physics body, used to apply physics to the object - We're going to assume that all 187 | // objects connected to a rigidbody are fixed and cannot move independently from the rigid body 188 | // This means that WE CAN'T MODEL FLAPPING WINGS OR FLEXIBLE WINGS using this method 189 | public Rigidbody rb; 190 | 191 | // Flag to determine if the model needs to recalculate dimensions at runtime 192 | public bool dynamicallyVariableShape; 193 | 194 | // Check to show this component is up and running, not necessary here but leaving it in just in case 195 | public bool initialised = false; 196 | 197 | // ------------------------------------------------------------------------------------------- 198 | // General Aerodynamic Body Properties 199 | // ------------------------------------------------------------------------------------------- 200 | // These are properties which are not changed by the projection 201 | // from AeroBody to EAB and so are kept outside of the classes 202 | 203 | [Tooltip("Body camber is the camber due to the shape of the body. This does not include the camber induced by flap deflection.")] 204 | public float BodyCamber; // (m) 205 | [Tooltip("Effective camber is the combination of the body camber and the camber due to flap deflection")] 206 | public float EffectiveCamber; // (m) 207 | 208 | public void SetFlapCamber(float flapCamber) 209 | { 210 | EffectiveCamber = BodyCamber + flapCamber; 211 | aeroBody.SetAerodynamicRatios(EffectiveCamber); 212 | EAB.SetAerodynamicRatios(EffectiveCamber); 213 | } 214 | 215 | // The projection from body to eab assumes constant areas for the ellipsoid body 216 | // therefore planform and profile areas are the same in each frame 217 | public float bodyPlanformArea; 218 | public float planformArea // (m^2) 219 | { 220 | get 221 | { 222 | // This is used to scale up or down the planform area of bodies within a group so that 223 | // their total area equals the required planform area of the group 224 | if (myGroup == null) { return bodyPlanformArea; } else { return myGroup.areaScale * bodyPlanformArea; } 225 | } 226 | } 227 | public float profileArea; // (m^2) 228 | public Vector3 areaVector; // (m^2) 229 | 230 | // Not sure if the same applies to the volumes, I would assume so though 231 | public Vector3 volumeVector; // (m^3) 232 | public float ellipsoidSurfaceArea; // (m^2) 233 | public float lambda_x, lambda_y, lambda_z; // (dimensionless) 234 | 235 | // ------------------------------------------------------------------------------------------- 236 | // Aerodynamic Angles and Rotation Vectors 237 | // ------------------------------------------------------------------------------------------- 238 | 239 | // Sideslip (beta) and angle of attack (alpha) vectors 240 | public Vector3 angleOfAttackRotationVector; // (unit vector) 241 | public float alpha, alpha_0, beta; // (rad) 242 | public float alpha_deg, beta_deg; // (degrees) 243 | public float sinAlpha, cosAlpha, sinBeta, cosBeta; // (dimensionless) 244 | 245 | 246 | // ------------------------------------------------------------------------------------------- 247 | // Fluid Properties and Characteristics 248 | // ------------------------------------------------------------------------------------------- 249 | 250 | 251 | 252 | // Properties 253 | public float dynamicPressure; // (Pa) 254 | public float qS; // (N) 255 | public float rho = 1.29f; // (kg/m^3) 256 | public float mu = 1.8e-5f; // (Nm/s) 257 | 258 | // Resultant forces and moments to be applied to the rigid body component 259 | public Vector3 resultantAerodynamicForce_earthFrame, resultantAerodynamicForce_bodyFrame; // (N) 260 | public Vector3 resultantAerodynamicMoment_earthFrame, resultantAerodynamicMoment_bodyFrame; // (Nm) 261 | 262 | #endregion General Properties 263 | 264 | #region Aerodynamic Functions 265 | 266 | // ------------------------------------------------------------------------------------------- 267 | // Aerodynamic Functions 268 | // ------------------------------------------------------------------------------------------- 269 | // The numbers in the function names indicate the order in which they 270 | // should be called when computing the aerodynamics from start to finish 271 | 272 | public void GetReferenceFrames_1() 273 | { 274 | /* The frames of reference and their notations in this model are: 275 | * - Earth (earth) 276 | * The earth frame is equivalent to Unity's global coordinates 277 | * 278 | * - Object (unityObject) 279 | * The object frame is equivalent to Unity's local coordinates 280 | * i.e. the arbitrary local (x,y,z) for the aerodynamic object 281 | * This frame moves and rotates with the rigid body dynamics 282 | * 283 | * - Body (aeroBody) 284 | * The body frame is a rotation of the object frame 285 | * such that (x, y, z) are aligned with (span, thickness, chord) 286 | * Thickness chord and span are selected in order of ascending 287 | * dimensions of the ellipsoid, i.e. span >= chord >= thickness 288 | * 289 | * - Equivalent Aerodynamic Body (EAB) 290 | * The equivalent aerodynamic body is the resolved body axes 291 | * such that the equivalent body experiences no sideslip. This is 292 | * both a rotation of the frame and a projection of dimensions 293 | * of the body to form a new ellipsoid 294 | * 295 | * First we need to get the span, chord and thickness of the body 296 | * based on the scale of the object - this could change in the future to be 297 | * inputs by the user! Or it could default to using the bounding box of the mesh 298 | */ 299 | 300 | // For now, we're just looking at the scale of the object to obtain ellipsoid dimensions 301 | float x = transform.localScale.x; 302 | float y = transform.localScale.y; 303 | float z = transform.localScale.z; 304 | 305 | // This is redundant data really but it makes everything look nice 306 | earthFrame.SetDirectionVectors(Vector3.right, Vector3.up, Vector3.forward); 307 | unityObjectFrame.SetDirectionVectors(Vector3.right, Vector3.up, Vector3.forward); 308 | 309 | 310 | // The object rotation will need updating at every time step as the object moves according to 311 | // its rigid body dynamic motion, whereas the body frame is fixed relative to the object frame 312 | GetObjectAxisRotation(); 313 | 314 | // The normal to the lifting plane is aligned with the minor axis (thickness) of the ellipsoid, 315 | // The span is aligned to X and chord to Z 316 | // Coordinates are aligned so that [x, y, z] == [span, thickness, chord] 317 | 318 | // The order of these checks ensures that if x == y == z then they become (span, thickness, chord) 319 | // as defined in the theory 320 | if (x >= y) 321 | { 322 | if (y > z) 323 | { 324 | // Then x > y > z and we need to swap thickness and chord 325 | aeroBody.SetDimensions(x, z, y); 326 | aeroBodyFrame.SetDirectionVectors(Vector3.right, Vector3.forward, Vector3.up); 327 | } 328 | else 329 | { 330 | if (x >= z) 331 | { 332 | // Then x > z > y so we don't need to do anything 333 | aeroBody.SetDimensions(x, y, z); 334 | aeroBodyFrame.SetDirectionVectors(Vector3.right, Vector3.up, Vector3.forward); 335 | } 336 | else 337 | { 338 | // Then z > x > y so we need to swap chord and span 339 | aeroBody.SetDimensions(z, y, x); 340 | aeroBodyFrame.SetDirectionVectors(Vector3.forward, Vector3.up, Vector3.right); 341 | } 342 | } 343 | } 344 | else 345 | { 346 | // y > x 347 | if (y >= z) 348 | { 349 | if (x >= z) 350 | { 351 | // Then y > x > z 352 | aeroBody.SetDimensions(y, z, x); 353 | aeroBodyFrame.SetDirectionVectors(Vector3.up, Vector3.forward, Vector3.right); 354 | } 355 | else 356 | { 357 | // Then y > z > x 358 | aeroBody.SetDimensions(y, x, z); 359 | aeroBodyFrame.SetDirectionVectors(Vector3.up, Vector3.right, Vector3.forward); 360 | } 361 | } 362 | else 363 | { 364 | // Then z > y > x 365 | aeroBody.SetDimensions(z, x, y); 366 | aeroBodyFrame.SetDirectionVectors(Vector3.forward, Vector3.right, Vector3.up); 367 | } 368 | } 369 | // Rotate the reference axes to line up with (span, thickness, chord) == (a, b, c) 370 | aeroBodyFrame.SetFrameRotation(Quaternion.LookRotation(aeroBodyFrame.zDirection, aeroBodyFrame.yDirection)); 371 | 372 | // Set the aerodynamic related properties 373 | aeroBody.SetAerodynamicRatios(EffectiveCamber); 374 | } 375 | 376 | public void GetEllipsoidProperties_2() 377 | { 378 | // Area and volume vectors 379 | // Using the appropriate ellipsoid radii here so there is no potential for confusion by dividing by 2 380 | areaVector = Mathf.PI * new Vector3(aeroBody.minorAxis * aeroBody.midAxis, aeroBody.majorAxis * aeroBody.midAxis, aeroBody.majorAxis * aeroBody.minorAxis); 381 | 382 | float fourPiOver3 = 4f * Mathf.PI / 3f; 383 | volumeVector.x = fourPiOver3 * aeroBody.majorAxis * aeroBody.midAxis * aeroBody.midAxis; 384 | volumeVector.y = fourPiOver3 * aeroBody.minorAxis * aeroBody.majorAxis * aeroBody.majorAxis; 385 | volumeVector.z = fourPiOver3 * aeroBody.midAxis * aeroBody.majorAxis * aeroBody.majorAxis; 386 | 387 | // Planform area - area of the aerodynamic body in the lifting plane 388 | bodyPlanformArea = areaVector.y; 389 | 390 | // Approximate surface area of ellipsoid 391 | ellipsoidSurfaceArea = 4f * Mathf.PI * Mathf.Pow((1f / 3f) * (Mathf.Pow(aeroBody.majorAxis * aeroBody.minorAxis, 1.6f) 392 | + Mathf.Pow(aeroBody.majorAxis * aeroBody.midAxis, 1.6f) 393 | + Mathf.Pow(aeroBody.minorAxis * aeroBody.midAxis, 1.6f)), (1f / 1.6f)); 394 | 395 | // Axis Ratios 396 | lambda_x = aeroBody.thickness_b / aeroBody.chord_c; 397 | lambda_y = aeroBody.chord_c / aeroBody.span_a; 398 | lambda_z = aeroBody.thickness_b / aeroBody.span_a; 399 | } 400 | 401 | // Not interested in setting the inertia here, we'll just use Unity's model of the inertia based on the collider mesh 402 | //public void SetMassProperties_2() 403 | //{ 404 | // // Set the inertia of the body as 405 | // // Solid Ellipsoid 406 | // rb.inertiaTensor = 0.2f * rb.mass * new Vector3(aeroBody.thickness_b * aeroBody.thickness_b / 4f + aeroBody.chord_c * aeroBody.chord_c / 4f, aeroBody.span_a * aeroBody.span_a / 4f + aeroBody.chord_c * aeroBody.chord_c / 4f, aeroBody.span_a * aeroBody.span_a / 4f + aeroBody.thickness_b * aeroBody.thickness_b / 4f); 407 | //} 408 | 409 | public void ResolveWind_3() 410 | { 411 | // Putting this here because it's definitely going to be called and it needs doing 412 | // before we can resolve the wind into object, body and EAB axes 413 | GetObjectAxisRotation(); 414 | 415 | // In aerodynamics, wind velocity describes the velocity of the body relative to the wind 416 | 417 | // Using the transform position here so we can have aero body components separate from rigid bodies 418 | // and everything should still work 419 | earthVelocity = rb.GetPointVelocity(transform.position); 420 | 421 | // Start with earth frame 422 | earthFrame.SetResolvedWind(earthVelocity - externalFlowVelocity_inEarthFrame, rb.angularVelocity); 423 | // Rotate to object frame 424 | unityObjectFrame.SetResolvedWind(earthFrame.windVelocity, earthFrame.angularWindVelocity); 425 | // Rotate to body frame 426 | aeroBodyFrame.SetResolvedWind(unityObjectFrame.windVelocity, unityObjectFrame.angularWindVelocity); 427 | } 428 | 429 | public void SetWind_3(Vector3 wind, Vector3 angularWind) 430 | { 431 | // This is an alternative to getting the wind according to the rigid body velocity 432 | // here the external wind is simply passed in. This is used for experiments and tests 433 | 434 | // Putting this here because it's definitely going to be called and it needs doing 435 | // before we can resolve the wind into object, body and EAB axes 436 | GetObjectAxisRotation(); 437 | 438 | // In aerodynamics, wind velocity describes the velocity of the body relative to the wind 439 | 440 | // Start with earth frame 441 | earthFrame.SetResolvedWind(wind, angularWind); 442 | // Rotate to object frame 443 | unityObjectFrame.SetResolvedWind(earthFrame.windVelocity, earthFrame.angularWindVelocity); 444 | // Rotate to body frame 445 | aeroBodyFrame.SetResolvedWind(unityObjectFrame.windVelocity, unityObjectFrame.angularWindVelocity); 446 | } 447 | 448 | 449 | public void GetFlowCharacteristics_4() 450 | { 451 | // Wind square magnitude should be the same for all frames - I think! 452 | dynamicPressure = 0.5f * rho * aeroBodyFrame.windVelocity.sqrMagnitude; 453 | qS = dynamicPressure * planformArea; 454 | } 455 | 456 | public void GetAeroAngles_5() 457 | { 458 | // Sideslip 459 | beta = Mathf.Atan2(aeroBodyFrame.windVelocity.x, aeroBodyFrame.windVelocity.z); 460 | beta_deg = Mathf.Rad2Deg * beta; 461 | sinBeta = Mathf.Sin(beta); 462 | cosBeta = Mathf.Cos(beta); 463 | 464 | // Equivalent Aerodynamic Body is the Body frame rotated by the sideslip angle 465 | // I'll bet there's a cheaper way to do this but alas... 466 | equivAerobodyFrame.SetFrameRotation(Quaternion.Euler(0, beta_deg, 0)); 467 | 468 | // Resolve the body wind so we have zero sideslip 469 | equivAerobodyFrame.SetResolvedWind(aeroBodyFrame.windVelocity, aeroBodyFrame.angularWindVelocity); 470 | 471 | EAB_windVel = equivAerobodyFrame.windVelocity; 472 | 473 | // Angle of attack 474 | angleOfAttackRotationVector = Vector3.Cross(aeroBodyFrame.windVelocity_normalised, Vector3.down); 475 | 476 | // Include the minus sign here because alpha goes from the wind vector to the lifting plane 477 | alpha = -Mathf.Atan2(equivAerobodyFrame.windVelocity.y, equivAerobodyFrame.windVelocity.z); 478 | alpha_deg = Mathf.Rad2Deg * alpha; 479 | sinAlpha = Mathf.Sin(alpha); 480 | cosAlpha = Mathf.Cos(alpha); 481 | 482 | // I noticed this happen once but haven't seen it again since, so I'm leaving this just as a precaution 483 | if (Mathf.Abs(alpha_deg) > 90f) 484 | { 485 | Debug.LogError("Angle of attack exceeded +- 90 degrees for " + gameObject.name + ". Please send the object's position, rotation, velocity and angular velocity to Conor."); 486 | Debug.Break(); 487 | } 488 | } 489 | 490 | public void GetEquivalentAerodynamicBody_6() 491 | { 492 | // The equivalent aerodynamic body has the same aspect ratio and and span 493 | // as the actual body's shape, but with zero sideslip 494 | 495 | // Resolving in sideslip direction by rotating about the normal to the lift plane 496 | EAB.midAxis = aeroBody.majorAxis * aeroBody.midAxis / Mathf.Sqrt((aeroBody.midAxis * aeroBody.midAxis * sinBeta * sinBeta) + (aeroBody.majorAxis * aeroBody.majorAxis * cosBeta * cosBeta)); 497 | // Find major axis based on constant area of ellipse 498 | EAB.majorAxis = aeroBody.majorAxis * aeroBody.midAxis / EAB.midAxis; 499 | // No change to thickness 500 | EAB.minorAxis = aeroBody.minorAxis; 501 | 502 | // Store the diameters as well to save on factor of 2 in equations 503 | EAB.span_a = 2f * EAB.majorAxis; 504 | EAB.thickness_b = 2f * EAB.minorAxis; 505 | EAB.chord_c = 2f * EAB.midAxis; 506 | 507 | // Work out aero parameters of equivalent body 508 | EAB.SetAerodynamicRatios(EffectiveCamber); 509 | 510 | // Profile area is the projection in the wind direction 511 | profileArea = Vector3.Scale(areaVector, aeroBodyFrame.windVelocity_normalised).magnitude; 512 | } 513 | 514 | public void ResolveWindAndDimensions_1_to_6() 515 | { 516 | GetReferenceFrames_1(); 517 | GetEllipsoidProperties_2(); 518 | ResolveWind_3(); 519 | GetFlowCharacteristics_4(); 520 | GetAeroAngles_5(); 521 | GetEquivalentAerodynamicBody_6(); 522 | } 523 | 524 | public void GetComponentForces_7() 525 | { 526 | // Call the event, any components attached to this game object will be subscribed 527 | runModelEvent?.Invoke(this); 528 | } 529 | 530 | 531 | public void ApplyAerodynamicForces_8() 532 | { 533 | // Call the event, any components attached to this game object will be subscribed 534 | applyForcesEvent?.Invoke(rb); 535 | } 536 | 537 | // ============================== Experimental ========================================== 538 | 539 | //public float targetDt = 0.02f; 540 | ////public float minDt = 0.002f; 541 | //public float delta = 0.99f; 542 | //public float minStep; 543 | //public bool useAdaptiveTimeStep = false; 544 | //public bool accountForExternalForces = false; 545 | //public Vector3 netAcceleration; 546 | 547 | //public void ApplyAdaptiveTimeStep() 548 | //{ 549 | // // Start with our target time step, if nothing is smaller than this 550 | // // then we will default to the targetDt 551 | // minStep = targetDt; 552 | 553 | // // Multiple references to speed are made so store it 554 | // float speed = earthFrame.windVelocity.magnitude; 555 | 556 | // // Make sure we have some speed 557 | // if(speed == 0f) 558 | // { 559 | // // If not, then the acceleration can't change the velocity direction 560 | // // so there's no point in simulating at a small time step anyway 561 | // Time.fixedDeltaTime = minStep; 562 | // return; 563 | // } 564 | 565 | // // Check if the acceleration will invert the velocity by more than 2*v 566 | // netAcceleration = rb.GetAccumulatedForce() / rb.mass; 567 | 568 | // if (accountForExternalForces && rb.useGravity) 569 | // { 570 | // netAcceleration += Physics.gravity; 571 | // } 572 | 573 | // // Vector of k for each component separately? Or do we need to do some pythagoras here... 574 | 575 | // if(netAcceleration.x != 0f) 576 | // { 577 | // minStep = Mathf.Min(GetMinStep(speed, netAcceleration.x), minStep); 578 | // } 579 | 580 | // if(netAcceleration.y != 0f) 581 | // { 582 | // minStep = Mathf.Min(GetMinStep(speed, netAcceleration.y), minStep); 583 | // } 584 | 585 | // if (netAcceleration.z != 0f) 586 | // { 587 | // minStep = Mathf.Min(GetMinStep(speed, netAcceleration.z), minStep); 588 | // } 589 | 590 | // // Could use min function here but the if statement lets us put out 591 | // // a warning as well! 592 | // if (minStep < targetDt) 593 | // { 594 | // Time.fixedDeltaTime = minStep; 595 | // //Debug.LogWarning(gameObject.name + " encountered an acceleration which was too large for the target time step. " + 596 | // // "A smaller time step of " + minStep.ToString() + " s was used to ensure numerical stability of the simulation. " + 597 | // // "Consider increasing the mass of your object, running at a smaller time step, reducing wind speed, reducing the size of your object etc."); 598 | // } 599 | 600 | //} 601 | 602 | //private float GetMinStep(float speed, float acceleration) 603 | //{ 604 | // return delta * Mathf.Abs(speed / acceleration); 605 | //} 606 | 607 | // ====================================================================================== 608 | 609 | public void GetEllipsoid_1_to_2() 610 | { 611 | GetReferenceFrames_1(); 612 | GetEllipsoidProperties_2(); 613 | } 614 | 615 | public void AerodynamicBody_3_to_6() 616 | { 617 | ResolveWind_3(); 618 | GetFlowCharacteristics_4(); 619 | GetAeroAngles_5(); 620 | GetEquivalentAerodynamicBody_6(); 621 | } 622 | 623 | public void SetAlpha_rad_3_to_6(float alpha_rad) 624 | { 625 | // Assumes the body has zero velocity 626 | externalFlowVelocity_inEarthFrame = new Vector3(0, -Mathf.Sin(alpha_rad), Mathf.Cos(alpha_rad)); 627 | 628 | ResolveWind_3(); 629 | GetFlowCharacteristics_4(); 630 | GetAeroAngles_5(); 631 | GetEquivalentAerodynamicBody_6(); 632 | } 633 | 634 | public void SetAlpha_deg_3_to_6(float alpha_deg) 635 | { 636 | // Assumes the body has zero velocity 637 | float alpha_rad = Mathf.Deg2Rad * alpha_deg; 638 | externalFlowVelocity_inEarthFrame = new Vector3(0, -Mathf.Sin(alpha_rad), Mathf.Cos(alpha_rad)); 639 | 640 | ResolveWind_3(); 641 | GetFlowCharacteristics_4(); 642 | GetAeroAngles_5(); 643 | GetEquivalentAerodynamicBody_6(); 644 | } 645 | 646 | #endregion Aerodynamic Functions 647 | 648 | public void Initialise() 649 | { 650 | // At the moment, we need a rigid body for our velocity etc. Eventually I might be persuaded 651 | // into adding a kinematic component which keeps track of velocity for us. 652 | if(!rb) 653 | { 654 | // Check if there's a rigid body attached to the same game object 655 | rb = GetComponent(); 656 | 657 | if (!rb) 658 | { 659 | // Check all the parents of this object for a rigid body component 660 | rb = GetComponentInParent(); 661 | 662 | if (!rb) 663 | { 664 | // Add a rigid body if we can't find one anywhere 665 | Debug.LogWarning("No RigidBody Component found for " + gameObject.name + ", adding one."); 666 | rb = gameObject.AddComponent(); 667 | 668 | // Don't use Unity's filthy drag model - bah! 669 | rb.angularDrag = 0; 670 | rb.drag = 0; 671 | } 672 | } 673 | } 674 | 675 | GetEllipsoid_1_to_2(); 676 | initialised = true; 677 | } 678 | 679 | void Awake() 680 | { 681 | Initialise(); 682 | } 683 | 684 | void FixedUpdate() 685 | { 686 | if (dynamicallyVariableShape) 687 | { 688 | GetEllipsoid_1_to_2(); 689 | } 690 | AerodynamicBody_3_to_6(); 691 | GetComponentForces_7(); 692 | ApplyAerodynamicForces_8(); 693 | 694 | //if (useAdaptiveTimeStep) 695 | //{ 696 | // ApplyAdaptiveTimeStep(); 697 | //} 698 | } 699 | 700 | // This could be offloaded to a separate class with a singleton so we don't have to store it in 701 | // memory for every aero body component 702 | public static Mesh gizmoMesh; 703 | 704 | private void OnDrawGizmos() 705 | { 706 | // This is a very hacky way to draw a squashed sphere mesh as an ellipsoid 707 | // I keep getting errors when the project opens because it tries to destroy things and yeah... 708 | // Would be nice to be able to reference just the mesh as a resource instead of the game object 709 | if (!gizmoMesh) 710 | { 711 | gizmoMesh = Resources.Load("Sphere"); 712 | GetReferenceFrames_1(); 713 | //GameObject go = Resources.Load("Sphere") as GameObject; 714 | //gizmoMesh = go.GetComponent().sharedMesh; 715 | //DestroyImmediate(go); 716 | } 717 | 718 | // This is a lazy way to update the aerobody in the editor when the transform component is changed 719 | // We need to do this because the scale of the transform is used to determine the ellipsoid properties 720 | // I think we should allow people to adjust this but there are so many ways to derive the body that it 721 | // will take a while to enable them all or decide which ones are best 722 | if (!Application.isPlaying && transform.hasChanged) 723 | { 724 | GetReferenceFrames_1(); 725 | transform.hasChanged = false; 726 | } 727 | 728 | // Draw the ellipsoid with the same rotation as the aero body, need to do this because 729 | // the transform won't necessarily be the same rotation as the span, thickness, chord axes 730 | Gizmos.color = Color.blue; 731 | 732 | if (myGroup == null) 733 | { 734 | // Draw the usual ellipsoid wire mesh if the aerodynamic object is not part of a group 735 | Gizmos.DrawWireMesh(gizmoMesh, transform.position, transform.rotation * aeroBodyFrame.inverseObjectToFrameRotation, new Vector3(aeroBody.span_a, aeroBody.thickness_b, aeroBody.chord_c)); 736 | } 737 | else 738 | { 739 | // If the object is part of a group then it is scaled to be a cuboid shape and so we draw that instead 740 | 741 | // This is actually drawing the wrong thing. We only know the object's new planform area! 742 | Gizmos.matrix = Matrix4x4.TRS(transform.position, transform.rotation * aeroBodyFrame.inverseObjectToFrameRotation, new Vector3(aeroBody.span_a, aeroBody.thickness_b, aeroBody.chord_c)); 743 | Gizmos.DrawWireCube(Vector3.zero, Vector3.one); 744 | } 745 | 746 | } 747 | 748 | private void Reset() 749 | { 750 | GetReferenceFrames_1(); 751 | } 752 | } 753 | --------------------------------------------------------------------------------