From 04b7880abf3ae91c95a41a16ae9a8bc4cebcbaae Mon Sep 17 00:00:00 2001 From: PhineasFreak Date: Sun, 18 Feb 2018 01:20:01 +0200 Subject: [PATCH 01/34] Initial KSP 1.3.1 update * Recompiled for KSP 1.3.1. * Added compatibility for FilterExtensions to all parts. * Added the ability to toggle the fairing side decoupler (no more separate "Fairing" and "Fuselage" sides). * The base and nose shapes of fairing sides can now be changed in the editor (no more separate part configs for different fairing shapes). * Repackaged the distribution for the components to be on their own directories. * Changed the versioning system to use a - format. * Updated the part configs for compatibility with the latest KSP versions. * Reorganized the repository. * Added a .gitattributes file. * Added a missing Github license file. * Updated the Github readme file. --- .gitattributes | 53 + .gitignore | 24 +- .../ProceduralFairings_FilterExtensions.cfg | 14 + .../Config/ProceduralFairings_Settings.cfg | 162 +++ .../{ => Parts}/baseModel.mu | Bin .../{ => Parts}/baseRingModel.mu | Bin .../{ => Parts}/baseRingTex.dds | Bin .../{ => Parts}/baseTex.dds | Bin .../ProceduralFairings/Parts/base_ring.cfg | 103 ++ .../Parts/base_standard.cfg | 123 ++ .../{ => Parts}/blackRingTex.dds | Bin .../{ => Parts}/fairing1.dds | Bin .../ProceduralFairings/Parts/fairing_side.cfg | 69 ++ .../{ => Parts}/fuselage1.dds | Bin .../Parts/interstage_adapter.cfg | 136 ++ .../{ => Parts}/sideModel.mu | Bin .../{ => Parts}/thrustPlate.mu | Bin .../{ => Parts}/thrustPlate1.dds | Bin .../thrustPlate1bump_Normal_NRM.dds | Bin .../ProceduralFairings/Parts/thrust_plate.cfg | 90 ++ .../Plugins/ProceduralFairings.dll | Bin 0 -> 86016 bytes .../ProceduralFairings/ProceduralFairings.dll | Bin 65024 -> 0 bytes GameData/ProceduralFairings/adapter2.cfg | 143 --- GameData/ProceduralFairings/base.cfg | 127 -- GameData/ProceduralFairings/baseRing.cfg | 121 -- GameData/ProceduralFairings/changelog.txt | 301 +++++ GameData/ProceduralFairings/common.cfg | 158 --- GameData/ProceduralFairings/fuselage1.cfg | 71 -- GameData/ProceduralFairings/fuselage2.cfg | 69 -- GameData/ProceduralFairings/license.txt | 395 ++++++ GameData/ProceduralFairings/plate.cfg | 97 -- GameData/ProceduralFairings/side1.cfg | 79 -- GameData/ProceduralFairings/side2.cfg | 77 -- LICENSE.md | 395 ++++++ Misc/addAlpha.py | 16 - Misc/exportTex.py | 23 - Misc/history.txt | 101 -- Misc/makezip.bat | 7 - Misc/readme.txt | 239 ---- Misc/texTemplate.py | 25 - Misc/toDDS.py | 15 - README.md | 67 +- Source/FairingBase.cs | 868 ------------- Source/FairingDecoupler.cs | 197 --- Source/FairingShielding.cs | 341 ------ Source/FairingSide.cs | 512 -------- Source/NodeNumberTweaker.cs | 301 ----- Source/PFKMJoint.cs | 433 ------- Source/PayloadScan.cs | 179 --- Source/ProcAdapter.cs | 569 --------- Source/ProceduralFairings.csproj | 62 - Source/ProceduralFairings.sln | 47 +- Source/ProceduralFairings/FairingBase.cs | 1091 +++++++++++++++++ Source/ProceduralFairings/FairingDecoupler.cs | 229 ++++ Source/ProceduralFairings/FairingShielding.cs | 482 ++++++++ Source/ProceduralFairings/FairingSide.cs | 730 +++++++++++ .../ProceduralFairings/NodeNumberTweaker.cs | 351 ++++++ Source/ProceduralFairings/PFKMJoint.cs | 605 +++++++++ Source/ProceduralFairings/PayloadScan.cs | 211 ++++ Source/ProceduralFairings/ProcAdapter.cs | 728 +++++++++++ .../ProceduralFairings.csproj | 82 ++ .../Properties/AssemblyInfo.cs | 37 + Source/ProceduralFairings/Resizers.cs | 332 +++++ Source/ProceduralFairings/Utilities.cs | 437 +++++++ Source/Properties/AssemblyInfo.cs | 33 - Source/Resizers.cs | 281 ----- Source/build.bat | 13 - Source/s.bat | 4 - Source/utils.cs | 340 ----- 69 files changed, 7223 insertions(+), 5572 deletions(-) create mode 100644 .gitattributes create mode 100644 GameData/ProceduralFairings/Config/ProceduralFairings_FilterExtensions.cfg create mode 100644 GameData/ProceduralFairings/Config/ProceduralFairings_Settings.cfg rename GameData/ProceduralFairings/{ => Parts}/baseModel.mu (100%) rename GameData/ProceduralFairings/{ => Parts}/baseRingModel.mu (100%) rename GameData/ProceduralFairings/{ => Parts}/baseRingTex.dds (100%) rename GameData/ProceduralFairings/{ => Parts}/baseTex.dds (100%) create mode 100644 GameData/ProceduralFairings/Parts/base_ring.cfg create mode 100644 GameData/ProceduralFairings/Parts/base_standard.cfg rename GameData/ProceduralFairings/{ => Parts}/blackRingTex.dds (100%) rename GameData/ProceduralFairings/{ => Parts}/fairing1.dds (100%) create mode 100644 GameData/ProceduralFairings/Parts/fairing_side.cfg rename GameData/ProceduralFairings/{ => Parts}/fuselage1.dds (100%) create mode 100644 GameData/ProceduralFairings/Parts/interstage_adapter.cfg rename GameData/ProceduralFairings/{ => Parts}/sideModel.mu (100%) rename GameData/ProceduralFairings/{ => Parts}/thrustPlate.mu (100%) rename GameData/ProceduralFairings/{ => Parts}/thrustPlate1.dds (100%) rename GameData/ProceduralFairings/{ => Parts}/thrustPlate1bump_Normal_NRM.dds (100%) create mode 100644 GameData/ProceduralFairings/Parts/thrust_plate.cfg create mode 100644 GameData/ProceduralFairings/Plugins/ProceduralFairings.dll delete mode 100644 GameData/ProceduralFairings/ProceduralFairings.dll delete mode 100644 GameData/ProceduralFairings/adapter2.cfg delete mode 100644 GameData/ProceduralFairings/base.cfg delete mode 100644 GameData/ProceduralFairings/baseRing.cfg create mode 100644 GameData/ProceduralFairings/changelog.txt delete mode 100644 GameData/ProceduralFairings/common.cfg delete mode 100644 GameData/ProceduralFairings/fuselage1.cfg delete mode 100644 GameData/ProceduralFairings/fuselage2.cfg create mode 100644 GameData/ProceduralFairings/license.txt delete mode 100644 GameData/ProceduralFairings/plate.cfg delete mode 100644 GameData/ProceduralFairings/side1.cfg delete mode 100644 GameData/ProceduralFairings/side2.cfg create mode 100644 LICENSE.md delete mode 100644 Misc/addAlpha.py delete mode 100644 Misc/exportTex.py delete mode 100644 Misc/history.txt delete mode 100644 Misc/makezip.bat delete mode 100644 Misc/readme.txt delete mode 100644 Misc/texTemplate.py delete mode 100644 Misc/toDDS.py delete mode 100644 Source/FairingBase.cs delete mode 100644 Source/FairingDecoupler.cs delete mode 100644 Source/FairingShielding.cs delete mode 100644 Source/FairingSide.cs delete mode 100644 Source/NodeNumberTweaker.cs delete mode 100644 Source/PFKMJoint.cs delete mode 100644 Source/PayloadScan.cs delete mode 100644 Source/ProcAdapter.cs delete mode 100644 Source/ProceduralFairings.csproj create mode 100644 Source/ProceduralFairings/FairingBase.cs create mode 100644 Source/ProceduralFairings/FairingDecoupler.cs create mode 100644 Source/ProceduralFairings/FairingShielding.cs create mode 100644 Source/ProceduralFairings/FairingSide.cs create mode 100644 Source/ProceduralFairings/NodeNumberTweaker.cs create mode 100644 Source/ProceduralFairings/PFKMJoint.cs create mode 100644 Source/ProceduralFairings/PayloadScan.cs create mode 100644 Source/ProceduralFairings/ProcAdapter.cs create mode 100644 Source/ProceduralFairings/ProceduralFairings.csproj create mode 100644 Source/ProceduralFairings/Properties/AssemblyInfo.cs create mode 100644 Source/ProceduralFairings/Resizers.cs create mode 100644 Source/ProceduralFairings/Utilities.cs delete mode 100644 Source/Properties/AssemblyInfo.cs delete mode 100644 Source/Resizers.cs delete mode 100644 Source/build.bat delete mode 100644 Source/s.bat delete mode 100644 Source/utils.cs diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..793070f --- /dev/null +++ b/.gitattributes @@ -0,0 +1,53 @@ +# ================================================== +# Auto detect text files and perform LF +# normalization. +# ================================================== + +* text=auto + +# ================================================== +# Generic files. +# ================================================== + +*.cfg text eol=lf +*.CFG text eol=lf +*.csproj text eol=lf +*.CSPROJ text eol=lf +*.sln text eol=lf +*.SLN text eol=lf +*.txt text eol=lf +*.TXT text eol=lf +*.xml text eol=lf +*.XML text eol=lf + +# ================================================== +# Texture files. +# ================================================== + +*.dds binary +*.DDS binary +*.png binary +*.PNG binary + +# ================================================== +# Assemblies and database files. +# ================================================== + +*.dll binary +*.DLL binary + +# ================================================== +# Compilable source code files. +# ================================================== + +*.cs diff=csharp +*.CS diff=csharp + +# ================================================== +# Ignore list (folder and file names). +# ================================================== + +.gitattributes export-ignore +.gitignore export-ignore +*.md export-ignore +*.MD export-ignore diff --git a/.gitignore b/.gitignore index 4290de7..b5f493d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,18 @@ -*.sublime-workspace -*.sublime-project -*.user -Source/.vs -Source/bin -Source/obj +# ================================================== +# Ignore selected folders and file types from the git +# repository. +# ================================================== + +Source/Builds/* + +*.db +*.DB +*.DS_Store +*.ini +*.INI +*.lnk +*.LNK +*.pdb +*.PDB +*.png +*.PNG diff --git a/GameData/ProceduralFairings/Config/ProceduralFairings_FilterExtensions.cfg b/GameData/ProceduralFairings/Config/ProceduralFairings_FilterExtensions.cfg new file mode 100644 index 0000000..39e42cb --- /dev/null +++ b/GameData/ProceduralFairings/Config/ProceduralFairings_FilterExtensions.cfg @@ -0,0 +1,14 @@ +// ================================================== +// Add bulkhead profiles to all parts for FilterExtensions +// compatibility. +// ================================================== + +@PART[*]:HAS[@MODULE[ProceduralFairing*]]:NEEDS[FilterExtension] +{ + @bulkheadProfiles ^= :$:,proc: +} + +@PART[KzThrustPlate]:NEEDS[FilterExtension] +{ + @bulkheadProfiles ^= :$:,proc: +} diff --git a/GameData/ProceduralFairings/Config/ProceduralFairings_Settings.cfg b/GameData/ProceduralFairings/Config/ProceduralFairings_Settings.cfg new file mode 100644 index 0000000..83473c6 --- /dev/null +++ b/GameData/ProceduralFairings/Config/ProceduralFairings_Settings.cfg @@ -0,0 +1,162 @@ +// ================================================== +// Global procedural fairings settings. +// ================================================== + +PROCFAIRINGS_MINDIAMETER +{ + start = 1.00 + miniaturization = 0.400 + sandbox = 0.1 +} + +PROCFAIRINGS_MAXDIAMETER +{ + start = 1.50 + advAerodynamics = 4 + heavyAerodynamics = 12 + experimentalAerodynamics = 30 + sandbox = 50 +} + +PROCROCKET_MINDIAMETER +{ + start = 1.00 + miniaturization = 0.400 + sandbox = 0.1 +} + +PROCROCKET_MAXDIAMETER +{ + start = 1.50 + advConstruction = 4 + metaMaterials = 12 + aerospaceTech = 30 + sandbox = 50 +} + +// ================================================== +// Dummy parts to represent Procedural Fairings +// upgrades in the tech tree. +// ================================================== + +PART +{ + name = pf_tech_fairing04m + module = Part + author = Starstrider42 (config), e-dog (model) + + MODEL + { + model = ProceduralFairings/Parts/baseModel + } + + TechRequired = miniaturization + entryCost = 0 + cost = 0 + category = none + title = Procedural Fairings Upgrade + manufacturer = Keramzit Engineering + description = Allows fairings and plates to be made as small as 0.4m. +} + +PART +{ + name = pf_tech_fairing4m + module = Part + author = Starstrider42 (config), e-dog (model) + + MODEL + { + model = ProceduralFairings/Parts/baseModel + } + + TechRequired = advAerodynamics + entryCost = 0 + cost = 0 + category = none + title = Procedural Fairings Upgrade + manufacturer = Keramzit Engineering + description = Allows fairing bases up to 4m size. +} + +PART +{ + name = pf_tech_fairing12m + module = Part + author = Starstrider42 (config), e-dog (model) + + description = + + MODEL + { + model = ProceduralFairings/Parts/baseModel + } + + TechRequired = heavyAerodynamics + entryCost = 0 + cost = 0 + category = none + title = Procedural Fairings Upgrade + manufacturer = Keramzit Engineering + description = Allows fairing bases up to 12m size. +} + +PART +{ + name = pf_tech_fairing30m + module = Part + author = Starstrider42 (config), e-dog (model) + + MODEL + { + model = ProceduralFairings/Parts/baseModel + } + + TechRequired = experimentalAerodynamics + entryCost = 0 + cost = 0 + category = none + title = Procedural Fairings Upgrade + manufacturer = Keramzit Engineering + description = Allows fairing bases up to 30m size. +} + +PART +{ + name = pf_tech_rocket12m + module = Part + author = Starstrider42 (config), e-dog (model) + + MODEL + { + model = ProceduralFairings/Parts/thrustPlate + } + + TechRequired = metaMaterials + entryCost = 0 + cost = 0 + category = none + title = Procedural Fairings Upgrade + manufacturer = Keramzit Engineering + description = Allows thrust plates up to 12m size. +} + +PART +{ + name = pf_tech_rocket30m + module = Part + author = Starstrider42 (config), e-dog (model) + + MODEL + { + model = ProceduralFairings/Parts/thrustPlate + } + + TechRequired = aerospaceTech + entryCost = 0 + cost = 0 + category = none + title = Procedural Fairings Upgrade + manufacturer = Keramzit Engineering + description = Allows thrust plates up to 30m size. +} diff --git a/GameData/ProceduralFairings/baseModel.mu b/GameData/ProceduralFairings/Parts/baseModel.mu similarity index 100% rename from GameData/ProceduralFairings/baseModel.mu rename to GameData/ProceduralFairings/Parts/baseModel.mu diff --git a/GameData/ProceduralFairings/baseRingModel.mu b/GameData/ProceduralFairings/Parts/baseRingModel.mu similarity index 100% rename from GameData/ProceduralFairings/baseRingModel.mu rename to GameData/ProceduralFairings/Parts/baseRingModel.mu diff --git a/GameData/ProceduralFairings/baseRingTex.dds b/GameData/ProceduralFairings/Parts/baseRingTex.dds similarity index 100% rename from GameData/ProceduralFairings/baseRingTex.dds rename to GameData/ProceduralFairings/Parts/baseRingTex.dds diff --git a/GameData/ProceduralFairings/baseTex.dds b/GameData/ProceduralFairings/Parts/baseTex.dds similarity index 100% rename from GameData/ProceduralFairings/baseTex.dds rename to GameData/ProceduralFairings/Parts/baseTex.dds diff --git a/GameData/ProceduralFairings/Parts/base_ring.cfg b/GameData/ProceduralFairings/Parts/base_ring.cfg new file mode 100644 index 0000000..bbc3b87 --- /dev/null +++ b/GameData/ProceduralFairings/Parts/base_ring.cfg @@ -0,0 +1,103 @@ +PART +{ + name = KzResizableFairingBaseRing + module = Part + author = e-dog + + MODEL + { + model = ProceduralFairings/Parts/baseRingModel + scale = 1.0, 1.0, 1.0 + } + + scale = 1.0 + rescaleFactor = 1.0 + + node_stack_top = 0.0, 0.2, 0.0, 0.0, 1.0, 0.0, 1 + node_stack_bottom = 0.0, 0.0, 0.0, 0.0, -1.0, 0.0, 1 + + node_stack_connect01 = 0.5, 0.1, 0.0, 0.0, 1.0, 0.0, 0 + node_stack_connect02 = 0.5, 0.1, 0.0, 0.0, 1.0, 0.0, 0 + node_stack_connect03 = 0.5, 0.1, 0.0, 0.0, 1.0, 0.0, 0 + node_stack_connect04 = 0.5, 0.1, 0.0, 0.0, 1.0, 0.0, 0 + node_stack_connect05 = 0.5, 0.1, 0.0, 0.0, 1.0, 0.0, 0 + node_stack_connect06 = 0.5, 0.1, 0.0, 0.0, 1.0, 0.0, 0 + node_stack_connect07 = 0.5, 0.1, 0.0, 0.0, 1.0, 0.0, 0 + node_stack_connect08 = 0.5, 0.1, 0.0, 0.0, 1.0, 0.0, 0 + + attachRules = 1,0,1,1,0 + + TechRequired = aerodynamicSystems + cost = 100 + entryCost = 4600 + category = Payload + subcategory = 0 + title = Fairing Base Ring + manufacturer = Keramzit Engineering + description = Structural base for mounting side fairings and your payload. Decoupler sold separately. + + mass = 0 + dragModelType = default + maximum_drag = 0.2 + minimum_drag = 0.2 + angularDrag = 2 + crashTolerance = 12 + breakingForce = 2000 + breakingTorque = 2000 + maxTemp = 2600 + explosionPotential = 0 + fuelCrossFeed = True + thermalMassModifier = 2.0 + skinMassPerArea = 4.0 + skinInternalConductionMult = 0.25 + emissiveConstant = 0.8 + stackSymmetry = 7 + stageOffset = 1 + childStageOffset = 1 + bulkheadProfiles = size1 + tags = aero )cap cargo cone contain drag fairing hollow inter nose payload procedural protect rocket shroud stage (stor transport + + MODULE + { + name = ProceduralFairingBase + baseSize = 1.15 + sideThickness = 0.05 + verticalStep = 0.1 + } + + MODULE + { + name = KzNodeNumberTweaker + nodePrefix = connect + maxNumber = 8 + numNodes = 2 + radius = 0.625 + shouldResizeNodes = False + } + + MODULE + { + name = KzFairingBaseResizer + size = 1.25 + costPerTonne = 1000 + specificMass = 0.0064, 0.0130, 0.0098, 0 + specificBreakingForce = 1280 + specificBreakingTorque = 1280 + dragAreaScale = 1.5 + } + + MODULE + { + name = KzFairingBaseShielding + } + + MODULE + { + name = ModuleToggleCrossfeed + crossfeedStatus = False + toggleEditor = True + toggleFlight = True + enableText = Enable Crossfeed + disableText = Disable Crossfeed + } +} diff --git a/GameData/ProceduralFairings/Parts/base_standard.cfg b/GameData/ProceduralFairings/Parts/base_standard.cfg new file mode 100644 index 0000000..8035e50 --- /dev/null +++ b/GameData/ProceduralFairings/Parts/base_standard.cfg @@ -0,0 +1,123 @@ +PART +{ + name = KzResizableFairingBase + module = Part + author = e-dog + + MODEL + { + model = ProceduralFairings/Parts/baseModel + scale = 1.0, 1.0, 1.0 + } + + scale = 1.0 + rescaleFactor = 1.0 + + node_stack_top = 0.0, 0.5, 0.0, 0.0, 1.0, 0.0, 1 + node_stack_bottom = 0.0, 0.0, 0.0, 0.0, -1.0, 0.0, 1 + + node_stack_connect01 = 0.5, 0.1, 0.0, 0.0, 1.0, 0.0, 0 + node_stack_connect02 = 0.5, 0.1, 0.0, 0.0, 1.0, 0.0, 0 + node_stack_connect03 = 0.5, 0.1, 0.0, 0.0, 1.0, 0.0, 0 + node_stack_connect04 = 0.5, 0.1, 0.0, 0.0, 1.0, 0.0, 0 + node_stack_connect05 = 0.5, 0.1, 0.0, 0.0, 1.0, 0.0, 0 + node_stack_connect06 = 0.5, 0.1, 0.0, 0.0, 1.0, 0.0, 0 + node_stack_connect07 = 0.5, 0.1, 0.0, 0.0, 1.0, 0.0, 0 + node_stack_connect08 = 0.5, 0.1, 0.0, 0.0, 1.0, 0.0, 0 + + node_stack_interstage01 = 0.0, 1.20, 0.0, 0.0, -1.0, 0.0, 1 + node_stack_interstage01u = 0.0, 1.20, 0.0, 0.0, 1.0, 0.0, 1 + node_stack_interstage02 = 0.0, 1.90, 0.0, 0.0, -1.0, 0.0, 1 + node_stack_interstage02u = 0.0, 1.90, 0.0, 0.0, 1.0, 0.0, 1 + node_stack_interstage03 = 0.0, 2.60, 0.0, 0.0, -1.0, 0.0, 1 + node_stack_interstage03u = 0.0, 2.60, 0.0, 0.0, 1.0, 0.0, 1 + node_stack_interstage04 = 0.0, 3.30, 0.0, 0.0, -1.0, 0.0, 1 + node_stack_interstage04u = 0.0, 3.30, 0.0, 0.0, 1.0, 0.0, 1 + node_stack_interstage05 = 0.0, 4.00, 0.0, 0.0, -1.0, 0.0, 1 + node_stack_interstage05u = 0.0, 4.00, 0.0, 0.0, 1.0, 0.0, 1 + node_stack_interstage06 = 0.0, 4.70, 0.0, 0.0, -1.0, 0.0, 1 + node_stack_interstage06u = 0.0, 4.70, 0.0, 0.0, 1.0, 0.0, 1 + node_stack_interstage07 = 0.0, 5.40, 0.0, 0.0, -1.0, 0.0, 1 + node_stack_interstage07u = 0.0, 5.40, 0.0, 0.0, 1.0, 0.0, 1 + node_stack_interstage08 = 0.0, 6.10, 0.0, 0.0, -1.0, 0.0, 1 + node_stack_interstage08u = 0.0, 6.10, 0.0, 0.0, 1.0, 0.0, 1 + + attachRules = 1,0,1,1,0 + + fx_gasBurst_white = 0.0, 0.3, 0.0, 0.0, 1.0, 0.0, deploy + + sound_vent_large = deploy + + TechRequired = aviation + cost = 100 + entryCost = 4600 + category = Payload + subcategory = 0 + title = Fairing Base + manufacturer = Keramzit Engineering + description = Structural base for mounting side fairings and your payload. Decoupler sold separately. Raised surface can ease loading. + + mass = 0 + dragModelType = default + maximum_drag = 0.2 + minimum_drag = 0.2 + angularDrag = 2 + crashTolerance = 12 + breakingForce = 2000 + breakingTorque = 2000 + maxTemp = 2600 + fuelCrossFeed = True + thermalMassModifier = 2.0 + skinMassPerArea = 4.0 + skinInternalConductionMult = 0.25 + emissiveConstant = 0.8 + stackSymmetry = 7 + stageOffset = 1 + childStageOffset = 1 + bulkheadProfiles = size1 + tags = aero )cap cargo cone contain drag fairing hollow inter nose payload procedural protect rocket shroud stage (stor transport + + MODULE + { + name = ProceduralFairingBase + baseSize = 1.15 + sideThickness = 0.05 + verticalStep = 0.1 + } + + MODULE + { + name = KzNodeNumberTweaker + nodePrefix = connect + maxNumber = 8 + numNodes = 2 + radius = 0.625 + shouldResizeNodes = False + } + + MODULE + { + name = KzFairingBaseResizer + size = 1.25 + costPerTonne = 1000 + specificMass = 0.0070, 0.0260, 0.0100, 0 + specificBreakingForce = 1280 + specificBreakingTorque = 1280 + dragAreaScale = 1.5 + } + + MODULE + { + name = KzFairingBaseShielding + } + + MODULE + { + name = ModuleToggleCrossfeed + crossfeedStatus = False + toggleEditor = True + toggleFlight = True + enableText = Enable Crossfeed + disableText = Disable Crossfeed + } +} diff --git a/GameData/ProceduralFairings/blackRingTex.dds b/GameData/ProceduralFairings/Parts/blackRingTex.dds similarity index 100% rename from GameData/ProceduralFairings/blackRingTex.dds rename to GameData/ProceduralFairings/Parts/blackRingTex.dds diff --git a/GameData/ProceduralFairings/fairing1.dds b/GameData/ProceduralFairings/Parts/fairing1.dds similarity index 100% rename from GameData/ProceduralFairings/fairing1.dds rename to GameData/ProceduralFairings/Parts/fairing1.dds diff --git a/GameData/ProceduralFairings/Parts/fairing_side.cfg b/GameData/ProceduralFairings/Parts/fairing_side.cfg new file mode 100644 index 0000000..b3bf584 --- /dev/null +++ b/GameData/ProceduralFairings/Parts/fairing_side.cfg @@ -0,0 +1,69 @@ +PART +{ + name = KzProcFairingSideGlobal + module = Part + author = e-dog + + MODEL + { + model = ProceduralFairings/Parts/sideModel + } + + rescaleFactor = 1.0 + + node_stack_connect = 0.0, 0.5, 0.0, 0.0, -1.0, 0.0, 0 + + attachRules = 1,0,0,1,1 + + TechRequired = aviation + cost = 100 + entryCost = 4600 + category = Payload + subcategory = 0 + title = Procedural Fairing Side + manufacturer = Keramzit Engineering + description = Made from the finest materials found in the fields around the Space Center. Can be set to any shape required. + + mass = 0 + dragModelType = default + maximum_drag = 0.1 + minimum_drag = 0.1 + angularDrag = 2 + crashTolerance = 8 + breakingForce = 200 + breakingTorque = 200 + maxTemp = 2600 + thermalMassModifier = 2.0 + skinMassPerArea = 4.0 + skinInternalConductionMult = 0.25 + emissiveConstant = 0.8 + fuelCrossFeed = False + stageOffset = 1 + childStageOffset = 1 + stagingIcon = DECOUPLER_HOR + bulkheadProfiles = size0 + tags = aero )cap cargo cone contain drag fairing hollow inter nose payload procedural protect rocket shroud stage (stor transport + + MODULE + { + name = ProceduralFairingSide + density = 0.1 + costPerTonne = 5000 + specificBreakingForce = 2000 + specificBreakingTorque = 2000 + noseHeightRatio = 2.0 + baseConeShape = 0.3, 0.2, 1.0, 0.5 + noseConeShape = 0.5, 0.0, 1.0, 0.7 + baseConeSegments = 7 + noseConeSegments = 11 + mappingScale = 1024, 1024 + stripMapping = 992, 1024 + horMapping = 10, 490, 500, 980 + vertMapping = 10, 170, 694, 1014 + } + + MODULE + { + name = ProceduralFairingDecoupler + } +} diff --git a/GameData/ProceduralFairings/fuselage1.dds b/GameData/ProceduralFairings/Parts/fuselage1.dds similarity index 100% rename from GameData/ProceduralFairings/fuselage1.dds rename to GameData/ProceduralFairings/Parts/fuselage1.dds diff --git a/GameData/ProceduralFairings/Parts/interstage_adapter.cfg b/GameData/ProceduralFairings/Parts/interstage_adapter.cfg new file mode 100644 index 0000000..59e9716 --- /dev/null +++ b/GameData/ProceduralFairings/Parts/interstage_adapter.cfg @@ -0,0 +1,136 @@ +PART +{ + name = KzInterstageAdapter2 + module = Part + author = e-dog + + MODEL + { + model = ProceduralFairings/Parts/baseRingModel + scale = 1.0, 1.0, 1.0 + texture = baseRingTex, ProceduralFairings/Parts/blackRingTex + } + + scale = 1.0 + rescaleFactor = 1.0 + + node_stack_top = 0.0, 0.2, 0.0, 0.0, 1.0, 0.0, 1 + node_stack_top1 = 0.0, 2.0, 0.0, 0.0, 1.0, 0.0, 1 + node_stack_bottom = 0.0, 0.0, 0.0, 0.0, -1.0, 0.0, 1 + + node_stack_connect01 = -0.5, 0.1, 0.0, 0.0, 1.0, 0.0, 0 + node_stack_connect02 = 0.0, 0.1, 0.5, 0.0, 1.0, 0.0, 0 + node_stack_connect03 = 0.5, 0.1, 0.0, 0.0, 1.0, 0.0, 0 + node_stack_connect04 = 0.0, 0.1, -0.5, 0.0, 1.0, 0.0, 0 + node_stack_connect05 = -0.5, 0.1, 0.0, 0.0, 1.0, 0.0, 0 + node_stack_connect06 = 0.0, 0.1, 0.5, 0.0, 1.0, 0.0, 0 + node_stack_connect07 = 0.5, 0.1, 0.0, 0.0, 1.0, 0.0, 0 + node_stack_connect08 = 0.0, 0.1, -0.5, 0.0, 1.0, 0.0, 0 + + node_stack_interstage01 = 0.0, 0.425, 0.0, 0.0, -1.0, 0.0, 0 + node_stack_interstage01u = 0.0, 0.425, 0.0, 0.0, 1.0, 0.0, 0 + node_stack_interstage02 = 0.0, 0.650, 0.0, 0.0, -1.0, 0.0, 0 + node_stack_interstage02u = 0.0, 0.650, 0.0, 0.0, 1.0, 0.0, 0 + node_stack_interstage03 = 0.0, 0.875, 0.0, 0.0, -1.0, 0.0, 0 + node_stack_interstage03u = 0.0, 0.875, 0.0, 0.0, 1.0, 0.0, 0 + node_stack_interstage04 = 0.0, 1.100, 0.0, 0.0, -1.0, 0.0, 0 + node_stack_interstage04u = 0.0, 1.100, 0.0, 0.0, 1.0, 0.0, 0 + node_stack_interstage05 = 0.0, 1.325, 0.0, 0.0, -1.0, 0.0, 0 + node_stack_interstage05u = 0.0, 1.325, 0.0, 0.0, 1.0, 0.0, 0 + node_stack_interstage06 = 0.0, 1.550, 0.0, 0.0, -1.0, 0.0, 0 + node_stack_interstage06u = 0.0, 1.550, 0.0, 0.0, 1.0, 0.0, 0 + node_stack_interstage07 = 0.0, 1.775, 0.0, 0.0, -1.0, 0.0, 0 + node_stack_interstage07u = 0.0, 1.775, 0.0, 0.0, 1.0, 0.0, 0 + + attachRules = 1,0,1,1,0 + + fx_gasBurst_white = 0.0, 0.3, 0.0, 0.0, 1.0, 0.0, decouple + + sound_vent_large = decouple + + TechRequired = advConstruction + cost = 100 + entryCost = 4600 + category = Payload + subcategory = 0 + title = Interstage Fairing Adapter + manufacturer = Keramzit Engineering + description = Enables side fairings to hold the part at the top. + + mass = 0 + dragModelType = default + maximum_drag = 0.2 + minimum_drag = 0.2 + angularDrag = 2 + crashTolerance = 12 + breakingForce = 2000 + breakingTorque = 2000 + maxTemp = 2600 + fuelCrossFeed = True + thermalMassModifier = 2.0 + skinMassPerArea = 4.0 + skinInternalConductionMult = 0.25 + emissiveConstant = 0.8 + stackSymmetry = 7 + stageOffset = 1 + childStageOffset = 1 + bulkheadProfiles = size1 + tags = aero )cap cargo cone contain drag fairing hollow inter nose payload procedural protect rocket shroud stage (stor transport + + MODULE + { + name = ProceduralFairingAdapter + baseSize = 1.25 + topSize = 1.25 + height = 2.0 + costPerTonne = 1000 + specificMass = 0.0064, 0.0130, 0.0098, 0 + specificBreakingForce = 6050 + specificBreakingTorque = 6050 + dragAreaScale = 1.5 + topNodeDecouplesWhenFairingsGone = False + } + + MODULE + { + name = ProceduralFairingBase + baseSize = 1.15 + sideThickness = 0.05 + verticalStep = 0.1 + } + + MODULE + { + name = KzNodeNumberTweaker + nodePrefix = connect + maxNumber = 8 + numNodes = 4 + radius = 0.625 + shouldResizeNodes = False + } + + MODULE + { + name = KzFairingBaseShielding + } + + MODULE + { + name = ModuleDecouple + ejectionForce = 0 + explosiveNodeID = top1 + menuName = Decouple Top Node + stagingEnableText = Decoupler: Staging Disabled + stagingDisableText = Decoupler: Staging Enabled + } + + MODULE + { + name = ModuleToggleCrossfeed + crossfeedStatus = False + toggleEditor = True + toggleFlight = True + enableText = Enable Crossfeed + disableText = Disable Crossfeed + } +} diff --git a/GameData/ProceduralFairings/sideModel.mu b/GameData/ProceduralFairings/Parts/sideModel.mu similarity index 100% rename from GameData/ProceduralFairings/sideModel.mu rename to GameData/ProceduralFairings/Parts/sideModel.mu diff --git a/GameData/ProceduralFairings/thrustPlate.mu b/GameData/ProceduralFairings/Parts/thrustPlate.mu similarity index 100% rename from GameData/ProceduralFairings/thrustPlate.mu rename to GameData/ProceduralFairings/Parts/thrustPlate.mu diff --git a/GameData/ProceduralFairings/thrustPlate1.dds b/GameData/ProceduralFairings/Parts/thrustPlate1.dds similarity index 100% rename from GameData/ProceduralFairings/thrustPlate1.dds rename to GameData/ProceduralFairings/Parts/thrustPlate1.dds diff --git a/GameData/ProceduralFairings/thrustPlate1bump_Normal_NRM.dds b/GameData/ProceduralFairings/Parts/thrustPlate1bump_Normal_NRM.dds similarity index 100% rename from GameData/ProceduralFairings/thrustPlate1bump_Normal_NRM.dds rename to GameData/ProceduralFairings/Parts/thrustPlate1bump_Normal_NRM.dds diff --git a/GameData/ProceduralFairings/Parts/thrust_plate.cfg b/GameData/ProceduralFairings/Parts/thrust_plate.cfg new file mode 100644 index 0000000..c6e9641 --- /dev/null +++ b/GameData/ProceduralFairings/Parts/thrust_plate.cfg @@ -0,0 +1,90 @@ +PART +{ + name = KzThrustPlate + module = Part + author = e-dog + + MODEL + { + model = ProceduralFairings/Parts/thrustPlate + scale = 1.0, 1.0, 1.0 + } + + scale = 1.0 + rescaleFactor = 1.0 + + node_stack_top = 0.0, 0.1, 0.0, 0.0, 1.0, 0.0, 1 + node_stack_bottom = 0.0, 0.0, 0.0, 0.0, -1.0, 0.0, 1 + + node_stack_bottom01 = 0.5, 0.0, 0.0, 0.0, -1.0, 0.0, 1 + node_stack_bottom02 = 0.5, 0.0, 0.0, 0.0, -1.0, 0.0, 1 + node_stack_bottom03 = 0.5, 0.0, 0.0, 0.0, -1.0, 0.0, 1 + node_stack_bottom04 = 0.5, 0.0, 0.0, 0.0, -1.0, 0.0, 1 + node_stack_bottom05 = 0.5, 0.0, 0.0, 0.0, -1.0, 0.0, 1 + node_stack_bottom06 = 0.5, 0.0, 0.0, 0.0, -1.0, 0.0, 1 + node_stack_bottom07 = 0.5, 0.0, 0.0, 0.0, -1.0, 0.0, 1 + node_stack_bottom08 = 0.5, 0.0, 0.0, 0.0, -1.0, 0.0, 1 + node_stack_bottom09 = 0.5, 0.0, 0.0, 0.0, -1.0, 0.0, 1 + node_stack_bottom10 = 0.5, 0.0, 0.0, 0.0, -1.0, 0.0, 1 + node_stack_bottom11 = 0.5, 0.0, 0.0, 0.0, -1.0, 0.0, 1 + node_stack_bottom12 = 0.5, 0.0, 0.0, 0.0, -1.0, 0.0, 1 + node_stack_bottom13 = 0.5, 0.0, 0.0, 0.0, -1.0, 0.0, 1 + node_stack_bottom14 = 0.5, 0.0, 0.0, 0.0, -1.0, 0.0, 1 + node_stack_bottom15 = 0.5, 0.0, 0.0, 0.0, -1.0, 0.0, 1 + node_stack_bottom16 = 0.5, 0.0, 0.0, 0.0, -1.0, 0.0, 1 + + TechRequired = advConstruction + cost = 100 + entryCost = 4200 + category = Structural + subcategory = 0 + title = Thrust Plate Multi-Adapter + manufacturer = Keramzit Engineering + description = When quad-adapter just isn't enough, Keramzit Engineering has you covered with its wonderful Multi-adapter! Designed for building engine clusters, it also found its uses in multiple payload attachment and space bars. + attachRules = 1,0,1,1,0 + + mass = 0 + dragModelType = default + maximum_drag = 0.2 + minimum_drag = 0.2 + angularDrag = 2 + crashTolerance = 12 + breakingForce = 2000 + breakingTorque = 2000 + maxTemp = 2000 + explosionPotential = 0 + fuelCrossFeed = False + bulkheadProfiles = size1 + NoCrossFeedNodeKey = bottom + + MODULE + { + name = KzNodeNumberTweaker + nodePrefix = bottom + maxNumber = 16 + numNodes = 4 + radius = 0.625 + } + + MODULE + { + name = KzThrustPlateResizer + size = 1.25 + costPerTonne = 1000 + specificMass = 0.007, 0.026, 0.01, 0 + specificBreakingForce = 1536 + specificBreakingTorque = 1536 + minSizeName = PROCROCKET_MINDIAMETER + maxSizeName = PROCROCKET_MAXDIAMETER + } + + MODULE + { + name = ModuleToggleCrossfeed + crossfeedStatus = False + toggleEditor = True + toggleFlight = True + enableText = Enable Crossfeed + disableText = Disable Crossfeed + } +} diff --git a/GameData/ProceduralFairings/Plugins/ProceduralFairings.dll b/GameData/ProceduralFairings/Plugins/ProceduralFairings.dll new file mode 100644 index 0000000000000000000000000000000000000000..a6f6c526451a8936f49180f9a69cbe37f453072a GIT binary patch literal 86016 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�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 literal 0 HcmV?d00001 diff --git a/GameData/ProceduralFairings/ProceduralFairings.dll b/GameData/ProceduralFairings/ProceduralFairings.dll deleted file mode 100644 index d4cecd2c06ced4debd07c77162a56ef6aee36163..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 65024 zcmdSC34D~*)jxipWu93x$umzfla1_wAu|Dzup|LV!lob!h+-0~5+DkK3{HZA!_dTq zYF$yQf?7putyZn5ZMC&k>Pl-D>(biSw$j?=?e=Q7S8c1m?>YC*l7O~v-~aFP|No<$ z=bm%VJ@?#m&)x6y%nAM12tx>A;@<}!2=OSM^w-VsqrnKGgVkRNibuTPu6h13JQD;O7!xDLM+t` z(HU#+%jEWksH^d7(}eglC>}|@A*2aWgK!O=LKJaWmwFR{^yj}dhzFhi4AFfFvC{v! zr$UK@zXg!Hi~^8blNCY)_1CS50D=Kq6O|d-zw)ffs?OXV;K${qn=(AOeHi#He*{1r zmsRNvjQ?tcSTiL#lw1dlb%Tz=0`gam&Vp`K*OcVo<^&i?R{@>b27k}8aG+a=d1EO; zY$v1j>_0xwT0+(@e1#LHuv);dBr+v}=M8zGly^Dtc&5 zdbApW=shUXt{Q~&_bnH~L$wGe>HwI(;{Yj^Dnt5cJ&5sYi6r1aK5z*E@W?z4B^p7~ zqo{35j}`)keX*1D8d-ilRgSPbF$r{cPNcPogwF5RQ#ztI zt&593!nueEkAlxC@TS&)Juw+-gj+(oM8kb-~CAU(}EeQGD@IWhd8U!}CgafgOZqGi{LMx(* zqiulkBJa-Wc-D#T%0=lJ2zesC0gwBtjjb;?g&i{%ciZMZ$7Q?n6WO*)r9z&Z$~Ggt zj%k~e@Y_(sZSGG4ud4b1+x^6^$V2BNS5 zI~|!Dy#rCcKf8W2pJl%+;j5`J#&F_Pic9_m6&CG9 za0n9tU!tFgrkPkxPnRo!rs@Ol$MDFsd9)2~N9g9uWlwG;PDecZHa8m8T~jBYI0G@Q zKa_llH6(`G&M;)4SG1#WF!UJlv%~(_8q2+p2ZMk|wVx%P#9E{%@G891%Xv`4mfiYf z+tg|J^)(kF6y?T6XiSU53rHMMqU!k)){W?D#2&7>p^br@{>VfK%L=E;SWlA|?^} z#8M^}j3MeNN||OMdWsUHIjx=|zBDHp-(b!}i6)M3MtTa(-3-64CC;J(j~kbc8k=?Z zv9ZznsVS#uAf~o0W;(jWq8lK&k4NYxJ%!=MjF-X;6wss48bdhPyCefb&NR0;0ak;z z+lJofGlFKnKcoj;(Y;^}89|E!L2qg!1P6Vq9TVZRRoI2_CKa|2-mJpzh*WEe8Fa_A zaBrkH=;^((5rd!O2|HdD*&lX%u@h|Hrl5P%Y6rR6{^&mB>I7_$?X?4`b0E&@*LxA% zf}q)No3;x~2_QpT0X{<_Z6AgmpX~+o+P<^EU&31}n&7gyM>9DCR)i1pb8n}Qx> z8Vnh*$4HkQ?2}m+a@K{Mwb3u*3OV;acfYIGwrsZ@9IrqDaw<>?6a+mj`)!YGw{Fo3 z+j=s#HT%f4%Ay`=QSWM`kOuXVaXr$Y2)jyMT(?uKAQ}>xF=z%|IZWEq3OjDacWT)2 z#Fp9~X-_Zg>6L}@vD&_zJ$+mvSHBkq^^PwM27kw6BTL6^dtlB7C^Z?=ZMbYRXUH5m zh-%5vSuQQ`d$>*7zEYdL}Iu{Incf2hm+&q0rB|%89 z1*==lZg{wG5YMCOfeTc;Yj|1+lotyAKj;|7m)7Xo1&*?(jR9RJGrV)@P;A zhofWV*{|aY!BJB~(8qRhhN#-u1SX>E_StS`Q*p`;Zls_{o=>;FWV;e5bhgQuc+A=O+4ku(;UGl3LkK213 zUx*#4i}L{(;ZRp}1U3i-h3cu+_w7BGm8A_YR*B|1dtL`qNV@VDkuh+?)c z7#g9NP*EnXhT^6vArxnO;(fMfC)SvnP9^X*re@I7*O)?^G97m3Fa=va8t-qiBjgr`WEBqg30~ zc1i0`>|&3i#*~=ogJ_muDz@$Ux4?ALNgXHR)WlMD%{bq6YHi!819M%UUAx+;x9jat z>*k2lVAt98GHHLW(-?Lp#;~@fwq{RcUF^o0uCUV(E4L>?n~8R#U9VaN*zJjSP3!mU zhIpM_>o|4|IBM*gykd3@!Lef|@>V6}7Cc_Jux)m@(P}-;B)cTsvS^Iirp~e_DUC`Z zeR=i)Z)sn+wKNx9IySo4o;3e+u#6pc&-A&a)_qRch8QQ>4)4R%eY`4@VWLTW_GEY8 zYNsjU#DHSnN8=yN6St#Ib8NaDwD4SP6(>u@V5w%~7&*wOHu^I@X)OF?!!;r0GkM36vv?HHvBNV>I}E=t<2AMo42CU6)QwzN_z}0$v ztU2t=jFqV@XL6QuEosjL`%HUgK06kP7^_X&z1k_YOG~F3rA}$snbrOQ+#Az}S^J!J zyPZ2Or^D`$n@m5ZxDM{foKCw_5}zkxXA1Kqv-5FgNOsZy36sa1+4gMaYPOS$K!l)A zK-c!`iLagdLZ36op3TFr8u%Ni#558o;Ay_2#9c;lpQN}ssmt*g#FH!+y#k=_@RY%M z{mwBe9d}*!G5p|-Qz&{om0D(*xRTOc1<-LJoCA@VG-e~OrpRjm;u{b%k77#OK8E0Y z3cA})LT~{E?Y3hPTu8ydddSh zdxSN}*DTGdc4kPV4{g$zcBfsMW_>zscbc{7wCyzO(rFi`S(8q*0@kAw&49J&^rT4z zyVIQ}1?*0lahi0nJ863i)ec9QWzVv)cJKP@KQPqGokldLm{Vq#$=R6OE=xqgjXAhW zyb87Hk9LTc9+O7JntF90t<$rPA|ycMT!cotc*sl}9L;;m)MXLp?OkaXCoWBsV=8%M z8Z6u~(V_>2Q0A@ak}aJjawAs~3KfHxn=Vqpk*+jtN~s-e+Azv&1PtvVicxYypO4}3(^f9sY^$5wR6a{yEiehdUOAIG&97}-u z&(BNTG?o}nU;&;lE1cK|LiA>))+6%-Mp0k9HZQyU$XgMa_%uLM@^ff%dWw=n(VJu6 zff`@aN!We7olrf6S~Sy>0h-jlyG#5CHHhlQJ|snUd0$!iUCjST6B9F*>|Gu!l z9eU=|d3_?+E&;hs9mh0d6k?kTJlu;&e_HEF1Ytv({l}JJWy(9wYWJ=K(|S~DOgUmF z^ydBD<}yUw!G)>@%-i_I!icxUGHV$xI?Q`{Ln=?@+|sG*c$(3&o1;2}NZd(6xstgi z6Q2Q&S|=-YKAA0`o@xXkaTg*T3)4{;*%v=APfHuSG!u#Cvn&rvqM=Vz(wudj?+16zO_Sz2g7tIKq>7$A8`-7F!Sv(< z6eJz==;x7tnhLK+??aIGtbA!Il4@1H2yN{@04hDH{+e3#p)U{@TAayyhP_f9xxbfx zpa#{gU6@kRMq)F8UmpBi;^+Q$UZx{MJNyG+54u0r4VP{?ubgjD5kF8KzxStaV@ji>ruI za{m($aYG37GGoqtq}_6BQGanD>{zijw{7ji{*By|V7F{GivzJnMd4I?dU%ZV@bKv2HIau`LLOcRd3Y7%;Wdzl7dxJvXc?y5CURgQ z)+@GjoXq3EGgk6qrqCKIBl8#v=SeHhNW=DII}K*GHy*UTdWuSZ)y7C)+bkKjy*x{6 zJQqH$d3faJk}=@LcJwQ#%u$MNM@NgLJ&B;*!8?hTMf(_yeih8o#{sZz_1SV`4v%v{ z8H|1nFyxDV9gj@9yxbL=N|aMTYQ?=uP8YR3nJrOQ1ux-Ax3HXd(oD0_F$-i)ibzu` z2a|MvY=zRm&w68zHXvuJu9>t|EBO#0DcE!*;Og?IUVeKd;Z7f>1&931q^)=SKUs#4(s# zU}QnNfmfq#Q8R=+$;D;M(3-f{!ND01A{f~bO>Ufcl7r;Nk?28$1K7U9y2BTn?6H07 zq@##tMSk0_hAJ;s3{e}jiP)G1tL+O<{l1gP!%f_ zI!$`rF6#W1h6*i=ehbO5=V3z<)mrog!b4cVU0}PK&%>ha+aSn3R!UoIhn%tyR!7-l zpj;z38~qL#L**eWR2Ka%NlFp!2KGI|UL-&z1B)m6eZpP>h&znGoW+fZLG%>$m|Zng3?O9nV;n*p*{U2~)JT`E2vtOXKmxEo<2~m0s0X_w z`a>dw!%igPRJLRI$n-On5!tag6I6SxlWu_bGYDb@B;}mg>d)Pr#eb5fNXDDkg zX^wLt2S@UgbeyS;05^rwiD?x2VLmOK3hDdbG^h;z_iU`lAqv+uRQq&}&` z;9zI>SE%~eVwh_TORX_6-PF%{-O(6H&O~;1G?s#R#Sob8Xe>)E zAX4(sI3RC4K=d z{cbtsu#@*55ijQ*$HU%yg~XEkVZ0f|cS!iI1v-?4REa!W?hoj>dBcol(s?ori2o?7cXwbt_{!zh?l5Ic6PIGO|KJYUy&zAkgl2yN$BnpNiuqFFUM z^LZdg)cHTAHdA8D>i(nfP&e=DQrsqTh_3!V%r3ANLoP(@$MO7BoKH`&r$&DX%Mp)2 z_*V$?OhX?2PeBilZD5(E>e1`><0F28!s;(E4%ciSyWxP6UQ= ziJ;_cDn6G}RwxBm;~s>J*uq8a%_S{3!*mWtnq0H$t)nVi?O--3TNy?5IpuaaMTi-dHtsP|3{Kf7D*9`PP-)8RiG}{0 z3Y?L|PO&Sz_OwW!T#C`m3TO`L&0Q=tTkh|@s zPK;XZDtTw&^%AG8{Zq&>>2ry#c^CC$CLeJ}4{AO#&u-hKI-gCeqrZU?(ch9zs|aAI zj$yhXd)*}{t}IxuT}A=98cqC;6lWvb<#qx0PYKM_eVHSg$lP_j-ev>QE_lB)o%hOE zRXWn)YlV5*DvCW_`jDsBo?f?a?_PA1dD)&!S;(Z(Q%G6TIN!UcN~NUAIC+x1vNZPe zv1L%g(EG<41}5DtWc z%3WD=Y87NgZayOVHi-ob`T^J)O;|Fj`CM*4Z=pxB=%(fDE^p8ZOqT%RS@{T3zJ89VCYg?-Sr(PCk z)+#~{FWl$Khz1l5ccB{WdK}s|ju}_7gI90fP(qgI#&|Vx%t*tmB>QNrlfq6E$AIl; z>9`tY&&+gZGn*^D5k|aWe&lUG&5A9yXL9G)R+CABr&>;qVWfDqopp4M5Dt%cmktja zfYeUbEZ)fqJMHa!LTK9UQZbw?qJyPQPi|UK15{maZWjwjjU7r{9+%7}k88F)GY+L> zLFh6<6z60e(&KV$T*(*X=VE++ePl@93y?vZtvS< zTm81D*O_b2p(+zIn>jGjHC@&CoIa<^KE|HdXQO|dh@|#hI=lxmiBi?r(W}{ek5R)- z#<8Pzuvf;+9TQWF!p+XOF)`toHQ$A!SF_u8$OBT=$?jk~b2$1ZvU44TKztr&KI32> zXFGCrqiHJUH<*rxPgXCBv6wcW$MB(0IPq}OheMue$&G_~ik zF=P?vu`LEOT@XqJSv|1ESWyvWd|#e0utH=WADI#VSOg%)kY*g<*5k|%&5!<>oSb@; zMrVQSRMew8(hK`dZMB=cHo`L5<6s1uL!y5H#aS3y$a|djWK12Kh~4S7qoq_Y-4N3C z5>-kRlb=42xFtA?+K(j*F3MU^26~-i?M2MdmOrgxEr98#V zx~jCQv$*}+a^Q=?WQ+fqxyFw@@EUfb#s8vx#;5<^y5eG#bg}ZMCR}fkUbZ+qC&p8l z>d#PRAdf1K2aeoEPZPz6J zo1e9bclcSCc$c2ViTC8=Z+O@xW&KwU(fldKLwDRREbDi0mFI-^3N&1=eS&O**zrBF z9Wyb!QDw*JQa0{*eL623=~WFo(mR%%kL8CygnbOTnKEj|6{jc*%|m4_V+j9p$`51A zqo0U;PgEn$iIU!B&>ry#h&Vw-oB-b&GwwuUT4`U`AvDlD@{HlIvn)24XR16aptH-v z(E13=E7dr3Tzs&Xk}rcw%alskiJ^h2b&nj!Xz_{KjN^)(U4||j)w;YLhk74giTT=; zjj72Gk15U9x@c@&5h~@VQW0THF^-bp2!SMFIf}VlHIC)n>bquAjpVvQyU@0=Y^MT; zofWauj&5>gS&u3WG?7wn-Ru7=H{XDQ!XBmO75_`(`z(#2QSC|J|Ym)6uRFj_UL zO9r${s`HYU@4~6={r+SnR{whHB*gL-arAxItmzItg>^CJGW~#4LfFVAG)H}cg|+QO5EfKRQ}taM`h|I zrW44yQ+)_Fht*qbS>|x!EQ;kVqTD;kS-u6CoX7Ar(M{17q-!o$@-}Dqk{414@76?X9jC=(g2Ed&Ps_;!|jku(VWrZAB1x3=aADh zou0f2y6LGE$lQ?brbC0K6V*1E5nTi}e&Yz|HM&465hd`eh%Yupx48E4-ca?J_lE4` zeUOQ|9qEcbgwzrF?u_z@Lt(fm&X7!T7wR?)St={`v`AmtK>q9)xQ7=G@Qey?HJGs( zI^NzSccW`Q@mkx;z59fVW~nz|*Z`g;k{3k$v7*eaIhF50cXPd}(qaqMaW44jc2T^* z&U=f>hn(7Pgo4@3JhrEE1cV_M1mmSL55SH2a@xn3Cru@hQd52=B?9zNZ>CG%n0p!qtXNN0OPu+}o4LN@ zjy?WW_?VL~&YM7^!ntx*9ywmi7bS~)rzrcjlE-$*rYh^q5HxLzpw4)=R=MeFKF^RH z65np2D;Cre$qLx?;nP1Ks$^TPzc>>D4t3**nD#8Gr`fc+z{3^l<&zR6O}@#m=)WSn zaJSc=rk!lreB*A9B~6y1{;xTpY9x9W>PTLe6mC%QRsI2tIC|+JGgch0L8V(i`!xv`_p{0 zCOts0R2w3_mV#z_9X(v>_4Ej*2l0^aB7YA&rKn#c^Cg0eP`u&gi)&38l=CnW#n@+6 zHvn+9Y|4wZZK#`gv6;fBN_>1OZpdSCaIkx+1ZNn0+g4^2Ut#3lZNwq+bc<7}FC^f2 zn^X)>!!ZmM+Tc4kG*l58eFi;ZG>|dg%ec-jCMs5%uh0eHGAbLd<0b4jc!e)ve=lE2 z(NkoWUZ2T)684+SC*fE=zrhDAI={n*V-hm$>VSC^Vu8my)8p|5@P@h1?}_5B4DOM6 z4E7$4m+4m^vX3?*u0K5oU4Efk% zYOPTeI0%WxtD3t|Ll(c=lqdm{qN}WN8=2zWxp;bIdG>Yi47b(i^=pecRq%+sEfd4$jzE102W6r+kGbkjp9)L2E$C66rmH zDRcGCcD3&1`Hov`L*16sSdt=NH=_|~;Vabmnv);Tmrt3W&Bz(e z9yix>%<}NlHcOe7L<*wO(>BYQM!gQv=qYcCr4W)UcGaC$;X5;GV_I!fM^8jYq+VD)RB$II01jajNPTW7!CK=Ih1G-(whEEarx8h4m#n7u(8V_V`MC6%@IW=& z-@w6rd&}mgH&Rf(r*Sq1zkq~koGfHJe(IwN3_;(mf*)f9Fgx+3Ep;J=#~hxt1msAa zo0t|T0>2whGf>Bza3wlI+(1dzVWh&X5Oqbn8Vcj`Npgg#Wis7PT;TIXYdNYsc?(BX z$OJpF6S5N6y++zfbz21=L-9o$0V*;%J($U<8X6=fGIwe6y)ijWqEwljO$)EZE0t(i>%4)eE;WLc>oU%Exf4+o&BKrsvs8?< zaGt6VB2fT}m9UzodR-8E^~8XOSCsEyC2$+wp`f2E0so|9@5rY{_&T zheBYgM6ICd0mF0!F|TflDNl$(Q&Ic1mF*CIqOA{D*QBs3n z*KCZK>d7Defr05h`n8r2RnU|7FRbVkh)hidpnLXK`$oi~Pqp|SD_?j>V4)YIQJT&c{S5~GbuBzIN+gnU1t0EJ*mvpRH58fVy$ot{OT(4K{%W~jjJ;Z^Jpp4}RNRGxa zI>x=p8fb+D$Ml>z8em+$-kQ;WCPeb-8a^?SC)6>k3vBa_#_C`;^I71;M`rxdb_D#5 z)rBD~IT0B|JAi|;=t#~^$!U+_jAnaX*=yiyXI%+83>sT!Sm?!Q#oO^IxFqWAV)9H6 z>J0bo6SE;$_JhqKpmP|dK9ZYKqysJ?s{NU&jdSHhRJv^UmZ{kM^vyvfZfwPZcuY2r z_$0iNF&8L*V9hB=o&@=!f@l{=JVNn{V+FA}DwT8wRpGdZEoCLx5!hz7G_vA|c0*1< zx%ZC73T4qiq6Z{@W91k-HcrS!<0D7#i>fg-h#K)`8Rj!XwQnp(t|nr>A z@NQX$PtR*iDkiZ2T(}=r*zr3ubCJ4mntc3UF|)G1YAKBscHz#25I44&G)@3{QI4$I z2ucL{v7nD}Jk>tXF7Up>afrR*<7ho7f2LW$l?!*`1Cze!5`dgj!-o-Z{8)K>7gidg zkcU*K$S#T(+C?ch7_*au4%&>!mHkascFA>wixuU~ESlZiTf_noN%M^(EK+oIixho` zMT$PsB1Io&k)jW?NYRH|L^fEw!qb)f`wx&h=+~Q-?6I$nU+}P zm2)Kf2n*R`dsm4v*Acy|{h__9EgMzik-e*(_-DPVoj7{$nrkGo^fW(cqyrnEv>Ax` z-elBb*Jba1@Bxocrr3s()8GI8dv}q4^LuxOz&wTju)%`;WK8q1i*D^&3Kz!vp4h9h zhE6~@aUwwGEzmQFdVoCg%K*EUNnY#@SwqW-cLni2j3`JreGG5YdB%h(xUNe%(coS% zCRglV8DfCC3OEGjI3$<%EqQ0A6FCZlyYM_}$3CVKFLnrM+G-_E0usl^7icD~CcyHf*>-2nJb>Q)E|(ZRt8ePcphO<`&#hurkh#c#%9 zQZ3~sXda0#27Cn~utTR5%Ce6kTm~K_qYgRROMnt((qg}b6_o;2xQ~RJ+(93i0*&x& zxQNahEdAirQ?&CD#n!jQA8XFUk z^=Q$VZ_{;Mg{iEpZ#gcU0i4GLU92ag;!mfmo!AgPA8-nNc#_aGVOVsY$59D}+X0z@ z&@^FL2GkWK?8q<>nkGy!G$r)}vO~8=^3Z$|7!c@~_5z^NuT1eXcmXP!zPU|YT<7xi zOTEdPx9{VLm!gB0dMCjw4_4A|z?!nt&F!G$M7H6>c;zt*?`x`bdg@$=lbcJP`0~ta zv{WEgh_^cFeBTF|(k$ciPf?-9p1tV&kvo5^BDoK(Qs$9-8A0_?hfn16&VG)tz}xW> zYKQij@h-(`>@eedlGp~MMdgEtz+exWO&^BB*8yns_u4M>9(+QCFUUYGpXVq_M*8SI zf3g(b^Jjw{MPZ1F^!`63%y!`}BQ_Rku0k6!O=qfMfRC_s`o?MIE(N}^{5UXtV;Q>^ zW>bIebeD{p@h0krbOEFBAD!mln2DW12Dx4k9O+84VUq76)#m89|Fxy52+w{ zhdIcfufd!tlN<5jbc)-946^6TAp5vHthzzxkD%cAKb#&#@)&)ZmI7!|^oU~Awwiuv z#Id3HF*NAp-=S3EbPC0N2%UjY(-Bddvr(98P!aiBMnApwcqPeBVR4ap?U4w0?eSGT zg{@&k$>u@E;6nwRF6L2w4F@0-&F$y&2z)<=-5Dgj<)KGmA*&BJE$G9)Xto!#Ky;O| z10Xa_Sf)m(J_eAqp2vlYkOu7urMCQ;?|28)=Ooq0*J05eM8cNc;lccL!yYmg_ zG$O}T*{&LDobOwx>ijSKqX`lfZkhkBk~OGAP+|Xks+w?M?>L`|IVka_$5PiyDvq73 zp>+r+)&uaI^%7OSBNf61cXkGpQ|QY?JwhR-9-$CZk5GuIM<_H%3eh+1KM6}U%|!rb zK&Eb_uOOV-rb}FJ2+_d4z#Toi6GlRIU=*XP-@*&gz7zH_`764feubQk zBJ^f4Y=GurXimP~MkkmiFR+8EZn;&+mt%L6R#Ny!zBr05BJ*-Ax|FQLpk7R=V)RUu zc+4A9DOAtc$YY_68hQBT(Yt`tH@BDJ8F#$VvmnC5FDxWBAQ0&@774KZ1;{E z14eE2!qOemK>%d3z3ir{zME=l&@8^vfUhvVqJYDTV z3|QFiurVgIJ|QFWhF-kK$WD5oLF#oSXj7jH$=^VweMEU9%&~DaoNFlZ`h;Zk(4IztT_72&>;lz<_!Db( zft3aWzPl{1sVLXiQzIaby{~A9&!c@da{ENzf&QI(%1+vcj@}NugFg6%c1_fO@z)9J z{AwwO1Mts~NV&qBespq{JNLUE((m#wtR0n!hU@&FE68T>QId|zG+xqzjH>yPXiAW; z(@~jdvXHOSN&kwZqcY_rrKof5aOJm^oAB7(u(Oj^=LbZwf1H+E1vxEY9 z_s!##zoW3jnxr>S(3TMjs+2p|Ypl73T zgcF+)VOzy%N=Q#)qkh6&$}`r9jbrhyV89xmQ zr_X`x_8W+wCsQ35a$pDWTAd>ttVa687H}ocCiCMy4c|~x zVq2?1j-KDR>7gvVyaQT`vQFf7OXbb=?6`R7l?S8h01bRJC!2@oB;rrO%4V}3Zjw3Y zKH7nAN`Q*JhcNaY^7e(}dZa14QSc^RR3v#-kXM?`Kd;9oF~#_FZ!6SdUQ?iQM3i`q zD3-RCk$i_L<~)=?evXk01~SMR6>~mfqT2y{DmF{-)4m$bX{~-9sPIWxp2^~)x?`z4 z@t4H9s!KL?^`|MG^>GOd=={f@x;utrD0t`Wy55fIbLt)+cI110LnA0dnkFNd`WJm| zVVY(FDoEo#EeLQO`r|5yg#!}MOc({G2U%Jz8q^e-+Oi%!8%Cgc`>ll0R|qrnb|PTj zz9iGG376B_htxs|ZgmT#(onBCB zJE4&3{h==P{+wg#Q;0)>Ge1tFr_czA@YdL0r>UdrjKgqa`~NICzf(omG^SHMDig|> zxtUC5)>Hccr!-{Bv4>2MvB8aq${XQeEiwR#37<8@XYrg2Kjd$|7(A6Sc_yZ&QpDRF z$b8}EE(mtT9RoR%o_QM!MtWi^k?UEWDtjk0iiR?Rp&*)yWhHe-=ozo5wDCR4anSc(=O z)Y0T6N4BN^j`DGd-RPuYKU&8bOZQ7pw@Dsk) zM$>j<%(VS+R8{`8-H_9EnSC->9})Tj@lh&7(Z|&Y`tLHVf(v!Gcb_3yc)?4*z4cpE z1h1)3DEgzm1^XS`DrArxm8keWE;XBv=+&sUi1Wz>X)=@wl36H-UX8jXo#%XMYWX_< z7*(3uJTaiJqqastY>7yxKw@9hEWis1eU8x5o!@n?1yXzW-A1!FvRAPIf3PciXo z5X@K$7HR0isCP5+TVM8TNy^CCH3)4S;3DZh`4Oe;4#DqbTftQafgTC_)JMrLv-;#5 zxigV9lC40MSN48xLFRU7iW6ajDX>9w7i8%vN?=4ko(=0M;*}Ic z6A~TaZiIOJmW;97@)_{RCq9{*8bv%dR*WyK|M~_S?%OT}c0(!El0DIGyk$gS+ zNfLu()P^ra*{7oHVYM+YKT(~y94yorKgPRwbXTp9Owi|M*ja83H%xOKL}N~!T!4hUtPMv$ z$2}SO@d-#j9qD&ruflBZ-gP~gQBf_J6*b^=5XYGK&O7P0in67K5cfnz?gVqJKpm&u z2x99?kr9%xi(=rCTz42D#SF-%vmGm5lH5%q=qi(*{3hvg6N1^_j?AeSVfCVWi7~Mc z5d7nGhjt}a;%3CTuf`#S)_9`PzlznVZ2gsVnES3%rFQdI#dwF?X>- zd5=J?zOgdQr=phbL;`$+*y67wsde&FiSf@BDpE%tNeNsAKZ)0lr)NZFezWSc=pbnB zKMy*#Lq~pJ2(L`KarF32NI=QZzYZfL&xQk*Cme-6ti)$BMU~O=(#6X(0Fpe6ETRMQ zXu85u!noedFJ(;ksDOIwQ@*uw*uv|BBUpi$(Yqkn=pDiO0-i#5BEAR_oscCLYFZO> z?4tM{OpitMvq%uV8z31B!h=4C(9k^qKC3)&FP>I)apLpzOxy<;y&oX)008OMT^juY zg4N#Dy_=#BBD4wAI;=?m`j);+I;sV=^VTGiWzT`R2zk8o}ZjS3MCt;b}gPdfO7e6NLAt zR3eO5Dp3?4^TQ0w3+I^V&i@J0LPSV{jLf_!C*^geu)!OqVVw%tR6g`YD1JfCT<9Fw z4|H)E>T3?$i)gUdeR%k-g$C$4{#D~&AO5lFh$U;f&jW$!h{mR)fV4*HOUSLcKp|fy zn|(|6!9$M%Nps55aVh0#KkH2Kd3Ad~q15L806m}=>Xt~poRXNdi!W!Cq&PiA-HWmz z?#(d9zcU)gev{ml*dodNHCV-o&(;v9B<0xSR08~dHpgKC=4-}R1hb4Oa*F0f%Ap>h z+#_jHf~`V<_H$4ha;7vPpRIH(TZ}fySgxL;ZjpZTz+!`t&?fu{`N}2pGyw<4UjYBk zYn?J}%8Y5VTG0^-;S!iIiWj0d2i=8!XEK^xBf=|(lN-03g>e=OBIsTj1nrcS;+E6o z@443>d&=Skh`SH??;?nsx0(7t{zLt1r$+AZdyz)Gr?t^b!jwA}2=q?7fRf@l{KE>E ze<~i8EABuT{zShGOTWoSzcE1HIHr9&PHW;xzD57g&Es#5ElcDRQv#=!Sz;eUcL9YT z_irh6#7$)+?@j-ujwR9of~Pb51H-Q{e2L-TnCE-`cBFlPVGrm*ak_`T+WrY-9>NdS z&qjfI+(dbc;V_&$D6Xrc*n7jXy%z2yy{?7CZMCz#^Tm7=BPgyxT1)Jxy2}oVkt(7* zj3S1_p$6ig$Ncv@v++ZSw?_#6z$SQR6~T7}!Cx}`a|wmN%(-0Wr!v0jzRR}67bD-V zutYepr4)H#auyVqKz>jR))Gu$E*BIxG5tK7!oPq%LGi;P(xC$-3W?`Rsay+DM?ujB z7!qFz5hW5L$|^_*iQg8Iw#WMJvVCGX>-;9`^EO~mJc7JJ;(C-CKV=Bcpx9SVrGBiK zO8pe7JScWo2=P_14%HeG51kQDXuP}RL>yC5I%K9^j`(3yq_+lRQIDqArZES=RK4wB<6AWYd#9kWC`t{pw`%u zuR)F_`eCJ@_zmnFr1=J53|d)e10?emPIU=tHYH|2PEagHNi9+Bp*o$6w3c`mxx=f$ zZ;1;{vgFDyHOewO?&8r&>>HT?Fg!hoEZ_EH1Myp=8aEB5nE@QskTnLTG4d+ZE9 zQHq$}&$aMdA6f5y)P6|phfScKz{v#us29@E=J)t6tM18So5F-mz`MN^S6xV0lc;5^ z&LV6&=FJp$Ds!$7UBZJ)fPc3ytNx4lm{`hj$4?;6GsOxq0?xZRhb_E+QzU+YbPXbf zx3WhOH#0(OpZ?^|@j{>TncsH&zgK zr?{QdRUwCk+FjxyVIXckN_(!jTYQNz%v6BgD;{S|W9)wM4bB5 z?6dGyeWIKZBP+ttL9^Oz`i41WoLR1Wo+aB-rQus!tQEyiWi=JmKSRO!I-Lkr3#=2GqpQ4a)ahE`nc~(57pmzwia* zq6x}3#8B5M{Eaa2mvFe=_dQ5EtDy$dv*)>V?-WyB?{T@F^OD70VfZ>*>Gur3;eHkL znS})B7ZT5j3~L%G?Im7m;5vE_ z^=k?nbW?26N#?h>#wU8-KrRdPHr*26EBqD0cUMyQ;-a?@{tuQcJxBV?lU$Ki}HY{`YdJuk}(Hm)8>PV)!$*$qaL>J*97Jq8qWAn9aU)4d?z}Hd#_y`$g6~ zU)vAM{YEa=oeZTWxwDM-y=TGal3J2YO>!eZfERS?T;vlPfVXE z(|tDF=J$z56?QD_R3QGYu2+gZMA+F@EenU=~Z5 z<~zw>DqfBeHY(cnL4TcC&@8bxtIq~@slvXB{OUz7&v18_9*ht+2a9U5Ct0vw1Un=) zQQu%Rh>{(F~%wfC5_lt-8jiN?!a()xVHH`gI zyVxif6UB83d!^{h#zb+m!Zy||MBJT>JuB`&k2_I3s4&uVq9~?mF-l8Tnk3dUCN-NR z4k+yVRTTHQ;=HZm0{zauSHy}IA@`>t>OiRQT?@w zR}NBTVMkD&CbCdvdF2IGs5Kd1R}2jj>U&&bry(Dt^b9 z^rbf8S|g>9F4M(4#zw_Y(c)%^sH z=URckQSl43@L6K}I>MwU&k}EDvHSe9#A)j}PPpqH@pp>$L5WqkA2R2N;9yTufbhE?Jh85PvwA7h}(QNSD*bw45A9 zE$1}x59a)(MlI(w5&SsG{G~=M=X6oZ*lzKO%6fB+I8kDr3dmd|)-m?1pz^H|^lm5B z?Jihijd)k3qc*li1V$)M`t=&|JH})k4T$t8#f{=pg4dtJmKY=cwb{6!f5g8oi%}h| z&tl6_M}uOwk}`xb=1g%DW6uh*?pdP!63XFO?sv};Z(T~*Zm}GFL!%WoKC&m2bf+#tTloYI~f#Ip(`d!8-6C#7)TvswI{F=?00 zqUdtULFRXkm?JUnHMWR`E2N$&D;U@!URBtlU^sBDc#E-TwOPR>+K}kIlBH-1>y~K4 z;&)dOCjDibxZ-Llg=%w~xR0@C1*O|2DzD*mg3_HQ`u8v=m-c+Im9b|z?tIaCZ7y!R zn3u&S1h$J|iHUScePD<94r3R2)?#$HK)kB3%~%IrAbzT_r$kdAC4Qx_Z;Q=Xy}rd5 z^-EI&JB4)}<#&U4s&H0dm*`U1R|;nX+p4g}g6;rZ?t00&9N4J1MqzF4MS+XN4;ALB zJTb6a-2N%ad1mFRz@_3h3Ol)eRp2u5j>2vXuMT`tG~6ILUkk1aTp{@49dvolm=(BE zT(9Czc5MVU>qg0Wx_4{fYO!2lKdavr*dul-Y(@2jz-n%ioPR3#WZ+t{Rbd77Hw3O1 z^*l@1Q~F9|SKw3PZWZ@;>o#DIGqzjo#fPa{gTPL!uG=QXi%qutGSu>WL< z=e*b>!CDa)PQ~k$n4pW_1NyKsr2R&mT8xtrgdCIQxL6MM4xx#g$L!_Jy$K&&n{7EoB0WqjE)^_yS&`KA6nTk@mr^WR*GAF%z8ge7juU0Up; z?y#EA$pcQF8 zG{v{kZhnk^hvDBp#=pa8BR|H!!`P{@@b4b{yC1(jX5rsG_;)|{^|4cea52JOLF+9W z(1krc3fBVqa5vy!qzEUG1bY}RX1Ic3AHxBL8v#qOXGWYm7+wabp%)^!pW!_W7i&aW zq5YWVYTW+>L}BeDgkM0v*ryfN#_`@f@$6tY3J96{!N@eMmbAyU z%k76DXSDP&aY(=1egbz@!Z(#?MWFFSrygv~KL`C^O#M`0}EB(iGtI?yq zEv6QSw8`3u*rS`n(&n(VIbv3o3p%{j(5M{{&os1Y2laES+O=Bk2i3QVL*laH`Pw1z zDTezQwlh4Q;pq&oaxDRm-?Ib~Xh-O6-EC3$PV9ZXt$(9n3E;yG%aAXX=ra90?}fovhoEOITd!7=)~Kl&gwCFl&03lEx|7rnY0<_V2sb$&*B%o) zT_r*@>Rgv=UgKcoT5Yj@Q_)_~XI0(~=r6h#@F%q{Sd#44z@=^wUntR#*9za0kW*5V zg2(k$Jg3>l8C5T6l_DD42&{7O{JzF%Q z*BaYP{se6gMzE$4`*9y<_i+2ZS?Ww!VP%IjH|JxKf{Ngc?r;h3dCV zt13RE-=jsf@9HNr0FFvHzsk8n;%z1L$t}2XZ9( z0r8h=!)P(?ixe4UTC6yUlFo5T5nc?tP1Z>NTI0v!A9|VgP3I6&S&dbQEd|dZgrnH4 zAb+02aFpRj?T^LB7(K?7T14wGO1(<}S6cIo4&yG=eur^NNr~t%_PI;M3ge5e#l{Mw zzGN9-Tgl1b=`LAqOg5HQtu;n@27OSUG-1SeTi*|VJ0MPh=X4m$8?etM?z69eCi|T4 zLaU!OTxYy1zTvtR@WO&yS;F1MWbN4M%_zk}7r|eJ9)QfBV#g!SdhXXxH@;?^hVW{< z$xZERzdls)HDkYijr%oYGt-BezMkuPnBjhnJaDr1bth;})^-54pbeG-USD-DYWV=x z{4Lt6R=ruKJyZK#eWS6#x7D?i`=R*(av=}juRUQ0T$7pS zfEdQUVy$s4{N!!%Vz9<_K>QT@u?NI|7T3G>7@Oe9R69{ukM>sKqpn)x-3s!nvqA*d zh5iA~A68NCaT!|TA+S4dfkTwg{h1|cZ+dXVX^x${jg^xY<>wi-xk+cWp1zD=&u4izwBP> zY0;wJHg0a1hg#mD&l7JOPZw=;@7Ml-I<3`Y%X-IEZyw}a-qs%~s5jr%MF|hVY)kdef_)QNK`l^_!{)ezNugVVAbf= z&xACu-V1m@TvT^8wB`O^_Wa^&=quh9?XDX@--!Ko&7_jP&8HIk^-6QU`wDK4*UL7j zw-*zI6UHuG}bkw3h1Ngdovi_#EQa^-RnBaL=Q2%CYi30p$ zx%TIJg4Z@o_Kb=d6Xtr#%**Wso?4SUXTLtdPrbnzI>C$ly`K9x$46N5BdkfS@vVC5 zOCR>qxbt0i51iiifGHS3z|1MfqtyUE*5w)g2I1jznRpk$QCL@`y79`pH%63{*F8rQkn+=6Asx!SP>+uR3@U-*9H>A-B^7l7AS z{m%2YsKB|;0kNg_1J6C|=PeikkMp)@+e=OaeAs)O_i;hz0*~{#z+v{=$8pEvMx6iA znt6r!m@Dbst0z6@d;84Ot+aQtVPTY6VV>x}(7VD2l;7+3fnHr-QT{`3A7&jt_Kuq5pM5%U9@O8mio|6o<8Qr} znd@9{dk6GKw0FG+#DAB*@7-g%u+zB5{C%nEdzxF^9`g&>nct|-XvFMG|3^vOcR<{x zwff@RzRHY3?@QWVeJHfTx8K|uI@Pxav!l}i4@L$7AFAFAcyZ7r_ULyN4f$+splGt^ zfO%77hwm}7xO#+O(TMM$Nu%U`^Xo+y`z}LTtcgI;#ET3Mh~J~PKOp`F_!=lnv|60p zJ{6dP`6od~JP%kQUIeTcZvn={yMS%NE|`KlT%CaP86GdX5nhQ~2UD=iM)Y+IH;D@n z9ul7^=wZnN3`ZI6XLu0sm%)SLBGFiQm?@e@I@AK5R#>ZDF77DoLHJvM6dpjhsAxdD zPHZR|Mffn_b)wVWkMP5Q6h4gbQvv)=0HC2$S!y|4um4>9G}6Q2`TEVEuVZ)-!`qFQ z#DRkS2BkX4@D1ZXgcUr@Va+6ZEub${YhEOd5A`5?As~eZICek7gA5Nd)Lbd?bg0Kl ziPu7-fKLVXGd#%fFhkAFk{R|e9AG%ga6iL?3=cEZJj~Cq2k`080K-v+`xzc&c$lGv zy`!f?wG4Y04lo>LxF2wmv)_BUIKw%J@E+$d;44n8k8(sEzU#z~iVri7=4Y)KjsmVM8TC_r9mGD?)1kwFH5M@olOClXSSdbE5FF9C1 z^>`RzJA9bKS}-Nng|*;y;?Zy|!q9=?0K-v+`z582Vn-S7XLyj|VTM`}>jU_7XcVv| zGFn8waS&mY5%3k~AXXYzL=M`l1H$l`5T|0;!*GD%D8u~>4>CN=P;*FHE#NE88s}z6 z7-4uj!zURYL@Yd~m|}YvK3PIO`3A!$!{4GX!*B;byyE$($L$SyE4)*@bG(;%AM}3N zd(iug_eJlU-mtI1*Xdj2JKlG-?>3(ot6U$}yM@@bpf#+6eV}4cg!rK9^MMSk=I~bW z1yEiTj{wGM9s}H7^TdaS=^Y^hlFz|<{x)FGX#AhN7Zid-2X-pPy&U?hf^A4>6;}E5*DTigR^XP+3VfY>hnTDFKpCIH zzu$_5`UE5s*^OSDVOPvGAU@w^MW+^!X3 zJ3fjs)jgok)dqBW-t3;Q-Rt>_{%8Dq&$B{&%lnk5l4V$%*tTW;{2iP7rit^WiIZDI zme3*=o;SED7$*}FbR&joY)F$R^+q!;WcyK{- z;H>%E)(*~3Boo_)H*Oi6yZ($bTBfU%)5QtfHV<#yx_QU)#73yK9fSc~9^F12p*ic4 zgOJd(Z8(7orQ3#wC~4a)F=x@nzl&EiLLb5oJefi#-Vctwi995mZb?i2G*}%KCojm1&7uRY{`VT zDq7Ea;#@U2G&H!G_y^aE#8~v~Z9~HL_@>2(f^3qA^`LwNQHqd8L&q?bx403S(=ETt8`kpQSr@eEFjpIu5`)S^~ zDYe>S%bKxaY0@(p$C||ANRB69VV0hfDJ0#GP&Uvho%;KE15RGXifMzZ3UaPfx zwI-JDavL!m&1`zu<=sw=TS6I2f}-tQ59`6Tpu58>^J)f2G8*bu7~QEwb$6%k_FEKC zY1e~jDGE1(dM~OqQ&VG1?Zv@c=xx18;l_^X?Yc_EBQ z)Eh}6?gX0%{pLkRY=pDh8~bue=L&Ot?R~z-fSvBOG{#AAv z8xwAA89;4CBl$NdzuIP?bnn6H@dw9(At-~hE>9@xGKy@)A3GtCrvlnw}U7S&pMtkl~?LF9whmxf?C*W#XYScHF05wAh>q{Al-6DbmMP(^4mg_6w z_BN?jqb+5F_Q70R0P$@od>&SX4cr2})v(&W@=Dkdb1o&8#ld$xT9!>Mj*~_3_Z;4= z+U?VZm^I&vB8DvUBA=tSt}88WS_0&%m<^wd7N4vVPF^OK1?4_U?Yzv;sGHfJg%=F1CL3btC+-VcTZudQCIkZBrT{yqK z9$WH8n>*XV93k0l-^>0y^3_4vB8$~1P{-g!jg6i<##at=mx7Jn_O{e|2&3}NM%>s3 zng5otSjfG2VrB}kIb~h{N zL$hn%?kBNXn!BEclhrsiBYr>rBOKn0^)kjPs-d*NaSJjGl!8QuJq{`1d__urz~tO zx(dqOYDa;ytA<_oAm6rwqu7v374L_BT>Y+)< z#I7z^Ya2n+hU>-Dqu{QfjTRi(EgvUXEOI3X3D2VbYPUg`Be7bQX-ab>R`dB`tmgAs ztmd;!~s4oX5s) ziFBsmU7O8B6k#`4(r^cX(`0*?PeY#}M6G7wk&kSz_gZeH)`lWBmnT$gyNhAi47gT_ zMuWBjI!&7+Kj~HoBBJ_G?piI59|WH#iWz}#CI-e~HXU)Kc}=DxHqNRNrmiUj#>&fU z4F%`8xEZ#YIB8~SNj&G~dzKRE>UHk^uqW|oLqw-I;D*Ho zfvZZW`DnY=yb(1BL^D{H7TGM2Hvg>}!gfDIWx&{1*6l2ll22nnfve0)e5s7_4veb3 z4LDLyU%{GmMv*JQ*azID@34&H&Y* zo26BpxRiD>klO5wHn%UsY^c|)MQ>jVv+fem+F{U-5>OTj5Tcm*7%93tlJ1YB?7Q^Hn0(IC#URV$0%;3FX}& z3eAK!G=XBqhK*`;+~r+FDYj|ue3Lva4l^R!rl41CF>k(EYf%j#YiJ$C^1N3h?sQxl zl$@O>-i#<7@WHZ|E-GON^g3?g?JF!mc7_(hXAz{R3wRzSjb#1M;f)}2G{)LO*1D~& z0ktSw>wfs4$jR_$nTz3WmKJ-lR=$*G#%fXOhXdv_a>-Jd*#uhA3_5a)vivdvsg!$& z)IWo~c~|j(M{?y34ug7!%w;;oiClPxWl1AJ9r}^veNzZC! z8y9a={ROOv!R*3(YLl4U(trsulT5mpon(bW7W{dZTmJ`PD%Wcyel00y5?8my!^Jp( zT?^ac#en#v!3vU_+lbxLPCssJ#;KLN)L?A}atNeGZ8;@&wCz`K*Q>bc1ena)sw_3I zYKSajp!^D=6fqTu1fs@l|032+Mf1#w+uhZUmgan4TIBbs)Vs!BtNe|G3Dss}GbyH+ z&N5<2mPuxt$-H5fy$Xy+ry4}Jt?Yr@elElGIlCWpEsnxDFeO!2Hs99vftJ@|AL_|> zpi*R+i?IIa(^ZzOw5Dc#;uyDW5iQ18g?A3Z{M_H>*ILi=9@S?l-fIGpn0ttkEM19< zE!^<^CiSy5P_YW#_TYAwFicqsV;w&+cMVXtRc^BZnyGry6V~D>lruTca^fF@WGx8> z;?zy968q9UI#3CMv4IBmJmaNjj+#ran$_MiL^@_ zb=jm<4na!i*=M;fu~E}fSR#UXOA_d}R8*|S0&H!;L4-srS;+LtPGj>ugJ}9R%=x$^ zCL}~)q^WP2?@r_gSwhQcr4em50~oIL)OffkmIAVMu@qpkDKYkA_tmt5B~5E)_G7;_ z@wgkiyc8wLrG+=InUEqxby|pgd?Tb4$0oO;(Tu;tyjV0(3Mg<*k~gz`EjQfa^3u=9 zZ8qxJ^wqn6(>NG|KdUnJC&YDQrN}f+e5)p8@hQcXjgY%&t0heasNk-#i){*2ikdYfZ zCSQg|=JLF<>geUZU-#S8W;i0 zq*r_LlJOznu!Qe(mo{odMeVD%B$@cMQ9dNw$)o&uZDd(|_TYr&!fdkg|5Z$VNU_K7 zQ_OitgQLPUTJ*IGFRZV#IA(OP6&55rwZ@V%$fJE%+XY?90BEzQy(BA|05RHTBQTro zx=&>m9}nRn>e}=k$IA+}Pof&^jInT<&X#0a)U?07=y$EMsiK`uF!q_k%S^K-+YJc$ z(SN2R3IgX^mXYyia_gdy4`Ktgnrv%&qrzH6w;P(5w|jH9aZN(Ra##k9!O{$qAi|Q2 z;-XbcbBS@2fcdg?XNgV}+_r?IaTsS@?58y)cizBcTeUU~N)`m!x%FALYkk5N)~Ql` zpAR8tA*Af7OJhCDr~~h&EX%r6kJ$yKz>#G}%S(j~mNCk5UiW=;(>F*nt!QD3CPB`7 zJ4=eG3jDRcwW%wFW>K%#W!8E-xQ=Za-GNfp??kLFvWIp(OoS`egdv-(T)LelS=1#b z9qrwAOkyxC8l0a3zXe?ZMZp&Q*%3T}2&sO+hV32S(84b3Zo4Kn%q+e4pjN|!Y|UdO zv|VbJ=Q#9YCGTk0dL?85Pd?tjtZg}_N&R#KoK^ci1J|eGu*6?!Q$`>~0cl#KNz=k^ zJE8zys@}K0h->`h>S72)!`%;2m1UhT*wFn^9yR;2yy<)gT}uKQEre~pS53U5%2>R{ zs=rz4HfJEPvEAtQ&H%XU+~CVOhlX+dY}4(y3WwwxJpK`o z+5*;a5&x`x0k4p6bFV__9ZK|h9J$GF#cf#m7--FPNUO}23#nV7RLB>`Dpecl$Cv2< z>(fHWwazP$kGN%Ox9neSsuqG~b?eq@jkK`av{pOR^`#p0m-bZw%Ya{D!GZ~?S(p1O z`$wZYJ=$*C=mO`SX@Nr)@jFB|hg!Uv?|}FMmai zIrsMA8qY2z7&oAv=oa(hIMkD;XhrW1FJxRbJZo@X7}j~?pJ?9ZSJY?R zM_c5@i>7n6zs0NPSP9^c%8K8j;{u;U3A#uw=Kd}C!a^pW!xk!(YXkX3(&ttmr3Z0L z7^#O%dTS%0UD8|Dy5xa45$q(z-zS%wf0R})(c*r28;;;PmwWCxTEZP!e{mxBEnt2` zl5xpNZocZ?H~FeV_iBcRxG=@<4u9A7ZQF?4#Y^NJhZShBqYew^>2PuXYTGql^OoQc1( zo!owAk&>i!fmIU!OnOl!S_tZBuQ7nPg?0c&?2@=+UZVwhtD(E!Br@mz!o1Jn+mlTz(HPQiIhIQ zE?Vo8YTG?#dlzX>-cpn~(B3225>F>#wn7`7kv1e*?r%Qc=+uroF{JZVqc?WqCAG6) zJb4YTBD*acxDAyhxf>=&!+IBZK$?~&?eo}cl=gn}0Al2VKyUFmab_fq?io0jx;uq*Dn(kscKe7v~iWiqASB_N}o z$o=3Tq{4_53CeTt_w^N8^fDw}TcZs0c>R~HFO1wBaq%!c_V>G=I`Y#gRfe&~!~M07 zZ7WbH@!K=0ca)vBc<>2N!7P7FG>~@fz!mrP04eeDz#F6R)7Kb=O3C)nSxF~}qCVh!6o%IGHFgV$~xUXwpZtjsBK%54+Dw@r`TcNf5g-7TOk zMy|9*BAHTz>@k%mkI3&hXTHPRYth$7A>KM^MzEZ4u!8h-jH1&13f4qe$(KUc`75Nv zZAAs5cuJR1xl_*@w9=qXo%w_)wu3Z^cQs;qd1A!{#PrVn^Am;g zt@4?fQ+(|ncgt&qsgvcs)m*uk=eovqlA@=cQcln#v}rf=7L(jqdG8j^x;*%JsxqD{ z7K?e8n;CpGvq&SgT$y{h+zB^%j+Ybh2Kik1jEAp*f)M3tGS%J-NS9kBOE;#4Kx?er zItAK3!82gxz4!g!vHVo|sng|t!T!|fQ&W{3fYSI>d5tH>gcE-gQ!|76V^hWPd`?3G zn*fA!+?mRcl|Lv48tn&${EyX2Kr4`>C!!UFclm0Po6H{D$tt!{dkIZL7s+2 zmE71VLv!%sao#%rO#XPWIP=QPE0DH$nqCWq;>_R^`Pa3n%nH=H&!1pVP70%_$U;?lKj-U90-jy!U(JgE&~;`$@vcpU_FEJ2_F(UtGB=$U_iX78WDGa4xsq$L`jiQi`?@hI2e zGf6x;=Mx?xZ6LLlsH0t#eWM;iwx|eVT=z$3^(^z|?51#J8+{ z1A*H6eSpZ6l;v~dDxc!MECn)<62&l7hvl`R(Eh^GgO6d#;4?_6k=Tv<$&F(b8K8d) zm43z_vI*j1o@GQ=)%>`SANOWL1^R`tse-ut$CEBsI(=&J_w!YRt~~g;2`UY1p#BF6 z*iQmGhQ8fTG(zS0|HNNS(k0TTRbJ9vC_;oG8^mC>DaBA4C4ocA_#X6W0`PMal`qwk z31rSbKg%N}RJm{5goFCaGf;J(TtCskPtPp+>>d;*Tac)pI#tL|ptgOf6o#!~qIc?) z>2m)>{)qAQexa-)_B%FJ!e;J$k=TX4HxGSkkDcJFbEc%N5;O#H@aG8NL>?<*nEnSW zZ~u7ym>=wE(S-lRQ+$b)pOWh02k=BlOMcP>P9)J#kflrrKYxbDk_UfL-h+>y8<{iW z$>KVJCb)Pr@x>EhCFGH5{t(=$X$|4$#^n;WA$N+>;N~XFdy|-&HI<&(`yYn(q#Qv| z{^0pMW{QSOC9fC-wE8sV`JXNaGkeEK7fME?y=mEp!Do`^y<=F35>D2YgM?;5-c3lq z&2)f|kogPiYdX>KnY|gW<4Ix_mosw&Qs0}zZ%r1bNlj-d4=chy1D|jwauVmY!qNOh zVtV#Yelve`h&R@VksSC-%>ic5`>T&i`Ds~jh$g8uDZF(}f zMb~@Z6i46@7g{V#@)=nK;uMCSbxW` zJ`0=goyTj>+66J-Q0U!MBe*mK?A*3UY*8DFhj1<>Mj#r!y6@4~r?j(aZ^ ztjU+iVV}xtW^+ExJ9L;lqff*~Dvg0QEEcVoYYYmR(51Y$n0UfP&xTd&bj>n@j|f%@ zN8C6hsKJNDC-Qk&79Xdfc4l?HUHDgb-?{npi~sSX!r%F?AO7hd{FDAa7@wM1#CY6F z^oO9WJopy?o}8RARVRk~1nLgHoHA=vlpK2F1MqW|{*clI{=Y37L(C}VlzWAWG@Fi( z%USr?*N<|V`)O*te5*u6R(#SORc|t1X(Y2)S$%$ zj|2oum5K5#uJ-p)(MZ2HBiWbfI3o7@p;{09;+WB-k9eRrVw*&pL7d2`j)^pHA((K) zItroCd^lKcDT=4bKJtLK^qIlOV;EJin$oScBJX2JJ0l18b9u?r%-*XQ8&6-%F|kKc z8AWKcWPBmE-+4&a$=G&%uVdu-|H_vaq^zzGRUwvWo+}Rgv_3xd1 zwSIo{>`SlIz87qqd!cr2W0Pn;=kn+H3Mo)6=20QZliYTy7~<{Z zmZ69f)fYb3prjHQXMNcVjgXb*(I;FyVN|?*sxmh zvd$q@{3)aFS@mLp6FRmb&kpOqu98ikr*iH$s>Nd*{_8BJPtYFu)4%%K<;U*zL?S>H z9f&%sgGk*NfgFQKMK*bL;GwSl_i@e>RZB;cr|<|h=yDRVSP7fcxW#aWXl$vJ0 zezu}R7uAwv&mXxOtG>8WDv;F?oH*CoMTT9qkdGTYJHH*H_+osIo4J-qnNap;fAS}< z@1yK*LD^FFtc!mxnJ=~;1^NeI_+IOMKpi9x%YXO#Z$I{cj&PcT0qK~h&b=_u<`uHV~bDea`JdzrK2kkp}H6a+S12TIp$D4Ex0LkyOjt|lQlFq2amGofa%a4uZq&>@f{sGEc zSZ#BTh4*gk*Wf9RuK=9?D!duQL-3x^!PZ~34ILNyu3`SLu8yVFLr2Ww!`v6UM{VTX zLGEKlf3Wu0!hBTSRd~SzCtn^W&wr^Fr*c?7(d!3WoQsbwE60aNZgQMsbq{ONM=S@_^PxgK6hSU^()*i-}1o=)<=H6|2JM)Q&vGRuby;gW^(S#e|K)Vda3#^fBx!U z`?KGD=i={v_UC`_4?CZoR)G7X@4s_6ynV;Nj^!O5$bEusWsSX+t441ctA^5<{abpzb~>)2o2odriYvzF=^S>qd)HB9=KJ1 z@Q{Fx1-;1O%yV4RY;0cVs%_5n=Q{KJTsC@bt@B8Y zjYY>KlC5eAfA#SJX5jpsV|ifMo{lW(u;#FxbB6P5g1ZBsuTx%B_lGjt;MW|%@=^Mo zWjj{ezWP~kGqiV=ez&b99pLFOZgD3$WMCBE53BGE4;7B-oP%~O8SwMhX=St>F9kX# zpaUln-?-|>tFkw33^$TKBn28nDhJAWia+jH-eG$tmr!1ZA9Qqdlz)%bo-d`N#X3r< z0|&zVMdOpdm$rS7TL*ddiq)B;zK$s95S%}F^HudkqhJ60KTd(;{`+|T5og_g{qz4H G1^zdNZu(&W diff --git a/GameData/ProceduralFairings/adapter2.cfg b/GameData/ProceduralFairings/adapter2.cfg deleted file mode 100644 index e45e1ff..0000000 --- a/GameData/ProceduralFairings/adapter2.cfg +++ /dev/null @@ -1,143 +0,0 @@ -PART -{ -name = KzInterstageAdapter2 -module = Part -author = e-dog - -MODEL -{ - model = ProceduralFairings/baseRingModel - texture = baseRingTex, ProceduralFairings/blackRingTex - scale = 1, 1, 1 -} - -scale = 1 -rescaleFactor = 1 - -// definition format is Position X, Position Y, Position Z, Up X, Up Y, Up Z -node_stack_bottom = 0.0, 0.0, 0.0, 0.0, -1.0, 0.0, 1 -node_stack_top = 0.0, 0.2, 0.0, 0.0, 1.0, 0.0, 1 -node_stack_top1 = 0.0, 2.0, 0.0, 0.0, 1.0, 0.0, 1 -node_stack_connect01 = -0.5, 0.1, 0.0, 0.0, 1.0, 0.0, 0 -node_stack_connect02 = 0.0, 0.1, 0.5, 0.0, 1.0, 0.0, 0 -node_stack_connect03 = 0.5, 0.1, 0.0, 0.0, 1.0, 0.0, 0 -node_stack_connect04 = 0.0, 0.1, -0.5, 0.0, 1.0, 0.0, 0 -node_stack_connect05 = -0.5, 0.1, 0.0, 0.0, 1.0, 0.0, 0 -node_stack_connect06 = 0.0, 0.1, 0.5, 0.0, 1.0, 0.0, 0 -node_stack_connect07 = 0.5, 0.1, 0.0, 0.0, 1.0, 0.0, 0 -node_stack_connect08 = 0.0, 0.1, -0.5, 0.0, 1.0, 0.0, 0 - -node_stack_interstage01 = 0.0, 0.425, 0.0, 0.0, -1.0, 0.0, 0 -node_stack_interstage01u = 0.0, 0.425, 0.0, 0.0, 1.0, 0.0, 0 -node_stack_interstage02 = 0.0, 0.65, 0.0, 0.0, -1.0, 0.0, 0 -node_stack_interstage02u = 0.0, 0.65, 0.0, 0.0, 1.0, 0.0, 0 -node_stack_interstage03 = 0.0, 0.875, 0.0, 0.0, -1.0, 0.0, 0 -node_stack_interstage03u = 0.0, 0.875, 0.0, 0.0, 1.0, 0.0, 0 -node_stack_interstage04 = 0.0, 1.10, 0.0, 0.0, -1.0, 0.0, 0 -node_stack_interstage04u = 0.0, 1.10, 0.0, 0.0, 1.0, 0.0, 0 -node_stack_interstage05 = 0.0, 1.325, 0.0, 0.0, -1.0, 0.0, 0 -node_stack_interstage05u = 0.0, 1.325, 0.0, 0.0, 1.0, 0.0, 0 -node_stack_interstage06 = 0.0, 1.550, 0.0, 0.0, -1.0, 0.0, 0 -node_stack_interstage06u = 0.0, 1.550, 0.0, 0.0, 1.0, 0.0, 0 -node_stack_interstage07 = 0.0, 1.775, 0.0, 0.0, -1.0, 0.0, 0 -node_stack_interstage07u = 0.0, 1.775, 0.0, 0.0, 1.0, 0.0, 0 - - -stackSymmetry = 7 - -fx_gasBurst_white = 0.0, 0.3, 0.0, 0.0, 1.0, 0.0, decouple -sound_vent_large = decouple - -TechRequired = advConstruction -cost = 100 -entryCost = 4600 -category = Payload -subcategory = 0 -title = Interstage Fairing Adapter -manufacturer = Keramzit Engineering -description = Enables side fairings to hold the part at the top. -attachRules = 1,0,1,1,0 - -tags = procedural fairings - -// --- standard part parameters --- -mass = 0 -dragModelType = default -maximum_drag = 0.2 -minimum_drag = 0.2 -angularDrag = 2 -crashTolerance = 12 -maxTemp = 2600 -explosionPotential = 0 -fuelCrossFeed = False -thermalMassModifier = 2.0 -skinMassPerArea = 4.0 -skinInternalConductionMult = 0.25 -emissiveConstant = 0.8 - -breakingForce = 2000 -breakingTorque = 2000 - -stageOffset = 1 -childStageOffset = 1 - -MODULE -{ - name = ProceduralFairingAdapter - baseSize=1.25 - topSize =1.25 - height=2 - costPerTonne=1000 - specificMass=0.0064, 0.0130, 0.0098, 0 - specificBreakingForce =6050 - specificBreakingTorque=6050 - dragAreaScale = 1.5 - topNodeDecouplesWhenFairingsGone = false -} - -MODULE -{ - name = ProceduralFairingBase - baseSize=1.15 - sideThickness=0.05 - verticalStep=0.1 - fuelCrossFeed=false -} - -MODULE -{ - name = KzNodeNumberTweaker - nodePrefix = connect - maxNumber = 8 - numNodes = 4 - radius = 0.625 - shouldResizeNodes = False -} - -MODULE -{ - name = ModuleDecouple - ejectionForce = 0 - explosiveNodeID = top1 - stagingEnableText = Decoupler: Staging Disabled - stagingDisableText = Decoupler: Staging Enabled - menuName = Decouple TopNode -} - -MODULE -{ - name = KzFairingBaseShielding -} - -MODULE -{ - name = ModuleToggleCrossfeed - crossfeedStatus = false - toggleEditor = true - toggleFlight = true - enableText = Crossfeed: Disabled - disableText = Crossfeed: Enabled -} - -} - diff --git a/GameData/ProceduralFairings/base.cfg b/GameData/ProceduralFairings/base.cfg deleted file mode 100644 index 01a6c14..0000000 --- a/GameData/ProceduralFairings/base.cfg +++ /dev/null @@ -1,127 +0,0 @@ -PART -{ -name = KzResizableFairingBase -module = Part -author = e-dog - -MODEL -{ - model = ProceduralFairings/baseModel - scale = 1, 1, 1 -} - -scale = 1 -rescaleFactor = 1 - -// definition format is Position X, Position Y, Position Z, Up X, Up Y, Up Z -node_stack_bottom = 0.0, 0.0, 0.0, 0.0, -1.0, 0.0, 1 -node_stack_top = 0.0, 0.5, 0.0, 0.0, 1.0, 0.0, 1 -node_stack_connect01 = 0.5, 0.1, 0.0, 0.0, 1.0, 0.0, 0 -node_stack_connect02 = 0.5, 0.1, 0.0, 0.0, 1.0, 0.0, 0 -node_stack_connect03 = 0.5, 0.1, 0.0, 0.0, 1.0, 0.0, 0 -node_stack_connect04 = 0.5, 0.1, 0.0, 0.0, 1.0, 0.0, 0 -node_stack_connect05 = 0.5, 0.1, 0.0, 0.0, 1.0, 0.0, 0 -node_stack_connect06 = 0.5, 0.1, 0.0, 0.0, 1.0, 0.0, 0 -node_stack_connect07 = 0.5, 0.1, 0.0, 0.0, 1.0, 0.0, 0 -node_stack_connect08 = 0.5, 0.1, 0.0, 0.0, 1.0, 0.0, 0 - -node_stack_interstage01 = 0.0, 1.20, 0.0, 0.0, -1.0, 0.0, 1 -node_stack_interstage01u = 0.0, 1.20, 0.0, 0.0, 1.0, 0.0, 1 -node_stack_interstage02 = 0.0, 1.90, 0.0, 0.0, -1.0, 0.0, 1 -node_stack_interstage02u = 0.0, 1.90, 0.0, 0.0, 1.0, 0.0, 1 -node_stack_interstage03 = 0.0, 2.60, 0.0, 0.0, -1.0, 0.0, 1 -node_stack_interstage03u = 0.0, 2.60, 0.0, 0.0, 1.0, 0.0, 1 -node_stack_interstage04 = 0.0, 3.30, 0.0, 0.0, -1.0, 0.0, 1 -node_stack_interstage04u = 0.0, 3.30, 0.0, 0.0, 1.0, 0.0, 1 -node_stack_interstage05 = 0.0, 4.00, 0.0, 0.0, -1.0, 0.0, 1 -node_stack_interstage05u = 0.0, 4.00, 0.0, 0.0, 1.0, 0.0, 1 -node_stack_interstage06 = 0.0, 4.70, 0.0, 0.0, -1.0, 0.0, 1 -node_stack_interstage06u = 0.0, 4.70, 0.0, 0.0, 1.0, 0.0, 1 -node_stack_interstage07 = 0.0, 5.40, 0.0, 0.0, -1.0, 0.0, 1 -node_stack_interstage07u = 0.0, 5.40, 0.0, 0.0, 1.0, 0.0, 1 -node_stack_interstage08 = 0.0, 6.10, 0.0, 0.0, -1.0, 0.0, 1 -node_stack_interstage08u = 0.0, 6.10, 0.0, 0.0, 1.0, 0.0, 1 - -stackSymmetry = 7 - -fx_gasBurst_white = 0.0, 0.3, 0.0, 0.0, 1.0, 0.0, deploy -sound_vent_large = deploy - -TechRequired = aviation -cost = 100 -entryCost = 4600 -category = Payload -subcategory = 0 -title = Fairing Base -manufacturer = Keramzit Engineering -description = Structural base for mounting side fairings and your payload. Decoupler sold separately. Raised surface can ease loading. -attachRules = 1,0,1,1,0 - -tags = procedural fairings - -// --- standard part parameters --- -mass = 0 -dragModelType = default -maximum_drag = 0.2 -minimum_drag = 0.2 -angularDrag = 2 -crashTolerance = 12 -maxTemp = 2600 -explosionPotential = 0 -thermalMassModifier = 2.0 -skinMassPerArea = 4.0 -skinInternalConductionMult = 0.25 -emissiveConstant = 0.8 - -breakingForce = 2000 -breakingTorque = 2000 - -MODULE -{ - name = ProceduralFairingBase - baseSize=1.15 - sideThickness=0.05 - verticalStep=0.1 -} - -MODULE -{ - name = KzNodeNumberTweaker - nodePrefix = connect - maxNumber = 8 - numNodes = 2 - radius = 0.625 - shouldResizeNodes = False -} - -MODULE -{ - name = KzFairingBaseResizer - size = 1.25 - costPerTonne=1000 - specificMass=0.0070, 0.0260, 0.0100, 0 - specificBreakingForce = 1280 - specificBreakingTorque = 1280 - dragAreaScale = 1.5 -} - -MODULE -{ - name = KzFairingBaseShielding -} - - - -MODULE -{ - name = ModuleToggleCrossfeed - crossfeedStatus = false - toggleEditor = true - toggleFlight = true - enableText = Crossfeed: Disabled - disableText = Crossfeed: Enabled -} - - -} - diff --git a/GameData/ProceduralFairings/baseRing.cfg b/GameData/ProceduralFairings/baseRing.cfg deleted file mode 100644 index 6040005..0000000 --- a/GameData/ProceduralFairings/baseRing.cfg +++ /dev/null @@ -1,121 +0,0 @@ -PART -{ -name = KzResizableFairingBaseRing -module = Part -author = e-dog - -MODEL -{ - model = ProceduralFairings/baseRingModel - scale = 1, 1, 1 -} - -scale = 1 -rescaleFactor = 1 - -// definition format is Position X, Position Y, Position Z, Up X, Up Y, Up Z -node_stack_bottom = 0.0, 0.0, 0.0, 0.0, -1.0, 0.0, 1 -node_stack_top = 0.0, 0.2, 0.0, 0.0, 1.0, 0.0, 1 -node_stack_connect01 = 0.5, 0.1, 0.0, 0.0, 1.0, 0.0, 0 -node_stack_connect02 = 0.5, 0.1, 0.0, 0.0, 1.0, 0.0, 0 -node_stack_connect03 = 0.5, 0.1, 0.0, 0.0, 1.0, 0.0, 0 -node_stack_connect04 = 0.5, 0.1, 0.0, 0.0, 1.0, 0.0, 0 -node_stack_connect05 = 0.5, 0.1, 0.0, 0.0, 1.0, 0.0, 0 -node_stack_connect06 = 0.5, 0.1, 0.0, 0.0, 1.0, 0.0, 0 -node_stack_connect07 = 0.5, 0.1, 0.0, 0.0, 1.0, 0.0, 0 -node_stack_connect08 = 0.5, 0.1, 0.0, 0.0, 1.0, 0.0, 0 - -node_stack_interstage01 = 0.0, 0.90, 0.0, 0.0, -1.0, 0.0, 1 -node_stack_interstage01u = 0.0, 0.90, 0.0, 0.0, 1.0, 0.0, 1 -node_stack_interstage02 = 0.0, 1.60, 0.0, 0.0, -1.0, 0.0, 1 -node_stack_interstage02u = 0.0, 1.60, 0.0, 0.0, 1.0, 0.0, 1 -node_stack_interstage03 = 0.0, 2.30, 0.0, 0.0, -1.0, 0.0, 1 -node_stack_interstage03u = 0.0, 2.30, 0.0, 0.0, 1.0, 0.0, 1 -node_stack_interstage04 = 0.0, 3.00, 0.0, 0.0, -1.0, 0.0, 1 -node_stack_interstage04u = 0.0, 3.00, 0.0, 0.0, 1.0, 0.0, 1 -node_stack_interstage05 = 0.0, 3.70, 0.0, 0.0, -1.0, 0.0, 1 -node_stack_interstage05u = 0.0, 3.70, 0.0, 0.0, 1.0, 0.0, 1 -node_stack_interstage06 = 0.0, 4.40, 0.0, 0.0, -1.0, 0.0, 1 -node_stack_interstage06u = 0.0, 4.40, 0.0, 0.0, 1.0, 0.0, 1 -node_stack_interstage07 = 0.0, 5.10, 0.0, 0.0, -1.0, 0.0, 1 -node_stack_interstage07u = 0.0, 5.10, 0.0, 0.0, 1.0, 0.0, 1 -node_stack_interstage08 = 0.0, 5.80, 0.0, 0.0, -1.0, 0.0, 1 -node_stack_interstage08u = 0.0, 5.80, 0.0, 0.0, 1.0, 0.0, 1 - -stackSymmetry = 7 - -TechRequired = aerodynamicSystems -cost = 100 -entryCost = 4600 -category = Payload -subcategory = 0 -title = Fairing Base Ring -manufacturer = Keramzit Engineering -description = Structural base for mounting side fairings and your payload. Decoupler sold separately. -attachRules = 1,0,1,1,0 - -tags = procedural fairings - -// --- standard part parameters --- -mass = 0 -dragModelType = default -maximum_drag = 0.2 -minimum_drag = 0.2 -angularDrag = 2 -crashTolerance = 12 -maxTemp = 2600 -explosionPotential = 0 -thermalMassModifier = 2.0 -skinMassPerArea = 4.0 -skinInternalConductionMult = 0.25 -emissiveConstant = 0.8 - -breakingForce = 2000 -breakingTorque = 2000 - -MODULE -{ - name = ProceduralFairingBase - baseSize=1.15 - sideThickness=0.05 - verticalStep=0.1 -} - -MODULE -{ - name = KzNodeNumberTweaker - nodePrefix = connect - maxNumber = 8 - numNodes = 2 - radius = 0.625 - shouldResizeNodes = False -} - -MODULE -{ - name = KzFairingBaseResizer - size = 1.25 - costPerTonne=1000 - specificMass=0.0064, 0.0130, 0.0098, 0 - specificBreakingForce = 1280 - specificBreakingTorque = 1280 - dragAreaScale = 1.5 -} - -MODULE -{ - name = KzFairingBaseShielding -} - -MODULE -{ - name = ModuleToggleCrossfeed - crossfeedStatus = false - toggleEditor = true - toggleFlight = true - enableText = Crossfeed: Disabled - disableText = Crossfeed: Enabled -} - -} - diff --git a/GameData/ProceduralFairings/changelog.txt b/GameData/ProceduralFairings/changelog.txt new file mode 100644 index 0000000..b36674f --- /dev/null +++ b/GameData/ProceduralFairings/changelog.txt @@ -0,0 +1,301 @@ +// ================================================== +// ProceduralFairings v1.3.1-1 for KSP 1.3.1 (YYYY-MM-DD) +// ================================================== + + • Recompiled for KSP 1.3.1. + • Added compatibility for FilterExtensions to all parts. + • Added the ability to toggle the fairing side decoupler (no more separate "Fairing" and "Fuselage" sides). + • The base and nose shapes of fairing sides can now be changed in the editor (no more separate part configs for different fairing shapes). + • Updated the part configs for compatibility with the latest KSP versions. + • Repackaged the distribution for the components to be on their own directories. + • Changed the versioning system to use a - format. + +// ================================================== +// ProceduralFairings v3.21 for KSP 1.2 (2017-06-07) +// ================================================== + + • With the help of user sarbian, reverse engineered some changes from user KortexM. + • wait for part.editorStarted or part.started, then call an update to reset attached procedural parts (fixes the part shifting upon craft loading). + +// ================================================== +// ProceduralFairings v3.20 for KSP 1.2 (2016-11-08) +// ================================================== + + Added the following changes by user KortexM: + + • Ready for KSP 1.2 (recompiled, small changes to configs). + • Added Interstage Nodes (but no trusses) to Fairings and Interstage Fairing, two connections possible on each node (up & down). + • A few code cleanups (ForEach to For mostly). + • Fixed Fuel cross feed (be aware that flow to or from the interstage nodes is affected too!). + • Fixed ghost nodes appearing when adding a new fairing base in the VAB/SPH. + • Fixed blue ghost lines (invalid fairing outline) when having multiple fairing bases in VAB/SPH. + • Fixed interstage nodes positions for Interstage Adapter when resized. + • Some UI fixes. + • Code cleanups (deLINQing etc). + • All other bugs/features untouched (hopefully...). + +// ================================================== +// ProceduralFairings v3.17 for KSP 1.1.2 (2016-05-08) +// ================================================== + + • Rebuilt for KSP 1.1.2. + • Fixed thermal issues with FAR (patch by user NathanKell). + • Fixed procedural mass issues (patch by user NathanKell). + • Fixed auto-strut joint issue (connecting to the same rigid body). + • Improved payload auto-strut performance (strut heaviest part only). + +// ================================================== +// ProceduralFairings v3.16 for KSP 1.1 (2016-04-20) +// ================================================== + + • Updated for KSP 1.1 (build 1230). + +// ================================================== +// ProceduralFairings v3.15 for KSP 1.0.4 (2015-06-27) +// ================================================== + + • Updated for KSP 1.0.4. + • Merged pull request for Procedural Parts bug fix. + • Adjusted thermal parameters for KSP 1.0.4 (hopefully). + +// ================================================== +// ProceduralFairings v3.14 for KSP 1.0.2 (2015-05-10) +// ================================================== + + • Updated for the new FAR. + • Reduced fairing shape update rate in the editor to 2 times per second. + • Adjusted default fairing decoupler value to avoid "vessel changed" messages spammed by bugged KSP sliders. + +// ================================================== +// ProceduralFairings v3.13 for KSP 1.0.2 (2015-05-03) +// ================================================== + + • Updated for KSP 1.0.2. + • Rewrote automatic payload struts, strutting all shielded parts now. + • Fixed bug when it was impossible to revert sometimes. + • Fixed slowdown in the editor when tweaking fairing sides. + • Fixed thrust plate mass parameters. + +// ================================================== +// ProceduralFairings v3.12 for KSP 1.0 (2015-04-28) +// ================================================== + + • Updated for KSP 1.0. + • Procedural fairings now use new stock air stream shielding and drag cube rendering. + • Inline fairings now check if the top is closed by a single big part of the same vessel. Make sure that's the case or the shielding won't work. + • Changed and rearranged tech limits for the new tech tree. + • Converted textures to DDS. + • Fixed manual shape UI issues. + • Number of shielded parts is now displayed in the right-click menu for the fairing base. It might be inaccurate in the editor. + • Removed old deprecated parts (non-resizable fairing bases etc). + +// ================================================== +// ProceduralFairings v3.11 for KSP 0.90 (2014-12-17) +// ================================================== + + • Updated for KSP 0.90. + • Added optional manual fairing shape controls. + • Fixed tech restrictions checking in science mode (patch by user Zwa333). + +// ================================================== +// ProceduralFairings v3.10 for KSP 0.25 (2014-10-11) +// ================================================== + + • Rebuilt for KSP 0.25. + +// ================================================== +// ProceduralFairings v3.09 for KSP 0.24.2 (2014-08-03) +// ================================================== + + • Updated KAE DLL. + +// ================================================== +// ProceduralFairings v3.08 for KSP 0.24.2 (2014-07-26) +// ================================================== + + • Updated KAE DLL for KSP 0.24.2. + +// ================================================== +// ProceduralFairings v3.07 for KSP 0.24 (2014-07-25) +// ================================================== + + • Decoupler workaround. + +// ================================================== +// ProceduralFairings v3.06 for KSP 0.24 (2014-07-20) +// ================================================== + + • Updated for KSP 0.24. + +// ================================================== +// ProceduralFairings v3.05 for KSP 0.23.5 (2014-06-17) +// ================================================== + + • Fixed collider bug introduced in 3.04. + +// ================================================== +// ProceduralFairings v3.03 for KSP 0.23.5 (2014-06-17) +// ================================================== + + • Added fake parts to make tech upgrades visible in the tech tree. + • Restored TechRequired for old parts to avoid issues with loading old designs in career mode. + • Fixed bug that allowed to cheat tech limits in career mode. + • Reduced default fairing ejection torque. + +// ================================================== +// ProceduralFairings v3.03 for KSP 0.23.5 (2014-06-06) +// ================================================== + + • Added "sandbox" tech to specify minimum and maximum sizes in sandbox mode (see "common.cfg"). + • Changed mass formula for all parts except side fairings. Generally, larger sizes are significantly lighter now. + • Part mass is now displayed when you right-click the part in VAB. + • Rebuilding side fairing mesh only when really needed (faster in VAB, especially with FAR). + +// ================================================== +// ProceduralFairings v3.02 for KSP 0.23.5 (2014-05-22) +// ================================================== + + • Updated KSPAPIExtensions, should work with Procedural Parts now. + • A bit less restrictive tech, allowing sizes a bit larger and smaller than stock ones. + • Trying to avoid moving attached parts after loading design or saved game. + • Fixed wrong size of newly added side nodes. + +// ================================================== +// ProceduralFairings v3.01 for KSP 0.23.5 (2014-05-14) +// ================================================== + + • Updated KSPAPIExtensions. + • Added size step parameters for RSS. + +// ================================================== +// ProceduralFairings v3.00 for KSP 0.23.5 (2014-05-13) +// ================================================== + + • Moved files up to GameData folder (no "Keramzit" folder anymore). Make sure to delete old mod before installing (which is a good practice anyway). + • Added new resizable fairing bases with configurable number of side nodes. + • Old parts (bases and adapter) are deprecated. Launched vessels should be fine, but you might have trouble loading old designs in VAB/SPH in career mode. + • Added new part: Thrust Plate Multi-Adapter. + • Using KSPAPIExtensions for better tweakables. + • Removed old keyboard-based tweaks - use new tweakables. + • Tweaking outer diameter (with fairings), instead of inner radius. + • Added fairing decoupler torque tweakable. + • Side nodes (for attaching fairings) get larger with the base size to make them more sturdy in KSP 0.23.5+ + • Tech limits are not checked in sandbox mode anymore. + • Extra payload radius is now zero by default. + • Fixed interstage adapter decoupling with fuselage fairings. + +// ================================================== +// ProceduralFairings v2.4.4 for KSP 0.23 (2014-03-31) +// ================================================== + + • Added tweakables. + • Rearranged tech tree, added 3.75m and 5m parts. + • Interstage adapter is available earlier now, but its radius is limited by aerodynamics tech. + • Launch clamps are ignored in payload scanning now. + • Payload scanning doesn't follow surface attachment to the parent part anymore. + • Improved interstage fairing shape when its top is inside payload. + • Added base cone angle limit to make fairings look better. + • Part descriptions and readme text copy edited by user Duxwing. + +// ================================================== +// ProceduralFairings v2.4.3 for KSP 0.23 (2013-12-18) +// ================================================== + + • Improved payload scanning for interstage adapter. + • Recompiled for KSP 0.23. + +// ================================================== +// ProceduralFairings v2.4.2 for KSP 0.22 (2013-10-19) +// ================================================== + + • Zero-radius payload is now used when no payload attached, so fairings will always reshape. + • Added parts to the tech tree. + • Moved fuselage shrouds to Structural tab. + • Changing adapter attachment node size with radius. + +// ================================================== +// ProceduralFairings v2.4.1 for KSP 0.21.1 (2013-08-22) +// ================================================== + + • Disabled fuel cross feed on the interstage adapter - enable at your own risk, it confuses Engineer Redux to death. + • Added stock decoupler module to the interstage adapter topmost node to help with delta-v calculations. + • Improved fairing shape for interstage adapter when fairing top is inside payload. + +// ================================================== +// ProceduralFairings v2.4 for KSP 0.21.1 (2013-08-20) +// ================================================== + + • Added procedural interstage fairing adapter with adjustable radii and height which decouples from the top part when fairings are ejected. + • Added conic fuselage. + • Fixed another inline fairing shape bug. + +// ================================================== +// ProceduralFairings v2.3 for KSP 0.21.1 (2013-08-07) +// ================================================== + + • Changed fuselage texture to distinguish it from fairings. + • You can now lock fairing shape: mouse over the side fairing/fuselage and press L. + • Reduced side nodes size for smaller base rings and 0.625m fairing base (for easier placement). + • Fixed inline fairings making a top cone when there should be just a cylinder. + +// ================================================== +// ProceduralFairings v2.2 for KSP 0.21.1 (2013-07-30) +// ================================================== + + • Added experimental egg-shaped fuselage (a side fairing without decoupler). + • Moved fairing decoupler code to separate PartModule. + • Auto-struts are now created between the top inline base and side fairings as well. NOTE: if you payload is wobbly, the sides might still wobble. + • Fixed bug with misplaced fairings on new ring bases. + +// ================================================== +// ProceduralFairings v2.1 for KSP 0.21.1 (2013-07-28) +// ================================================== + + • Added low-profile fairing bases (base rings), intended for inline fairings. All of them have 4 side fairing attachment points. + • Replaced base model with one that looks more lightweight. It has the same size etc., so it won't break your existing ships. + • You can now toggle fuel cross feed for fairing base: mouse over and press G in editor or use right-click menu in flight. + • You can now disable auto-struts between side fairings: mouse over the base and press T. + • Fixed inline fairings not connecting with the top base sometimes. + • Fixed nested inline fairings not connecting to the proper base. + • Fairing outline (blue lines) is not displayed now for inline fairings if sides are attached to any of the two bases. + +// ================================================== +// ProceduralFairings v2.0 for KSP 0.21.1 (2013-07-24) +// ================================================== + + • Inline truncated fairings are now created between two bases (one must be flipped). It won't work properly for off-center bases. If you want it off-center, tell me what for and how it should look. + • You can now change ejection force by pressing F when mouse is over the side fairing. + • Fixed rapid unplanned disassembly of side fairings when going out of time warp sometimes. + +// ================================================== +// ProceduralFairings v1.3 for KSP 0.20.2 (2013-07-14) +// ================================================== + + • Fixed ejection direction bug - it shouldn't matter how you place fairings now. + +// ================================================== +// ProceduralFairings v1.2 for KSP 0.20.2 (2013-07-12) +// ================================================== + + • Added invisible automatically placed struts between side fairings to mostly eliminate wobble. + • Replaced ejectionNoseDv with ejectionTorque so that all ejected fairings have the same motion, regardless of shape. + • Improved payload scanning for better fitting of mesh and box colliders. + • You can now adjust radius by moving the mouse over the base part while holding R (the default key, can be changed in part .cfg file). + • Fixed "recursion" bug which caused misplaced fairings to grow out of control. (It's also a foundation for future inline fairings). + • Using a (hopefully) better method to offset side fairing center of mass. + • Using proportionally smaller part of texture for 1/3 (and smaller) side fairings to reduce texture stretching. + • Renamed "capsule-shaped" fairings to "egg-shaped" to be more Kerbal. + +// ================================================== +// ProceduralFairings v1.1 for KSP 0.20.2 (2013-07-10) +// ================================================== + + • Fix for future FAR compatibility (needs fixed FAR version to actually work). + • Less rotation on eject to reduce collisions with payload and lower stages. + • Conic side fairings added, original ones are made a bit more capsule-shaped. + +// ================================================== +// ProceduralFairings v1.0 for KSP 0.20.2 (2013-07-09) +// ================================================== + + • Initial release. diff --git a/GameData/ProceduralFairings/common.cfg b/GameData/ProceduralFairings/common.cfg deleted file mode 100644 index e9527d3..0000000 --- a/GameData/ProceduralFairings/common.cfg +++ /dev/null @@ -1,158 +0,0 @@ - -PROCFAIRINGS_MINDIAMETER -{ - start = 1.00 - miniaturization = 0.400 - sandbox = 0.1 -} - -PROCFAIRINGS_MAXDIAMETER -{ - start = 1.50 - advAerodynamics = 4 - heavyAerodynamics = 12 - experimentalAerodynamics = 30 - sandbox = 50 -} - - -PROCROCKET_MINDIAMETER -{ - start = 1.00 - miniaturization = 0.400 - sandbox = 0.1 -} - -PROCROCKET_MAXDIAMETER -{ - start = 1.50 - advConstruction = 4 - metaMaterials = 12 - aerospaceTech = 30 - sandbox = 50 -} - - -//----------------------------------------------------------------------- -// Dummy parts to represent Procedural Fairings upgrades in the tech tree - -PART -{ - name = pf_tech_fairing04m - TechRequired = miniaturization - description = Allows fairings and plates to be made as small as 0.4m. - - MODEL - { - model = ProceduralFairings/baseModel - } - - title = Procedural Fairings Upgrade - module = Part - author = Starstrider42 (config), e-dog (model) - entryCost = 0 - cost = 0 - category = none - manufacturer = Keramzit Engineering -} - -PART -{ - name = pf_tech_fairing4m - TechRequired = advAerodynamics - description = Allows fairing bases up to 4m size. - - MODEL - { - model = ProceduralFairings/baseModel - } - - title = Procedural Fairings Upgrade - module = Part - author = Starstrider42 (config), e-dog (model) - entryCost = 0 - cost = 0 - category = none - manufacturer = Keramzit Engineering -} - -PART -{ - name = pf_tech_fairing12m - TechRequired = heavyAerodynamics - description = Allows fairing bases up to 12m size. - - MODEL - { - model = ProceduralFairings/baseModel - } - - title = Procedural Fairings Upgrade - module = Part - author = Starstrider42 (config), e-dog (model) - entryCost = 0 - cost = 0 - category = none - manufacturer = Keramzit Engineering -} - -PART -{ - name = pf_tech_fairing30m - TechRequired = experimentalAerodynamics - description = Allows fairing bases up to 30m size. - - MODEL - { - model = ProceduralFairings/baseModel - } - - title = Procedural Fairings Upgrade - module = Part - author = Starstrider42 (config), e-dog (model) - entryCost = 0 - cost = 0 - category = none - manufacturer = Keramzit Engineering -} - -PART -{ - name = pf_tech_rocket12m - TechRequired = metaMaterials - description = Allows thrust plates up to 12m size. - - MODEL - { - model = ProceduralFairings/thrustPlate - } - - title = Procedural Fairings Upgrade - module = Part - author = Starstrider42 (config), e-dog (model) - entryCost = 0 - cost = 0 - category = none - manufacturer = Keramzit Engineering -} - -PART -{ - name = pf_tech_rocket30m - TechRequired = aerospaceTech - description = Allows thrust plates up to 30m size. - - MODEL - { - model = ProceduralFairings/thrustPlate - } - - title = Procedural Fairings Upgrade - module = Part - author = Starstrider42 (config), e-dog (model) - entryCost = 0 - cost = 0 - category = none - manufacturer = Keramzit Engineering -} - diff --git a/GameData/ProceduralFairings/fuselage1.cfg b/GameData/ProceduralFairings/fuselage1.cfg deleted file mode 100644 index 5e2e5eb..0000000 --- a/GameData/ProceduralFairings/fuselage1.cfg +++ /dev/null @@ -1,71 +0,0 @@ -PART -{ -name = KzProcFairingFuselage1 -module = Part -author = e-dog - -MODEL -{ - model = ProceduralFairings/sideModel - texture = fairing1, ProceduralFairings/fuselage1 -} - -//scale = 1 -rescaleFactor = 1 - -node_stack_connect = 0, 0.5, 0, 0, -1, 0, 0 - -TechRequired = advConstruction -cost = 100 -entryCost = 2300 -category = Payload -subcategory = 0 -title = Egg-Shaped Fuselage Fairing -manufacturer = Keramzit Engineering -description = Lacks a decoupler. Useful for space bases, flying saucers, hiding things, and other unconventional projects. - -attachRules = 1,0,0,1,1 - -tags = procedural fairings - -mass = 0 -dragModelType = default -maximum_drag = 0.2 -minimum_drag = 0.2 -angularDrag = 2 -crashTolerance = 8 -breakingForce = 200 -breakingTorque = 200 -maxTemp = 2600 -thermalMassModifier = 2.0 -skinMassPerArea = 4.0 -skinInternalConductionMult = 0.25 -emissiveConstant = 0.8 -fuelCrossFeed = True - - -MODULE -{ - name = ProceduralFairingSide - - density=0.1 - costPerTonne=3000 - specificBreakingForce =2000 - specificBreakingTorque=2000 - - noseHeightRatio=2 - baseConeShape=0.3, 0.2, 1, 0.5 - //noseConeShape=0.5, 0, 1, 0.5 - //baseConeShape=0.3, 0.3, 0.7, 0.7 - noseConeShape=0.5, 0, 1, 0.7 - baseConeSegments=7 - noseConeSegments=11 - - mappingScale=1024, 1024 - stripMapping=992, 1024 - horMapping=10, 490, 500, 980 - vertMapping=10, 170, 694, 1014 -} - -} - diff --git a/GameData/ProceduralFairings/fuselage2.cfg b/GameData/ProceduralFairings/fuselage2.cfg deleted file mode 100644 index f5c131e..0000000 --- a/GameData/ProceduralFairings/fuselage2.cfg +++ /dev/null @@ -1,69 +0,0 @@ -PART -{ -name = KzProcFairingFuselage2 -module = Part -author = e-dog - -MODEL -{ - model = ProceduralFairings/sideModel - texture = fairing1, ProceduralFairings/fuselage1 -} - -//scale = 1 -rescaleFactor = 1 - -node_stack_connect = 0, 0.5, 0, 0, -1, 0, 0 - -TechRequired = advConstruction -cost = 100 -entryCost = 2300 -category = Payload -subcategory = 0 -title = Conic Fuselage Fairing -manufacturer = Keramzit Engineering -description = Lacks a decoupler. Useful for space bases, flying saucers, hiding things, and other unconventional projects. - -attachRules = 1,0,0,1,1 - -tags = procedural fairings - -mass = 0 -dragModelType = default -maximum_drag = 0.2 -minimum_drag = 0.2 -angularDrag = 2 -crashTolerance = 8 -breakingForce = 200 -breakingTorque = 200 -maxTemp = 2600 -thermalMassModifier = 2.0 -skinMassPerArea = 4.0 -skinInternalConductionMult = 0.25 -emissiveConstant = 0.8 -fuelCrossFeed = True - - -MODULE -{ - name = ProceduralFairingSide - - density=0.1 - costPerTonne=3000 - specificBreakingForce =2000 - specificBreakingTorque=2000 - - noseHeightRatio=2 - baseConeShape=0.3, 0.3, 0.7, 0.7 - noseConeShape=0.1, 0, 0.7, 0.667 - baseConeSegments=3 - noseConeSegments=11 - - mappingScale=1024, 1024 - stripMapping=992, 1024 - horMapping=10, 490, 500, 980 - vertMapping=10, 170, 694, 1014 -} - -} - diff --git a/GameData/ProceduralFairings/license.txt b/GameData/ProceduralFairings/license.txt new file mode 100644 index 0000000..38003bd --- /dev/null +++ b/GameData/ProceduralFairings/license.txt @@ -0,0 +1,395 @@ +Attribution 4.0 International + +======================================================================= + +Creative Commons Corporation ("Creative Commons") is not a law firm and +does not provide legal services or legal advice. Distribution of +Creative Commons public licenses does not create a lawyer-client or +other relationship. Creative Commons makes its licenses and related +information available on an "as-is" basis. Creative Commons gives no +warranties regarding its licenses, any material licensed under their +terms and conditions, or any related information. Creative Commons +disclaims all liability for damages resulting from their use to the +fullest extent possible. + +Using Creative Commons Public Licenses + +Creative Commons public licenses provide a standard set of terms and +conditions that creators and other rights holders may use to share +original works of authorship and other material subject to copyright +and certain other rights specified in the public license below. The +following considerations are for informational purposes only, are not +exhaustive, and do not form part of our licenses. + + Considerations for licensors: Our public licenses are + intended for use by those authorized to give the public + permission to use material in ways otherwise restricted by + copyright and certain other rights. Our licenses are + irrevocable. Licensors should read and understand the terms + and conditions of the license they choose before applying it. + Licensors should also secure all rights necessary before + applying our licenses so that the public can reuse the + material as expected. Licensors should clearly mark any + material not subject to the license. This includes other CC- + licensed material, or material used under an exception or + limitation to copyright. More considerations for licensors: + wiki.creativecommons.org/Considerations_for_licensors + + Considerations for the public: By using one of our public + licenses, a licensor grants the public permission to use the + licensed material under specified terms and conditions. If + the licensor's permission is not necessary for any reason--for + example, because of any applicable exception or limitation to + copyright--then that use is not regulated by the license. Our + licenses grant only permissions under copyright and certain + other rights that a licensor has authority to grant. Use of + the licensed material may still be restricted for other + reasons, including because others have copyright or other + rights in the material. A licensor may make special requests, + such as asking that all changes be marked or described. + Although not required by our licenses, you are encouraged to + respect those requests where reasonable. More_considerations + for the public: + wiki.creativecommons.org/Considerations_for_licensees + +======================================================================= + +Creative Commons Attribution 4.0 International Public License + +By exercising the Licensed Rights (defined below), You accept and agree +to be bound by the terms and conditions of this Creative Commons +Attribution 4.0 International Public License ("Public License"). To the +extent this Public License may be interpreted as a contract, You are +granted the Licensed Rights in consideration of Your acceptance of +these terms and conditions, and the Licensor grants You such rights in +consideration of benefits the Licensor receives from making the +Licensed Material available under these terms and conditions. + + +Section 1 -- Definitions. + + a. Adapted Material means material subject to Copyright and Similar + Rights that is derived from or based upon the Licensed Material + and in which the Licensed Material is translated, altered, + arranged, transformed, or otherwise modified in a manner requiring + permission under the Copyright and Similar Rights held by the + Licensor. For purposes of this Public License, where the Licensed + Material is a musical work, performance, or sound recording, + Adapted Material is always produced where the Licensed Material is + synched in timed relation with a moving image. + + b. Adapter's License means the license You apply to Your Copyright + and Similar Rights in Your contributions to Adapted Material in + accordance with the terms and conditions of this Public License. + + c. Copyright and Similar Rights means copyright and/or similar rights + closely related to copyright including, without limitation, + performance, broadcast, sound recording, and Sui Generis Database + Rights, without regard to how the rights are labeled or + categorized. For purposes of this Public License, the rights + specified in Section 2(b)(1)-(2) are not Copyright and Similar + Rights. + + d. Effective Technological Measures means those measures that, in the + absence of proper authority, may not be circumvented under laws + fulfilling obligations under Article 11 of the WIPO Copyright + Treaty adopted on December 20, 1996, and/or similar international + agreements. + + e. Exceptions and Limitations means fair use, fair dealing, and/or + any other exception or limitation to Copyright and Similar Rights + that applies to Your use of the Licensed Material. + + f. Licensed Material means the artistic or literary work, database, + or other material to which the Licensor applied this Public + License. + + g. Licensed Rights means the rights granted to You subject to the + terms and conditions of this Public License, which are limited to + all Copyright and Similar Rights that apply to Your use of the + Licensed Material and that the Licensor has authority to license. + + h. Licensor means the individual(s) or entity(ies) granting rights + under this Public License. + + i. Share means to provide material to the public by any means or + process that requires permission under the Licensed Rights, such + as reproduction, public display, public performance, distribution, + dissemination, communication, or importation, and to make material + available to the public including in ways that members of the + public may access the material from a place and at a time + individually chosen by them. + + j. Sui Generis Database Rights means rights other than copyright + resulting from Directive 96/9/EC of the European Parliament and of + the Council of 11 March 1996 on the legal protection of databases, + as amended and/or succeeded, as well as other essentially + equivalent rights anywhere in the world. + + k. You means the individual or entity exercising the Licensed Rights + under this Public License. Your has a corresponding meaning. + + +Section 2 -- Scope. + + a. License grant. + + 1. Subject to the terms and conditions of this Public License, + the Licensor hereby grants You a worldwide, royalty-free, + non-sublicensable, non-exclusive, irrevocable license to + exercise the Licensed Rights in the Licensed Material to: + + a. reproduce and Share the Licensed Material, in whole or + in part; and + + b. produce, reproduce, and Share Adapted Material. + + 2. Exceptions and Limitations. For the avoidance of doubt, where + Exceptions and Limitations apply to Your use, this Public + License does not apply, and You do not need to comply with + its terms and conditions. + + 3. Term. The term of this Public License is specified in Section + 6(a). + + 4. Media and formats; technical modifications allowed. The + Licensor authorizes You to exercise the Licensed Rights in + all media and formats whether now known or hereafter created, + and to make technical modifications necessary to do so. The + Licensor waives and/or agrees not to assert any right or + authority to forbid You from making technical modifications + necessary to exercise the Licensed Rights, including + technical modifications necessary to circumvent Effective + Technological Measures. For purposes of this Public License, + simply making modifications authorized by this Section 2(a) + (4) never produces Adapted Material. + + 5. Downstream recipients. + + a. Offer from the Licensor -- Licensed Material. Every + recipient of the Licensed Material automatically + receives an offer from the Licensor to exercise the + Licensed Rights under the terms and conditions of this + Public License. + + b. No downstream restrictions. You may not offer or impose + any additional or different terms or conditions on, or + apply any Effective Technological Measures to, the + Licensed Material if doing so restricts exercise of the + Licensed Rights by any recipient of the Licensed + Material. + + 6. No endorsement. Nothing in this Public License constitutes or + may be construed as permission to assert or imply that You + are, or that Your use of the Licensed Material is, connected + with, or sponsored, endorsed, or granted official status by, + the Licensor or others designated to receive attribution as + provided in Section 3(a)(1)(A)(i). + + b. Other rights. + + 1. Moral rights, such as the right of integrity, are not + licensed under this Public License, nor are publicity, + privacy, and/or other similar personality rights; however, to + the extent possible, the Licensor waives and/or agrees not to + assert any such rights held by the Licensor to the limited + extent necessary to allow You to exercise the Licensed + Rights, but not otherwise. + + 2. Patent and trademark rights are not licensed under this + Public License. + + 3. To the extent possible, the Licensor waives any right to + collect royalties from You for the exercise of the Licensed + Rights, whether directly or through a collecting society + under any voluntary or waivable statutory or compulsory + licensing scheme. In all other cases the Licensor expressly + reserves any right to collect such royalties. + + +Section 3 -- License Conditions. + +Your exercise of the Licensed Rights is expressly made subject to the +following conditions. + + a. Attribution. + + 1. If You Share the Licensed Material (including in modified + form), You must: + + a. retain the following if it is supplied by the Licensor + with the Licensed Material: + + i. identification of the creator(s) of the Licensed + Material and any others designated to receive + attribution, in any reasonable manner requested by + the Licensor (including by pseudonym if + designated); + + ii. a copyright notice; + + iii. a notice that refers to this Public License; + + iv. a notice that refers to the disclaimer of + warranties; + + v. a URI or hyperlink to the Licensed Material to the + extent reasonably practicable; + + b. indicate if You modified the Licensed Material and + retain an indication of any previous modifications; and + + c. indicate the Licensed Material is licensed under this + Public License, and include the text of, or the URI or + hyperlink to, this Public License. + + 2. You may satisfy the conditions in Section 3(a)(1) in any + reasonable manner based on the medium, means, and context in + which You Share the Licensed Material. For example, it may be + reasonable to satisfy the conditions by providing a URI or + hyperlink to a resource that includes the required + information. + + 3. If requested by the Licensor, You must remove any of the + information required by Section 3(a)(1)(A) to the extent + reasonably practicable. + + 4. If You Share Adapted Material You produce, the Adapter's + License You apply must not prevent recipients of the Adapted + Material from complying with this Public License. + + +Section 4 -- Sui Generis Database Rights. + +Where the Licensed Rights include Sui Generis Database Rights that +apply to Your use of the Licensed Material: + + a. for the avoidance of doubt, Section 2(a)(1) grants You the right + to extract, reuse, reproduce, and Share all or a substantial + portion of the contents of the database; + + b. if You include all or a substantial portion of the database + contents in a database in which You have Sui Generis Database + Rights, then the database in which You have Sui Generis Database + Rights (but not its individual contents) is Adapted Material; and + + c. You must comply with the conditions in Section 3(a) if You Share + all or a substantial portion of the contents of the database. + +For the avoidance of doubt, this Section 4 supplements and does not +replace Your obligations under this Public License where the Licensed +Rights include other Copyright and Similar Rights. + + +Section 5 -- Disclaimer of Warranties and Limitation of Liability. + + a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE + EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS + AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF + ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS, + IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION, + WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR + PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS, + ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT + KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT + ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU. + + b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE + TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION, + NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT, + INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES, + COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR + USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN + ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR + DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR + IN PART, THIS LIMITATION MAY NOT APPLY TO YOU. + + c. The disclaimer of warranties and limitation of liability provided + above shall be interpreted in a manner that, to the extent + possible, most closely approximates an absolute disclaimer and + waiver of all liability. + + +Section 6 -- Term and Termination. + + a. This Public License applies for the term of the Copyright and + Similar Rights licensed here. However, if You fail to comply with + this Public License, then Your rights under this Public License + terminate automatically. + + b. Where Your right to use the Licensed Material has terminated under + Section 6(a), it reinstates: + + 1. automatically as of the date the violation is cured, provided + it is cured within 30 days of Your discovery of the + violation; or + + 2. upon express reinstatement by the Licensor. + + For the avoidance of doubt, this Section 6(b) does not affect any + right the Licensor may have to seek remedies for Your violations + of this Public License. + + c. For the avoidance of doubt, the Licensor may also offer the + Licensed Material under separate terms or conditions or stop + distributing the Licensed Material at any time; however, doing so + will not terminate this Public License. + + d. Sections 1, 5, 6, 7, and 8 survive termination of this Public + License. + + +Section 7 -- Other Terms and Conditions. + + a. The Licensor shall not be bound by any additional or different + terms or conditions communicated by You unless expressly agreed. + + b. Any arrangements, understandings, or agreements regarding the + Licensed Material not stated herein are separate from and + independent of the terms and conditions of this Public License. + + +Section 8 -- Interpretation. + + a. For the avoidance of doubt, this Public License does not, and + shall not be interpreted to, reduce, limit, restrict, or impose + conditions on any use of the Licensed Material that could lawfully + be made without permission under this Public License. + + b. To the extent possible, if any provision of this Public License is + deemed unenforceable, it shall be automatically reformed to the + minimum extent necessary to make it enforceable. If the provision + cannot be reformed, it shall be severed from this Public License + without affecting the enforceability of the remaining terms and + conditions. + + c. No term or condition of this Public License will be waived and no + failure to comply consented to unless expressly agreed to by the + Licensor. + + d. Nothing in this Public License constitutes or may be interpreted + as a limitation upon, or waiver of, any privileges and immunities + that apply to the Licensor or You, including from the legal + processes of any jurisdiction or authority. + + +======================================================================= + +Creative Commons is not a party to its public +licenses. Notwithstanding, Creative Commons may elect to apply one of +its public licenses to material it publishes and in those instances +will be considered the “Licensor.” The text of the Creative Commons +public licenses is dedicated to the public domain under the CC0 Public +Domain Dedication. Except for the limited purpose of indicating that +material is shared under a Creative Commons public license or as +otherwise permitted by the Creative Commons policies published at +creativecommons.org/policies, Creative Commons does not authorize the +use of the trademark "Creative Commons" or any other trademark or logo +of Creative Commons without its prior written consent including, +without limitation, in connection with any unauthorized modifications +to any of its public licenses or any other arrangements, +understandings, or agreements concerning use of licensed material. For +the avoidance of doubt, this paragraph does not form part of the +public licenses. + +Creative Commons may be contacted at creativecommons.org. diff --git a/GameData/ProceduralFairings/plate.cfg b/GameData/ProceduralFairings/plate.cfg deleted file mode 100644 index 21df705..0000000 --- a/GameData/ProceduralFairings/plate.cfg +++ /dev/null @@ -1,97 +0,0 @@ -PART -{ -name = KzThrustPlate -module = Part -author = e-dog - -MODEL -{ - model = ProceduralFairings/thrustPlate - scale = 1, 1, 1 -} - -scale = 1 -rescaleFactor = 1 - -// definition format is Position X, Position Y, Position Z, Up X, Up Y, Up Z -node_stack_top = 0.0, 0.1, 0.0, 0.0, 1.0, 0.0, 1 -node_stack_bottom = 0.0, 0.0, 0.0, 0.0, -1.0, 0.0, 1 -node_stack_bottom01 = 0.5, 0.0, 0.0, 0.0, -1.0, 0.0, 1 -node_stack_bottom02 = 0.5, 0.0, 0.0, 0.0, -1.0, 0.0, 1 -node_stack_bottom03 = 0.5, 0.0, 0.0, 0.0, -1.0, 0.0, 1 -node_stack_bottom04 = 0.5, 0.0, 0.0, 0.0, -1.0, 0.0, 1 -node_stack_bottom05 = 0.5, 0.0, 0.0, 0.0, -1.0, 0.0, 1 -node_stack_bottom06 = 0.5, 0.0, 0.0, 0.0, -1.0, 0.0, 1 -node_stack_bottom07 = 0.5, 0.0, 0.0, 0.0, -1.0, 0.0, 1 -node_stack_bottom08 = 0.5, 0.0, 0.0, 0.0, -1.0, 0.0, 1 -node_stack_bottom09 = 0.5, 0.0, 0.0, 0.0, -1.0, 0.0, 1 -node_stack_bottom10 = 0.5, 0.0, 0.0, 0.0, -1.0, 0.0, 1 -node_stack_bottom11 = 0.5, 0.0, 0.0, 0.0, -1.0, 0.0, 1 -node_stack_bottom12 = 0.5, 0.0, 0.0, 0.0, -1.0, 0.0, 1 -node_stack_bottom13 = 0.5, 0.0, 0.0, 0.0, -1.0, 0.0, 1 -node_stack_bottom14 = 0.5, 0.0, 0.0, 0.0, -1.0, 0.0, 1 -node_stack_bottom15 = 0.5, 0.0, 0.0, 0.0, -1.0, 0.0, 1 -node_stack_bottom16 = 0.5, 0.0, 0.0, 0.0, -1.0, 0.0, 1 - -//stackSymmetry = 3 - -TechRequired = advConstruction -cost = 100 -entryCost = 4200 -category = Structural -subcategory = 0 -title = Thrust Plate Multi-Adapter -manufacturer = Keramzit Engineering -description = When quad-adapter just isn't enough, Keramzit Engineering has you covered with its wonderful Multi-adapter! Designed for bulding engine clusters, it also found its uses in multiple payload attachment and space bars. -attachRules = 1,0,1,1,0 - -// --- standard part parameters --- -mass = 0 -dragModelType = default -maximum_drag = 0.2 -minimum_drag = 0.2 -angularDrag = 2 -crashTolerance = 12 -maxTemp = 2000 -explosionPotential = 0 -NoCrossFeedNodeKey = bottom - -breakingForce = 2000 -breakingTorque = 2000 - -stageOffset = 1 -childStageOffset = 1 - -MODULE -{ - name = KzNodeNumberTweaker - nodePrefix = bottom - maxNumber = 16 - numNodes = 4 - radius = 0.625 -} - -MODULE -{ - name = KzThrustPlateResizer - size = 1.25 - costPerTonne=1000 - specificMass=0.0070, 0.0260, 0.0100, 0 - specificBreakingForce = 1536 - specificBreakingTorque = 1536 - minSizeName = PROCROCKET_MINDIAMETER - maxSizeName = PROCROCKET_MAXDIAMETER -} - -MODULE -{ - name = ModuleToggleCrossfeed - crossfeedStatus = false - toggleEditor = true - toggleFlight = true - enableText = Crossfeed: Disabled - disableText = Crossfeed: Enabled -} - -} - diff --git a/GameData/ProceduralFairings/side1.cfg b/GameData/ProceduralFairings/side1.cfg deleted file mode 100644 index a37faa7..0000000 --- a/GameData/ProceduralFairings/side1.cfg +++ /dev/null @@ -1,79 +0,0 @@ -PART -{ -name = KzProcFairingSide1 -module = Part -author = e-dog - -MODEL -{ - model = ProceduralFairings/sideModel -} - -//scale = 1 -rescaleFactor = 1 - -node_stack_connect = 0, 0.5, 0, 0, -1, 0, 0 - -TechRequired = aviation -cost = 100 -entryCost = 4600 -category = Payload -subcategory = 0 -title = Egg-Shaped Fairing -manufacturer = Keramzit Engineering -description = Made from the finest materials found in the fields around the Space Center. Egg-shaped version. - -attachRules = 1,0,0,1,1 - -tags = procedural fairings - -mass = 0 -dragModelType = default -maximum_drag = 0.1 -minimum_drag = 0.1 -angularDrag = 2 -crashTolerance = 8 -breakingForce = 200 -breakingTorque = 200 -maxTemp = 2600 -thermalMassModifier = 2.0 -skinMassPerArea = 4.0 -skinInternalConductionMult = 0.25 -emissiveConstant = 0.8 -fuelCrossFeed = False - -stagingIcon = DECOUPLER_HOR - -stageOffset = 1 -childStageOffset = 1 - -MODULE -{ - name = ProceduralFairingSide - - density=0.1 - costPerTonne=5000 - specificBreakingForce =2000 - specificBreakingTorque=2000 - - noseHeightRatio=2 - baseConeShape=0.3, 0.2, 1, 0.5 - //noseConeShape=0.5, 0, 1, 0.5 - //baseConeShape=0.3, 0.3, 0.7, 0.7 - noseConeShape=0.5, 0, 1, 0.7 - baseConeSegments=7 - noseConeSegments=11 - - mappingScale=1024, 1024 - stripMapping=992, 1024 - horMapping=10, 490, 500, 980 - vertMapping=10, 170, 694, 1014 -} - -MODULE -{ - name = ProceduralFairingDecoupler -} - -} - diff --git a/GameData/ProceduralFairings/side2.cfg b/GameData/ProceduralFairings/side2.cfg deleted file mode 100644 index 3544396..0000000 --- a/GameData/ProceduralFairings/side2.cfg +++ /dev/null @@ -1,77 +0,0 @@ -PART -{ -name = KzProcFairingSide2 -module = Part -author = e-dog - -MODEL -{ - model = ProceduralFairings/sideModel -} - -//scale = 1 -rescaleFactor = 1 - -node_stack_connect = 0, 0.5, 0, 0, -1, 0, 0 - -TechRequired = aviation -cost = 100 -entryCost = 4600 -category = Payload -subcategory = 0 -title = Conic Fairing -manufacturer = Keramzit Engineering -description = Made from the finest materials found in the fields around the Space Center. Conic version. - -attachRules = 1,0,0,1,1 - -tags = procedural fairings - -mass = 0 -dragModelType = default -maximum_drag = 0.1 -minimum_drag = 0.1 -angularDrag = 2 -crashTolerance = 8 -breakingForce = 200 -breakingTorque = 200 -maxTemp = 2600 -thermalMassModifier = 2.0 -skinMassPerArea = 4.0 -skinInternalConductionMult = 0.25 -emissiveConstant = 0.8 -fuelCrossFeed = False - -stagingIcon = DECOUPLER_HOR - -stageOffset = 1 -childStageOffset = 1 - -MODULE -{ - name = ProceduralFairingSide - - density=0.1 - costPerTonne=5000 - specificBreakingForce =2000 - specificBreakingTorque=2000 - - noseHeightRatio=2 - baseConeShape=0.3, 0.3, 0.7, 0.7 - noseConeShape=0.1, 0, 0.7, 0.667 - baseConeSegments=3 - noseConeSegments=11 - - mappingScale=1024, 1024 - stripMapping=992, 1024 - horMapping=10, 490, 500, 980 - vertMapping=10, 170, 694, 1014 -} - -MODULE -{ - name = ProceduralFairingDecoupler -} - -} - diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..38003bd --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,395 @@ +Attribution 4.0 International + +======================================================================= + +Creative Commons Corporation ("Creative Commons") is not a law firm and +does not provide legal services or legal advice. Distribution of +Creative Commons public licenses does not create a lawyer-client or +other relationship. Creative Commons makes its licenses and related +information available on an "as-is" basis. Creative Commons gives no +warranties regarding its licenses, any material licensed under their +terms and conditions, or any related information. Creative Commons +disclaims all liability for damages resulting from their use to the +fullest extent possible. + +Using Creative Commons Public Licenses + +Creative Commons public licenses provide a standard set of terms and +conditions that creators and other rights holders may use to share +original works of authorship and other material subject to copyright +and certain other rights specified in the public license below. The +following considerations are for informational purposes only, are not +exhaustive, and do not form part of our licenses. + + Considerations for licensors: Our public licenses are + intended for use by those authorized to give the public + permission to use material in ways otherwise restricted by + copyright and certain other rights. Our licenses are + irrevocable. Licensors should read and understand the terms + and conditions of the license they choose before applying it. + Licensors should also secure all rights necessary before + applying our licenses so that the public can reuse the + material as expected. Licensors should clearly mark any + material not subject to the license. This includes other CC- + licensed material, or material used under an exception or + limitation to copyright. More considerations for licensors: + wiki.creativecommons.org/Considerations_for_licensors + + Considerations for the public: By using one of our public + licenses, a licensor grants the public permission to use the + licensed material under specified terms and conditions. If + the licensor's permission is not necessary for any reason--for + example, because of any applicable exception or limitation to + copyright--then that use is not regulated by the license. Our + licenses grant only permissions under copyright and certain + other rights that a licensor has authority to grant. Use of + the licensed material may still be restricted for other + reasons, including because others have copyright or other + rights in the material. A licensor may make special requests, + such as asking that all changes be marked or described. + Although not required by our licenses, you are encouraged to + respect those requests where reasonable. More_considerations + for the public: + wiki.creativecommons.org/Considerations_for_licensees + +======================================================================= + +Creative Commons Attribution 4.0 International Public License + +By exercising the Licensed Rights (defined below), You accept and agree +to be bound by the terms and conditions of this Creative Commons +Attribution 4.0 International Public License ("Public License"). To the +extent this Public License may be interpreted as a contract, You are +granted the Licensed Rights in consideration of Your acceptance of +these terms and conditions, and the Licensor grants You such rights in +consideration of benefits the Licensor receives from making the +Licensed Material available under these terms and conditions. + + +Section 1 -- Definitions. + + a. Adapted Material means material subject to Copyright and Similar + Rights that is derived from or based upon the Licensed Material + and in which the Licensed Material is translated, altered, + arranged, transformed, or otherwise modified in a manner requiring + permission under the Copyright and Similar Rights held by the + Licensor. For purposes of this Public License, where the Licensed + Material is a musical work, performance, or sound recording, + Adapted Material is always produced where the Licensed Material is + synched in timed relation with a moving image. + + b. Adapter's License means the license You apply to Your Copyright + and Similar Rights in Your contributions to Adapted Material in + accordance with the terms and conditions of this Public License. + + c. Copyright and Similar Rights means copyright and/or similar rights + closely related to copyright including, without limitation, + performance, broadcast, sound recording, and Sui Generis Database + Rights, without regard to how the rights are labeled or + categorized. For purposes of this Public License, the rights + specified in Section 2(b)(1)-(2) are not Copyright and Similar + Rights. + + d. Effective Technological Measures means those measures that, in the + absence of proper authority, may not be circumvented under laws + fulfilling obligations under Article 11 of the WIPO Copyright + Treaty adopted on December 20, 1996, and/or similar international + agreements. + + e. Exceptions and Limitations means fair use, fair dealing, and/or + any other exception or limitation to Copyright and Similar Rights + that applies to Your use of the Licensed Material. + + f. Licensed Material means the artistic or literary work, database, + or other material to which the Licensor applied this Public + License. + + g. Licensed Rights means the rights granted to You subject to the + terms and conditions of this Public License, which are limited to + all Copyright and Similar Rights that apply to Your use of the + Licensed Material and that the Licensor has authority to license. + + h. Licensor means the individual(s) or entity(ies) granting rights + under this Public License. + + i. Share means to provide material to the public by any means or + process that requires permission under the Licensed Rights, such + as reproduction, public display, public performance, distribution, + dissemination, communication, or importation, and to make material + available to the public including in ways that members of the + public may access the material from a place and at a time + individually chosen by them. + + j. Sui Generis Database Rights means rights other than copyright + resulting from Directive 96/9/EC of the European Parliament and of + the Council of 11 March 1996 on the legal protection of databases, + as amended and/or succeeded, as well as other essentially + equivalent rights anywhere in the world. + + k. You means the individual or entity exercising the Licensed Rights + under this Public License. Your has a corresponding meaning. + + +Section 2 -- Scope. + + a. License grant. + + 1. Subject to the terms and conditions of this Public License, + the Licensor hereby grants You a worldwide, royalty-free, + non-sublicensable, non-exclusive, irrevocable license to + exercise the Licensed Rights in the Licensed Material to: + + a. reproduce and Share the Licensed Material, in whole or + in part; and + + b. produce, reproduce, and Share Adapted Material. + + 2. Exceptions and Limitations. For the avoidance of doubt, where + Exceptions and Limitations apply to Your use, this Public + License does not apply, and You do not need to comply with + its terms and conditions. + + 3. Term. The term of this Public License is specified in Section + 6(a). + + 4. Media and formats; technical modifications allowed. The + Licensor authorizes You to exercise the Licensed Rights in + all media and formats whether now known or hereafter created, + and to make technical modifications necessary to do so. The + Licensor waives and/or agrees not to assert any right or + authority to forbid You from making technical modifications + necessary to exercise the Licensed Rights, including + technical modifications necessary to circumvent Effective + Technological Measures. For purposes of this Public License, + simply making modifications authorized by this Section 2(a) + (4) never produces Adapted Material. + + 5. Downstream recipients. + + a. Offer from the Licensor -- Licensed Material. Every + recipient of the Licensed Material automatically + receives an offer from the Licensor to exercise the + Licensed Rights under the terms and conditions of this + Public License. + + b. No downstream restrictions. You may not offer or impose + any additional or different terms or conditions on, or + apply any Effective Technological Measures to, the + Licensed Material if doing so restricts exercise of the + Licensed Rights by any recipient of the Licensed + Material. + + 6. No endorsement. Nothing in this Public License constitutes or + may be construed as permission to assert or imply that You + are, or that Your use of the Licensed Material is, connected + with, or sponsored, endorsed, or granted official status by, + the Licensor or others designated to receive attribution as + provided in Section 3(a)(1)(A)(i). + + b. Other rights. + + 1. Moral rights, such as the right of integrity, are not + licensed under this Public License, nor are publicity, + privacy, and/or other similar personality rights; however, to + the extent possible, the Licensor waives and/or agrees not to + assert any such rights held by the Licensor to the limited + extent necessary to allow You to exercise the Licensed + Rights, but not otherwise. + + 2. Patent and trademark rights are not licensed under this + Public License. + + 3. To the extent possible, the Licensor waives any right to + collect royalties from You for the exercise of the Licensed + Rights, whether directly or through a collecting society + under any voluntary or waivable statutory or compulsory + licensing scheme. In all other cases the Licensor expressly + reserves any right to collect such royalties. + + +Section 3 -- License Conditions. + +Your exercise of the Licensed Rights is expressly made subject to the +following conditions. + + a. Attribution. + + 1. If You Share the Licensed Material (including in modified + form), You must: + + a. retain the following if it is supplied by the Licensor + with the Licensed Material: + + i. identification of the creator(s) of the Licensed + Material and any others designated to receive + attribution, in any reasonable manner requested by + the Licensor (including by pseudonym if + designated); + + ii. a copyright notice; + + iii. a notice that refers to this Public License; + + iv. a notice that refers to the disclaimer of + warranties; + + v. a URI or hyperlink to the Licensed Material to the + extent reasonably practicable; + + b. indicate if You modified the Licensed Material and + retain an indication of any previous modifications; and + + c. indicate the Licensed Material is licensed under this + Public License, and include the text of, or the URI or + hyperlink to, this Public License. + + 2. You may satisfy the conditions in Section 3(a)(1) in any + reasonable manner based on the medium, means, and context in + which You Share the Licensed Material. For example, it may be + reasonable to satisfy the conditions by providing a URI or + hyperlink to a resource that includes the required + information. + + 3. If requested by the Licensor, You must remove any of the + information required by Section 3(a)(1)(A) to the extent + reasonably practicable. + + 4. If You Share Adapted Material You produce, the Adapter's + License You apply must not prevent recipients of the Adapted + Material from complying with this Public License. + + +Section 4 -- Sui Generis Database Rights. + +Where the Licensed Rights include Sui Generis Database Rights that +apply to Your use of the Licensed Material: + + a. for the avoidance of doubt, Section 2(a)(1) grants You the right + to extract, reuse, reproduce, and Share all or a substantial + portion of the contents of the database; + + b. if You include all or a substantial portion of the database + contents in a database in which You have Sui Generis Database + Rights, then the database in which You have Sui Generis Database + Rights (but not its individual contents) is Adapted Material; and + + c. You must comply with the conditions in Section 3(a) if You Share + all or a substantial portion of the contents of the database. + +For the avoidance of doubt, this Section 4 supplements and does not +replace Your obligations under this Public License where the Licensed +Rights include other Copyright and Similar Rights. + + +Section 5 -- Disclaimer of Warranties and Limitation of Liability. + + a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE + EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS + AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF + ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS, + IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION, + WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR + PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS, + ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT + KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT + ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU. + + b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE + TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION, + NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT, + INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES, + COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR + USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN + ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR + DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR + IN PART, THIS LIMITATION MAY NOT APPLY TO YOU. + + c. The disclaimer of warranties and limitation of liability provided + above shall be interpreted in a manner that, to the extent + possible, most closely approximates an absolute disclaimer and + waiver of all liability. + + +Section 6 -- Term and Termination. + + a. This Public License applies for the term of the Copyright and + Similar Rights licensed here. However, if You fail to comply with + this Public License, then Your rights under this Public License + terminate automatically. + + b. Where Your right to use the Licensed Material has terminated under + Section 6(a), it reinstates: + + 1. automatically as of the date the violation is cured, provided + it is cured within 30 days of Your discovery of the + violation; or + + 2. upon express reinstatement by the Licensor. + + For the avoidance of doubt, this Section 6(b) does not affect any + right the Licensor may have to seek remedies for Your violations + of this Public License. + + c. For the avoidance of doubt, the Licensor may also offer the + Licensed Material under separate terms or conditions or stop + distributing the Licensed Material at any time; however, doing so + will not terminate this Public License. + + d. Sections 1, 5, 6, 7, and 8 survive termination of this Public + License. + + +Section 7 -- Other Terms and Conditions. + + a. The Licensor shall not be bound by any additional or different + terms or conditions communicated by You unless expressly agreed. + + b. Any arrangements, understandings, or agreements regarding the + Licensed Material not stated herein are separate from and + independent of the terms and conditions of this Public License. + + +Section 8 -- Interpretation. + + a. For the avoidance of doubt, this Public License does not, and + shall not be interpreted to, reduce, limit, restrict, or impose + conditions on any use of the Licensed Material that could lawfully + be made without permission under this Public License. + + b. To the extent possible, if any provision of this Public License is + deemed unenforceable, it shall be automatically reformed to the + minimum extent necessary to make it enforceable. If the provision + cannot be reformed, it shall be severed from this Public License + without affecting the enforceability of the remaining terms and + conditions. + + c. No term or condition of this Public License will be waived and no + failure to comply consented to unless expressly agreed to by the + Licensor. + + d. Nothing in this Public License constitutes or may be interpreted + as a limitation upon, or waiver of, any privileges and immunities + that apply to the Licensor or You, including from the legal + processes of any jurisdiction or authority. + + +======================================================================= + +Creative Commons is not a party to its public +licenses. Notwithstanding, Creative Commons may elect to apply one of +its public licenses to material it publishes and in those instances +will be considered the “Licensor.” The text of the Creative Commons +public licenses is dedicated to the public domain under the CC0 Public +Domain Dedication. Except for the limited purpose of indicating that +material is shared under a Creative Commons public license or as +otherwise permitted by the Creative Commons policies published at +creativecommons.org/policies, Creative Commons does not authorize the +use of the trademark "Creative Commons" or any other trademark or logo +of Creative Commons without its prior written consent including, +without limitation, in connection with any unauthorized modifications +to any of its public licenses or any other arrangements, +understandings, or agreements concerning use of licensed material. For +the avoidance of doubt, this paragraph does not form part of the +public licenses. + +Creative Commons may be contacted at creativecommons.org. diff --git a/Misc/addAlpha.py b/Misc/addAlpha.py deleted file mode 100644 index ed8526d..0000000 --- a/Misc/addAlpha.py +++ /dev/null @@ -1,16 +0,0 @@ -import os, fnmatch, shutil -import Image, struct - - -for root, dirnames, filenames in os.walk('.'): - for fn in fnmatch.filter(filenames, '*-rgb.png'): - filename=os.path.join(root, fn) - afn=os.path.splitext(filename)[0][:-4]+'-alpha.png' - if not os.path.exists(afn): continue - print 'processing '+filename+' + '+afn - - im=Image.open(filename) - aim=Image.open(afn) - im.putalpha(aim.convert('L')) - im.save(os.path.splitext(filename)[0][:-4]+'.png') - diff --git a/Misc/exportTex.py b/Misc/exportTex.py deleted file mode 100644 index 2f18780..0000000 --- a/Misc/exportTex.py +++ /dev/null @@ -1,23 +0,0 @@ -import os -import Image -from psd_tools import PSDImage - - -def export_tex(fn, downscale=2, ext='.png'): - print 'processing', fn - psd=PSDImage.load(fn+".psd") - im=psd.as_PIL() - wd,ht=im.size - if downscale>1: im=im.resize((wd/downscale, ht/downscale), Image.ANTIALIAS) - im.save(os.path.join(target_path, fn+ext)) - - -target_path='../unity/Assets/ProceduralFairings' -export_tex('baseTex') -export_tex('baseRingTex') -export_tex('thrustPlate1') -export_tex('thrustPlate1bump', 1) - -target_path="C:/games/KSPtest/GameData/ProceduralFairings/" -export_tex('fuselage1', 1, '.tga') - diff --git a/Misc/history.txt b/Misc/history.txt deleted file mode 100644 index b31f879..0000000 --- a/Misc/history.txt +++ /dev/null @@ -1,101 +0,0 @@ -3.02 -[*]Updated KSPAPIExtensions, should work with Procedural Parts now. -[*]A bit less restrictive tech, allowing sizes a bit larger and smaller than stock ones. -[*]Trying to avoid moving attached parts after loading design or saved game. -[*]Fixed wrong size of newly added side nodes. - -3.01 -[*]Updated KSPAPIExtensions. -[*]Added size step parameters for RSS. - -3.00 -[*]Moved files up to GameData folder (no Keramzit folder anymore). Make sure to delete old mod before installing (which is a good practice anyway). -[*]Added new resizable fairing bases with configurable number of side nodes. -[*]Old parts (bases and adapter) are deprecated. Launched vessels should be fine, but you might have trouble loading old designs in VAB/SPH in career mode. -[*]Added new part: Thrust Plate Multi-Adapter. -[*]Using KSPAPIExtensions for better tweakables. -[*]Removed old keyboard-based tweaks - use new tweakables. -[*]Tweaking outer diameter (with fairings), instead of inner radius. -[*]Added fairing decoupler torque tweakable. -[*]Side nodes (for attaching fairings) get larger with the base size to make them more sturdy in KSP 0.23.5+ -[*]Tech limits are not checked in sandbox mode anymore. -[*]Extra payload radius is now zero by default. -[*]Fixed interstage adapter decoupling with fuselage fairings. - -2.4.4 -[*]Added tweakables. -[*]Rearranged tech tree, added 3.75m and 5m parts. -[*]Interstage adapter is available earlier now, but its radius is limited by aerodynamics tech. -[*]Launch clamps are ignored in payload scanning now. -[*]Payload scanning doesn't follow surface attachment to the parent part anymore. -[*]Improved interstage fairing shape when its top is inside payload. -[*]Added base cone angle limit to make fairings look better. -[*]Part descriptions and readme text copy edited by Duxwing. - -2.4.3 -[*]Improved payload scanning for interstage adapter. -[*]Recompiled for KSP 0.23. - -2.4.2 -[*]Zero-radius payload is now used when no payload attached, so fairings will always reshape. -[*]Added parts to the tech tree. -[*]Moved fuselage shrouds to Structural tab. -[*]Changing adapter attachment node size with radius. - -2.4.1 -[*]Disabled fuel crossfeed on the interstage adapter - enable at your own risk, it confuses Engineer Redux to death. -[*]Added stock decoupler module to the interstage adapter topmost node to help with delta-v calculations. -[*]Improved fairing shape for interstage adapter when fairing top is inside payload. - -2.4 -[*]Added procedural interstage fairing adapter with adjustable radii and height which decouples from the top part when fairings are ejected. -[*]Added conic fuselage. -[*]Fixed another inline fairing shape bug. - -2.3 -[*]Changed fuselage texture to distinguish it from fairings. -[*]You can now lock fairing shape: mouse over the side fairing/fuselage and press L. -[*]Reduced side nodes size for smaller base rings and 0.625m fairing base (for easier placement). -[*]Fixed inline fairings making a top cone when there should be just a cylinder. - -2.2 -[*]Added experimental egg-shaped fuselage (a side fairing without decoupler). -[*]Moved fairing decoupler code to separate PartModule. -[*]Auto-struts are now created between the top inline base and side fairings as well. NOTE: if you payload is wobbly, the sides might still wobble. -[*]Fixed bug with misplaced fairings on new ring bases. - -2.1 -[*]Added low-profile fairing bases (base rings), intended for inline fairings. All of them have 4 side fairing attachment points. -[*]Replaced base model with one that looks more lightweight. It has the same size etc., so it won't break your existing ships. -[*]You can now toggle fuel crossfeed for fairing base: mouse over and press G in editor or use right-click menu in flight. -[*]You can now disable auto-struts between side fairings: mouse over the base and press T. -[*]Fixed inline fairings not connecting with the top base sometimes. -[*]Fixed nested inline fairings not connecting to the proper base. -[*]Fairing outline (blue lines) is not displayed now for inline fairings if sides are attached to any of the two bases. - -2.0 -[*]Inline truncated fairings are now created between two bases (one must be flipped). It won't work properly for off-center bases. If you want it off-center, tell me what for and how it should look. -[*]You can now change ejection force by pressing F when mouse is over the side fairing. -[*]Fixed rapid unplanned disassembly of side fairings when going out of time warp sometimes. - -1.3 -[*]Fixed ejection direction bug - it shouldn't matter how you place fairings now. - -1.2 -[*]Added invisible automatically placed struts between side fairings to mostly eliminate wobble. -[*]Replaced ejectionNoseDv with ejectionTorque so that all ejected fairings have the same motion, regardless of shape. -[*]Improved payload scanning for better fitting of mesh and box colliders. -[*]You can now adjust radius by moving the mouse over the base part while holding R (the default key, can be changed in part .cfg). -[*]Fixed "recursion" bug which caused misplaced fairings to grow out of control. (It's also a foundation for future inline fairings). -[*]Using a (hopefully) better method to offset side fairing center of mass. -[*]Using proportionally smaller part of texture for 1/3 (and smaller) side fairings to reduce texture stretching. -[*]Renamed "capsule-shaped" fairings to "egg-shaped" to be more Kerbal. - -1.1 -[*]Fix for future FAR compatibility (needs fixed FAR version to actually work). -[*]Less rotation on eject to reduce collisions with payload and lower stages. -[*]Conic side fairings added, original ones are made a bit more capsule-shaped. - -1.0 -[*]Initial release. - diff --git a/Misc/makezip.bat b/Misc/makezip.bat deleted file mode 100644 index 3f66bcd..0000000 --- a/Misc/makezip.bat +++ /dev/null @@ -1,7 +0,0 @@ -@echo off -set name=C:\games\KSPtest\ProcFairings_3.20.zip -del %name% -7z a %name% readme.txt -cd .. -7z a %name% GameData - diff --git a/Misc/readme.txt b/Misc/readme.txt deleted file mode 100644 index 3dc9ca0..0000000 --- a/Misc/readme.txt +++ /dev/null @@ -1,239 +0,0 @@ ---- Forum thread --- -http://forum.kerbalspaceprogram.com/showthread.php/39512 - ---- Download --- -https://github.com/e-dog/ProceduralFairings/releases - ---- License --- -http://creativecommons.org/licenses/by/3.0/ - - ---- Installation --- -Remove old version of the mod. -Copy "ProceduralFairings" folder into "Gamedata" in your KSP folder. - -If you don't need old parts (pre 3.00), remove "ProceduralFairings/deprecated" folder. -Do that if you never installed this mod before or if you're going to start a new game. Otherwise you'll get confusing obsolete parts which show in the tech tree, but not in the VAB. - --- Installation Notes -- - --If you downloaded KSP from Squad's website, then the KSP folder is where you unzipped it when you first downloaded the game --If you downloaded KSP from Steam, then right-clicking KSP in your Steam library, select "properties," switching to the "local files" tab, and pressing "browse local files" opens the game folder. --The "Source" folder is unnecessary unless you want to modify and compile plug-in code. --In the KSP main folder a "GameData" folder contains all add-ons; without any add-ons, it contains only the "Squad" sub-folder - the stock "add-on" from the developers of the game. Unzip the Keramzit folder into your Gamedata folder. - ---- Use --- - --- Tutorial: -- -http://imgur.com/a/xCF0q - --- Steps -- -1 Put a fairing base under your payload (all Procedural Fairings parts are in the Aerodynamics tab) and a decoupler if necessary. -2 Right-click the fairing base to change its size and other parameters. -3 Attached fairings automatically reshape for your payload. -4 Enabling symmetry on fairings will encapsulate your payload -5 Rearrange stages to jettison fairings at the proper stage. - --- Inline Fairings -- --Flipping another fairing base over and adding it above the payload will cause side fairings to stick to it instead of creating a nose cone, thereby creating inline fairings between two bases. --Procedural Fairings includes low-profile base rings intended for inline fairings. - --- Controls -- -Right-click parts and use tweakables. - --- Career mode -- -Maximum (and minimum) part size is limited by tech. See GameData/ProceduralFairings/common.cfg for details. - ---- Version history --- - - -3.20 -- Added the following changes by KortexM: -- Ready for KSP 1.2 (recompiled, small changes to configs) -- Added Interstage Nodes (no trusses) to Fairings and Interstage Fairing, two connections possible on each node (up & down) -- A few code cleanups (foreach mostly) -- Fixed Fuel Crossfeed (be aware that flow to/from interstage nodes is affected too!) -- Fixed ghost nodes appearing when adding a new fairing base in the VAB/SPH -- Fixed blue ghost lines (invalid fairing outline) when having multiple fairing bases in VAB/SPH -- Fixed interstage nodes positions for Interstage Adapter when resized -- Some UI fixes -- Code cleanups (de-Linqed etc.) -- All other bugs/features untouched (hopefully..) - -3.17 --Rebuilt for KSP 1.1.2. --Fixed thermal issues with FAR (patch by NathanKell). --Fixed procedural mass issues (patch by NathanKell). --Fixed auto-strut joint issue (connecting to the same rigid body). --Improved payload auto-strut performance (strut heaviest part only). - -3.16 --Updated for KSP 1.1.0. - -3.15 --Updated for KSP 1.0.4. --Merged pull request for Procedural Parts bug fix. --Adjusted thermal parameters for KSP 1.0.4 (hopefully). - -3.14 --Updated for the new FAR. --Reduced fairing shape update rate in the editor to 2 times per second. --Adjusted default fairing decoupler value to avoid "vessel changed" messages spammed by bugged KSP sliders. - -3.13 --Updated for KSP 1.0.2. --Rewrote automatic payload struts, strutting all shielded parts now. --Fixed bug when it was impossible to revert sometimes. --Fixed slowdown in the editor when tweaking fairing sides. --Fixed thrust plate mass parameters. - -3.12 --Updated for KSP 1.0. --Procedural fairings now use new stock airstream shielding and drag cube rendering. --Inline fairings now check if the top is closed by a single big part of the same vessel. Make sure that's the case or the shielding won't work. --Changed and rearranged tech limits for the new tech tree. --Converted textures to DDS. --Fixed manual shape UI issues. --Number of shielded parts is now displayed in the right-click menu for the fairing base. It might be inaccurate in the editor. --Removed old deprecated parts (non-resizable fairing bases etc). - -3.11 --Updated for KSP 0.90. --Added optional manual fairing shape controls. --Fixed tech restrictions checking in science mode (patch by Zwa333). - -3.10 --Updated KAE DLL for KSP 0.25. --Payload auto-struts by marce155. - -3.09 --Updated KAE DLL. - -3.08 --Updated KAE DLL for KSP 0.24.2. - -3.07 --Added procedural costs for PF parts, KSP 0.24.1 is required. --Added workaround for Win64 decoupler bug. --Made "removed" node markers much less visible (they are still somewhere 10km from the VAB). --Updated KAE DLL. - -3.06 --Updated for KSP 0.24. - -3.05 --Fixed collider bug introduced in 3.04. - -3.04 --Added fake parts to make tech upgrades visible in the tech tree. --Restored TechRequired for old parts to avoid issues with loading old designs in career mode. --Fixed bug that allowed to cheat tech limits in career mode. --Reduced default fairing ejection torque. - -3.03 --Added "sandbox" tech to specify minimum and maximum sizes in sandbox mode (see common.cfg). --Changed mass formula for all parts except side fairings. Generally, larger sizes are significantly lighter now. --Part mass is now displayed when you right-click the part in VAB. --Rebuilding side fairing mesh only when really needed (faster in VAB, especially with FAR). - -3.02 --Updated KSPAPIExtensions, should work with Procedural Parts now. --A bit less restrictive tech, allowing sizes a bit larger and smaller than stock ones. --Trying to avoid moving attached parts after loading design or saved game. --Fixed wrong size of newly added side nodes. - -3.01 --Updated KSPAPIExtensions. --Added size step parameters for RSS. - -3.00 --Moved files up to GameData folder (no Keramzit folder anymore). Make sure to delete old mod before installing (which is a good practice anyway). --Added new resizable fairing bases with configurable number of side nodes. --Old parts (bases and adapter) are deprecated. Launched vessels should be fine, but you might have trouble loading old designs in VAB/SPH in career mode. --Added new part: Thrust Plate Multi-Adapter. --Using KSPAPIExtensions by Swamp-Ig for better tweakables. --Removed old keyboard-based tweaks - use new tweakables. --Tweaking outer diameter (with fairings), instead of inner radius. --Added fairing decoupler torque tweakable. --Side nodes (for attaching fairings) get larger with the base size to make them more sturdy in KSP 0.23.5+ --Tech limits are not checked in sandbox mode anymore. --Extra payload radius is now zero by default. --Fixed interstage adapter decoupling with fuselage fairings. - -2.4.4 --Added tweakables. --Rearranged tech tree, added 3.75m and 5m parts. --Interstage adapter is available earlier now, but its radius is limited by aerodynamics tech. --Launch clamps are ignored in payload scanning now. --Payload scanning doesn't follow surface attachment to the parent part anymore. --Improved interstage fairing shape when its top is inside payload. --Added base cone angle limit to make fairings look better. --Part descriptions and readme text copy edited by Duxwing. - -2.4.3 --Improved payload scanning for interstage adapter. --Recompiled for KSP 0.23. - -2.4.2 --Zero-radius payload is now used when no payload attached; fairings therefore will always reshape. --Added parts to the tech tree. --Moved fuselage shrouds to Structural tab. --Changing adapter attachment node size with radius. - -2.4.1 --Disabled fuel crossfeed on the interstage adapter because enabling it confuses Engineer Redux. --So added stock decoupler module to the interstage adapter's topmost node as to aid delta-v calculations. --Improved fairing shape for interstage adapter when fairing top is inside payload. - -2.4 --Added procedural interstage fairing adapter with adjustable radii and height which decouples from the top part when fairings are ejected. --Added conic fuselage. --Fixed another inline fairing shape bug. - -2.3 --Changed fuselage texture to distinguish it from fairings. --Fairing shape can be locked: mouse over the side fairing/fuselage and press L. --Reduced side nodes size for smaller base rings and 0.625m fairing base (for easier placement). --Fixed inline fairings making a top cone when there should be just a cylinder. - -2.2 --Added experimental egg-shaped fuselage (a side fairing without a decoupler). --Moved fairing decoupler code to separate PartModule. --Auto-struts are now also created between the top inline base and side fairings. If your payload is wobbly, then the sides might wobble. --Fixed bug with misplaced fairings on new ring bases. - -2.1 --Added low-profile fairing bases (base rings), intended for inline fairings. All of them have 4 side fairing attachment points. --Replaced base model with one that looks more lightweight. It has the same size etc., so it won't break your existing ships. --Fuel crossfeed for a fairing base can be toggled by mousing over it and pressing G in the editor or using the right-click menu in flight. --Auto-struts between side fairings can now be disabled by mousing over the base and pressing T. --Fixed inline fairings' not connecting with the top base sometimes. --Fixed nested inline fairings' not connecting to the proper base. --Fairing outline (blue lines) is not displayed now for inline fairings if sides are attached to any two bases. - -2.0 --Inline truncated fairings are now created between two bases (one must be flipped). It won't work properly for off-center bases. If you want it off-center, tell me what for and how it should look. --You can now change ejection force by pressing F when mouse is over the side fairing. --Fixed rapid unplanned disassembly of side fairings when going out of time warp sometimes. - -1.3 --Fixed ejection direction bug - it shouldn't matter how you place fairings now. - -1.2 --Added invisible automatically placed struts between side fairings to mostly eliminate wobble. --Replaced ejectionNoseDv with ejectionTorque so that all ejected fairings have the same motion, regardless of shape. --Improved payload scanning for better fitting of mesh and box colliders. --You can now adjust radius by moving the mouse over the base part while holding R (the default key, can be changed in part .cfg). --Fixed "recursion" bug which caused misplaced fairings to grow out of control. (It's also a foundation for future inline fairings). --Using a (hopefully) better method to offset side fairing center of mass. --Using proportionally smaller part of texture for 1/3 (and smaller) side fairings to reduce texture stretching. --Renamed "capsule-shaped" fairings to "egg-shaped" to be more Kerbal. - -1.1 --Fix for future FAR compatibility (requires fixed FAR version) --So lessened rotation on eject as to reduce collisions with payload and lower stages. --Conic side fairings added. Original ones are made more capsule-shaped. - -1.0 --Initial release. - diff --git a/Misc/texTemplate.py b/Misc/texTemplate.py deleted file mode 100644 index 7668b96..0000000 --- a/Misc/texTemplate.py +++ /dev/null @@ -1,25 +0,0 @@ -import Image, ImageDraw - -im=Image.new('RGB', (1024, 1024), '#800') - -draw=ImageDraw.Draw(im) - -# strip -draw.rectangle((1024-32, 0, 1024, 1024), fill='#ca2') - -# outer -draw.polygon((10, 10+320, 10+480, 10+320, 10+480/2, 10), fill='#88c') -draw.rectangle((10, 1024-10-160, 10+480, 1024-10), fill='#8cc') -draw.rectangle((10, 10+320, 10+480, 1024-10-160), fill='#ccc') - -# inner -draw.polygon((20+480, 10+320, 20+480*2, 10+320, 20+480*3/2, 10), fill='#448') -# draw.rectangle((20+480, 10, 20+480*2, 10+320), fill='#448') -draw.rectangle((20+480, 1024-10-160, 20+480*2, 1024-10), fill='#488') -draw.rectangle((20+480, 10+320, 20+480*2, 1024-10-160), fill='#888') - -del draw - -# im.show() -im.save('template.png') - diff --git a/Misc/toDDS.py b/Misc/toDDS.py deleted file mode 100644 index 5194be7..0000000 --- a/Misc/toDDS.py +++ /dev/null @@ -1,15 +0,0 @@ -from PIL import Image, ImageOps -import os, glob - -for fn in glob.glob("../GameData/ProceduralFairings/*.tga"): - if "_NRM" in fn: - im=Image.open(fn) - r, g, b = im.split() - # g=ImageOps.invert(g) - im=Image.merge('RGBA', (g, g, g, r)) - im.save("tmp.png") - os.system("crunch -yflip -dxt5 -file tmp.png -out "+fn[:-4]+".dds") - os.remove("tmp.png") - else: - os.system("crunch -yflip -dxt5 -file "+fn+" -out "+fn[:-4]+".dds") - diff --git a/README.md b/README.md index 8bbdb5f..9acce60 100644 --- a/README.md +++ b/README.md @@ -1,58 +1,39 @@ # Procedural Fairings -Procedural Fairings mod for Kerbal Space Program. +![][PF:shield-version] +![][PF:shield-license] -[Forum thread](http://forum.kerbalspaceprogram.com/index.php?/topic/36371-110-procedural-fairings-316-april-20/) +**Procedural Fairings** is a mod for **Kerbal Space Program (KSP)** that creates fairings that can automatically reshape for any attached payload. -[Download](https://github.com/e-dog/ProceduralFairings/releases) +**[Original KSP forum thread][PF:original-forum-link]** -[License](http://creativecommons.org/licenses/by/3.0/) +## Installation +Extract the contents of the ProceduralFairings.zip and then drop the ProceduralFairings folder into the GameData folder. **Note:** if you are upgrading then make sure that any previous Procedural Fairings installation has been completely removed beforehand. -## Installation -Remove old version of the mod. +### Usage -Copy ProceduralFairings into "Gamedata" in your KSP folder. +1. Put a fairing base under your payload (all Procedural Fairings parts are in the Payload tab) and a decoupler if necessary. +2. Attached fairings automatically reshape for your payload. +3. Enabling symmetry on fairings will encapsulate your payload +4. Rearrange stages to jettison fairings at the proper stage. -## Installation Notes +### Notes -If you downloaded KSP from Squad's website, then the KSP folder is where you unzipped it when you first downloaded the game +* All part tweakables are accessible via the right-click part action windows (diameter, length, shape etc). +* Flipping another fairing base over and adding it above the payload will cause any side fairings to stick to it instead of creating a nose cone, thereby creating inline fairings between two bases. +* Procedural Fairings includes low-profile base rings intended for inline fairings. Separate interstage bases are also included. +* Procedural Fairings are fully integrated with the career mode. The minimum and maximum part sizes are limited by the available tech (see "ProceduralFairings_Settings.cfg" for more details). -If you downloaded KSP from Steam, then right-clicking KSP in your Steam library, select "properties," switching to the "local files" tab, and pressing "browse local files" opens the game folder. +## License -In the KSP main folder a "GameData" folder contains all add-ons; without any add-ons, it contains only the "Squad" and "NASAMission" sub-folders - the stock "add-ons" from the developers of the game. Unzip the ProceduralFairings folder into your Gamedata folder. +Procedural Fairings is licensed under a **Creative Commons Attribution 4.0 (CC-BY 4.0)** license. -## Tutorial -[Pictures](http://imgur.com/a/xCF0q) +You should have received a copy of the license along with this work. If not, visit the **[official Creative Commons web page][PF:cc-license-link]**. -### Steps -1. Put a fairing base under your payload (all Procedural Fairings parts are in the Aerodynamics tab) and a decoupler if necessary. -2. Attached fairings automatically reshape for your payload. -3. Enabling symmetry on fairings will encapsulate your payload -4. Rearrange stages to jettison fairings at the proper stage. +*** -### Inline Fairings -- Flipping another fairing base over and adding it above the payload will cause side fairings to stick to it instead of creating a nose cone, thereby creating inline fairings between two bases. -- Procedural Fairings includes low-profile base rings intended for inline fairings. - -### Controls -Right-click parts and use tweakables. - -## Career mode -Maximum (and minimum) part size is limited by tech. See GameData/ProceduralFairings/common.cfg for details. - -## Version history -**3.00** -- First release on GitHub. -- Moved files up to GameData folder (no Keramzit folder anymore). Make sure to delete old mod before installing (which is a good practice anyway). -- Added new resizable fairing bases with configurable number of side nodes. -- Old parts (bases and adapter) are deprecated. Launched vessels should be fine, but you might have trouble loading old designs in VAB/SPH in career mode. -- Added new part: Thrust Plate Multi-Adapter. -- Using KSPAPIExtensions by Swamp-Ig for better tweakables. -- Removed old keyboard-based tweaks - use new tweakables. -- Tweaking outer diameter (with fairings), instead of inner radius. -- Added fairing decoupler torque tweakable. -- Side nodes (for attaching fairings) get larger with the base size to make them more sturdy in KSP 0.23.5+ -- Tech limits are not checked in sandbox mode anymore. -- Extra payload radius is now zero by default. -- Fixed interstage adapter decoupling with fuselage fairings. +[PF:cc-license-link]: https://creativecommons.org/licenses/by/4.0/legalcode +[PF:original-forum-link]: http://forum.kerbalspaceprogram.com/index.php?showtopic=36371 +[PF:shield-license]: https://img.shields.io/badge/License-CC--BY%204.0-green.svg +[PF:shield-version]: https://img.shields.io/badge/KSP%20Version-1.3.1.1891-red.svg diff --git a/Source/FairingBase.cs b/Source/FairingBase.cs deleted file mode 100644 index 0b23105..0000000 --- a/Source/FairingBase.cs +++ /dev/null @@ -1,868 +0,0 @@ -// Procedural Fairings plug-in by Alexey Volynskov -// Licensed under CC BY 3.0 terms: http://creativecommons.org/licenses/by/3.0/ -using System; -using System.Collections.Generic; -using System.Text; -using UnityEngine; - - -namespace Keramzit -{ - - - //ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ// - - - public class ProceduralFairingBase : PartModule - { - [KSPField] public float outlineWidth = 0.05f; - [KSPField] public int outlineSlices = 12; - [KSPField] public Vector4 outlineColor = new Vector4(0, 0, 0.2f, 1); - [KSPField] public float verticalStep = 0.1f; - [KSPField] public float baseSize = 1.25f; - - [KSPField(isPersistant = true, guiActiveEditor = true, guiName = "Extra radius")] - [UI_FloatRange(minValue = -1, maxValue = 2, stepIncrement = 0.01f)] - public float extraRadius = 0.0f; - - [KSPField] public int circleSegments = 24; - - [KSPField] public float sideThickness = 0.05f; - - //[KSPField(isPersistant=true, guiActive=true, guiActiveEditor=true, guiName="Fuel crossfeed")] - //public bool fuelCrossFeed=false; - - - [KSPField(isPersistant = true, guiActiveEditor = true, guiName = "Fairing Auto-struts")] - [UI_Toggle(disabledText = "Off", enabledText = "On")] - public bool autoStrutSides = true; - - - [KSPField(isPersistant = true, guiActiveEditor = true, guiName = "Fairing Auto-shape")] - [UI_Toggle(disabledText = "Off", enabledText = "On")] - public bool autoShape = true; - - [KSPField(isPersistant = true, guiActiveEditor = true, guiName = "Max. size", guiFormat = "S4", guiUnits = "m")] - [UI_FloatEdit(sigFigs = 3, unit = "m", minValue = 0.1f, maxValue = 5, incrementLarge = 1.25f, incrementSmall = 0.125f, incrementSlide = 0.001f)] - public float manualMaxSize = 0.625f; - - [KSPField(isPersistant = true, guiActiveEditor = true, guiName = "Cyl. start", guiFormat = "S4", guiUnits = "m")] - [UI_FloatEdit(sigFigs = 3, unit = "m", minValue = 0, maxValue = 50, incrementLarge = 1.0f, incrementSmall = 0.1f, incrementSlide = 0.001f)] - public float manualCylStart = 0; - - [KSPField(isPersistant = true, guiActiveEditor = true, guiName = "Cyl. end", guiFormat = "S4", guiUnits = "m")] - [UI_FloatEdit(sigFigs = 3, unit = "m", minValue = 0, maxValue = 50, incrementLarge = 1.0f, incrementSmall = 0.1f, incrementSlide = 0.001f)] - public float manualCylEnd = 1; - - private bool limitsSet = false; - - [KSPField] public float diameterStepLarge = 1.25f; - [KSPField] public float diameterStepSmall = 0.125f; - - [KSPField] public float heightStepLarge = 1.0f; - [KSPField] public float heightStepSmall = 0.1f; - - public float updateDelay = 0; - public bool needShapeUpdate = true; - Part topBasePart = null; - - private float lastManualMaxSize, lastManualCylStart, lastManualCylEnd; - - - LineRenderer line = null; - - List outline = new List(); - - - List joints = new List(); - - - //[KSPEvent(name = "ToggleCrossFeed", active=true, guiActive=true, guiActiveEditor=true, - // guiActiveUnfocused=false, guiName="Toggle crossfeed")] - //public void ToggleCrossFeed() - //{ - // part.fuelCrossFeed = fuelCrossFeed = !fuelCrossFeed; - //} - - - - public override string GetInfo() - { - string s = "Attach side fairings and they'll be shaped for your attached payload.\n" + - "Remember to add a decoupler if you need one."; - - return s; - } - - - public override void OnStart(StartState state) - { - limitsSet = false; - - if (!HighLogic.LoadedSceneIsEditor && !HighLogic.LoadedSceneIsFlight) return; - - PFUtils.hideDragStuff(part); - - GameEvents.onEditorShipModified.Add(new EventData.OnEvent(onEditorVesselModified)); - - if (HighLogic.LoadedSceneIsEditor) { - // line=makeLineRenderer("payload outline", Color.red, outlineWidth); //== - if (line) line.transform.Rotate(0, 90, 0); - - - DestroyAllLineRenderers(); - destroyOutline(); /* redundant? */ - - for (int i = 0; i < outlineSlices; ++i) { - var r = makeLineRenderer("fairing outline", outlineColor, outlineWidth); - outline.Add(r); - r.transform.Rotate(0, i * 360f / outlineSlices, 0); - } - - ShowHideInterstageNodes(); - - updateDelay = 0.1f; - needShapeUpdate = true; - } - else { - topBasePart = null; - var adapter = part.GetComponent(); - if (adapter) - topBasePart = adapter.getTopPart(); - else { - var scan = scanPayload(); - if (scan.targets.Count > 0) topBasePart = scan.targets[0]; - } - } - - - SetUIChangedCallBacks(); - - // part.fuelCrossFeed=fuelCrossFeed; - - } - - private void SetUIChangedCallBacks() - { - ((UI_FloatEdit)Fields["manualMaxSize"].uiControlEditor).onFieldChanged += UIChanged; - ((UI_FloatEdit)Fields["manualCylStart"].uiControlEditor).onFieldChanged += UIChanged; - ((UI_FloatEdit)Fields["manualCylEnd"].uiControlEditor).onFieldChanged += UIChanged; - ((UI_Toggle)Fields["autoShape"].uiControlEditor).onFieldChanged += UIChanged; - } - - private bool uiChanged_SomeFields = true; - private void UIChanged(BaseField bf, object obj) - { - uiChanged_SomeFields = true; - } - - - - void onEditorVesselModified(ShipConstruct ship) - { - needShapeUpdate = true; - ShowHideInterstageNodes(); - } - - - public void ShowHideInterstageNodes() - { - var nnt = part.GetComponent(); - if (nnt) { - //if (nnt.showInterstageNodes == nnt.oldShowInterstageState) - // return; - - //nnt.oldShowInterstageState = nnt.showInterstageNodes; - - var nodes = part.FindAttachNodes("interstage"); - if (nodes == null) - return; - - // hide all unused interstage nodes - // just move it away in x direction - if (nnt.showInterstageNodes == false) { - for (int i = 0; i < nodes.Length; i++) { - var node = nodes[i]; - if (node.attachedPart == null) - node.position.x = 10000; - } - } - else { - for (int i = 0; i < nodes.Length; i++) { - var node = nodes[i]; - if (node.attachedPart == null) - node.position.x = 0; - } - } - } - - } - - - - public void removeJoints() - { - while (joints.Count > 0) { - int i = joints.Count - 1; - var joint = joints[i]; joints.RemoveAt(i); - UnityEngine.Object.Destroy(joint); - } - } - - - public void OnPartPack() - { - removeJoints(); - } - - - ConfigurableJoint addStrut(Part p, Part pp) - { - if (p == pp) return null; - - var rb = pp.Rigidbody; - if (rb == null || rb == p.Rigidbody) return null; - - var joint = p.gameObject.AddComponent(); - joint.xMotion = ConfigurableJointMotion.Locked; - joint.yMotion = ConfigurableJointMotion.Locked; - joint.zMotion = ConfigurableJointMotion.Locked; - joint.angularXMotion = ConfigurableJointMotion.Locked; - joint.angularYMotion = ConfigurableJointMotion.Locked; - joint.angularZMotion = ConfigurableJointMotion.Locked; - joint.projectionDistance = 0.1f; - joint.projectionAngle = 5; - joint.breakForce = p.breakingForce; - joint.breakTorque = p.breakingTorque; - joint.connectedBody = rb; - - joints.Add(joint); - return joint; - } - - - // public void OnPartUnpack() - // { - // if (!HighLogic.LoadedSceneIsEditor && autoStrutSides) - // { - // // strut side fairings together - // var attached=part.FindAttachNodes("connect"); - // for (int i=0; i(); - // // if (sf==null) continue; - - // var pp=attached[i>0 ? i-1 : attached.Length-1].attachedPart; - // if (pp==null) continue; - - // addStrut(p, pp); - - // if (topBasePart!=null) addStrut(p, topBasePart); - // } - // } - // } - - - IEnumerator createAutoStruts(List shieldedParts) - { - while (!FlightGlobals.ready || vessel.packed || !vessel.loaded) { - yield return new WaitForFixedUpdate(); - } - - var nnt = part.GetComponent(); - var attached = part.FindAttachNodes("connect"); - for (int i = 0; i < nnt.numNodes; ++i) { - var p = attached[i].attachedPart; - if (p == null || p.Rigidbody == null) continue; - - // var sf=p.GetComponent(); - // if (sf==null) continue; - - var pp = attached[i > 0 ? i - 1 : nnt.numNodes - 1].attachedPart; - if (pp == null) continue; - - addStrut(p, pp); - - if (topBasePart != null) addStrut(p, topBasePart); - } - } - - - public void onShieldingDisabled(List shieldedParts) - { - removeJoints(); - } - - - public void onShieldingEnabled(List shieldedParts) - { - if (!HighLogic.LoadedSceneIsFlight) return; - if (autoStrutSides) StartCoroutine(createAutoStruts(shieldedParts)); - } - - - public virtual void FixedUpdate() - { - if (!limitsSet && PFUtils.canCheckTech()) { - limitsSet = true; - float minSize = PFUtils.getTechMinValue("PROCFAIRINGS_MINDIAMETER", 0.25f); - float maxSize = PFUtils.getTechMaxValue("PROCFAIRINGS_MAXDIAMETER", 30); - - PFUtils.setFieldRange(Fields["manualMaxSize"], minSize, maxSize * 2); - - ((UI_FloatEdit)Fields["manualMaxSize"].uiControlEditor).incrementLarge = diameterStepLarge; - ((UI_FloatEdit)Fields["manualMaxSize"].uiControlEditor).incrementSmall = diameterStepSmall; - - ((UI_FloatEdit)Fields["manualCylStart"].uiControlEditor).incrementLarge = heightStepLarge; - ((UI_FloatEdit)Fields["manualCylStart"].uiControlEditor).incrementSmall = heightStepSmall; - ((UI_FloatEdit)Fields["manualCylEnd"].uiControlEditor).incrementLarge = heightStepLarge; - ((UI_FloatEdit)Fields["manualCylEnd"].uiControlEditor).incrementSmall = heightStepSmall; - } - - if (!part.packed && topBasePart != null) { - var adapter = part.GetComponent(); - if (adapter) { - topBasePart = adapter.getTopPart(); - if (topBasePart == null) removeJoints(); - } - } - } - - - LineRenderer makeLineRenderer(string name, Color color, float wd) - { - var o = new GameObject(name); - o.transform.parent = part.transform; - o.transform.localPosition = Vector3.zero; - o.transform.localRotation = Quaternion.identity; - var r = o.AddComponent(); - r.useWorldSpace = false; - r.material = new Material(Shader.Find("Particles/Additive")); - r.SetColors(color, color); - r.SetWidth(wd, wd); - r.SetVertexCount(0); - return r; - } - - - void destroyOutline() - { - for (int i = 0; i < outline.Count; i++) { - UnityEngine.GameObject.Destroy(outline[i].gameObject); - } - outline.Clear(); - - } - - - /// - /// Fix for the blue ghost lines showing invalid outline when cloning or symmetry-placing fairing bases in the VAB. - /// Find any already assigned (copied) linerenderers and delete them. - /// - void DestroyAllLineRenderers() - { - - LineRenderer[] lr = UnityEngine.GameObject.FindObjectsOfType(); - if (lr != null) { - for (int i = 0; i < lr.Length; i++) { - - Transform _transform = lr[i].transform; - if (!(_transform == null)) { - Transform _parent = _transform.parent; - if (!(_parent == null)) { - GameObject _gameObject = _parent.gameObject; - if (_gameObject) { - if ((_gameObject.Equals(this) ? true : _gameObject.Equals(base.gameObject))) { - GameObjectExtension.DestroyGameObject(lr[i].gameObject); - } - } - } - } - } - } - } - - - public void OnDestroy() - { - GameEvents.onEditorShipModified.Remove(new EventData.OnEvent(onEditorVesselModified)); - - if (line) { - UnityEngine.GameObject.Destroy(line.gameObject); - line = null; - } - - DestroyAllLineRenderers(); - destroyOutline(); - } - - - public void Update() - { - if (HighLogic.LoadedSceneIsEditor) { - if (uiChanged_SomeFields == true) { - uiChanged_SomeFields = false; - - if (lastManualMaxSize != manualMaxSize) needShapeUpdate = true; - if (lastManualCylStart != manualCylStart) needShapeUpdate = true; - if (lastManualCylEnd != manualCylEnd) needShapeUpdate = true; - - lastManualMaxSize = manualMaxSize; - lastManualCylStart = manualCylStart; - lastManualCylEnd = manualCylEnd; - - bool old = Fields["manualMaxSize"].guiActiveEditor; - Fields["manualMaxSize"].guiActiveEditor = !autoShape; - Fields["manualCylStart"].guiActiveEditor = !autoShape; - Fields["manualCylEnd"].guiActiveEditor = !autoShape; - - PFUtils.refreshPartWindow(); - - } - - if (updateDelay > 0) - updateDelay -= Time.deltaTime; - else - if (needShapeUpdate) { - needShapeUpdate = false; - recalcShape(); - updateDelay = 0.5f; - } - - - } - } - - - static public Vector3[] buildFairingShape(float baseRad, float maxRad, - float cylStart, float cylEnd, float noseHeightRatio, - Vector4 baseConeShape, Vector4 noseConeShape, - int baseConeSegments, int noseConeSegments, - Vector4 vertMapping, float mappingScaleY) - { - float baseConeRad = maxRad - baseRad; - float tip = maxRad * noseHeightRatio; - - var baseSlope = new BezierSlope(baseConeShape); - var noseSlope = new BezierSlope(noseConeShape); - - float baseV0 = vertMapping.x / mappingScaleY; - float baseV1 = vertMapping.y / mappingScaleY; - float noseV0 = vertMapping.z / mappingScaleY; - float noseV1 = vertMapping.w / mappingScaleY; - - var shape = new Vector3[1 + (cylStart == 0 ? 0 : baseConeSegments) + 1 + noseConeSegments]; - int vi = 0; - - if (cylStart != 0) { - for (int i = 0; i <= baseConeSegments; ++i, ++vi) { - float t = (float)i / baseConeSegments; - var p = baseSlope.interp(t); - shape[vi] = new Vector3(p.x * baseConeRad + baseRad, p.y * cylStart, - Mathf.Lerp(baseV0, baseV1, t)); - } - } - else - shape[vi++] = new Vector3(baseRad, 0, baseV1); - - for (int i = 0; i <= noseConeSegments; ++i, ++vi) { - float t = (float)i / noseConeSegments; - var p = noseSlope.interp(1 - t); - shape[vi] = new Vector3(p.x * maxRad, (1 - p.y) * tip + cylEnd, - Mathf.Lerp(noseV0, noseV1, t)); - } - - return shape; - } - - - static public Vector3[] buildInlineFairingShape(float baseRad, float maxRad, float topRad, - float cylStart, float cylEnd, float top, - Vector4 baseConeShape, - int baseConeSegments, - Vector4 vertMapping, float mappingScaleY) - { - float baseConeRad = maxRad - baseRad; - float topConeRad = maxRad - topRad; - - var baseSlope = new BezierSlope(baseConeShape); - - float baseV0 = vertMapping.x / mappingScaleY; - float baseV1 = vertMapping.y / mappingScaleY; - float noseV0 = vertMapping.z / mappingScaleY; - - var shape = new Vector3[2 + (cylStart == 0 ? 0 : baseConeSegments + 1) + (cylEnd == top ? 0 : baseConeSegments + 1)]; - int vi = 0; - - if (cylStart != 0) { - for (int i = 0; i <= baseConeSegments; ++i, ++vi) { - float t = (float)i / baseConeSegments; - var p = baseSlope.interp(t); - shape[vi] = new Vector3(p.x * baseConeRad + baseRad, p.y * cylStart, - Mathf.Lerp(baseV0, baseV1, t)); - } - } - - shape[vi++] = new Vector3(maxRad, cylStart, baseV1); - shape[vi++] = new Vector3(maxRad, cylEnd, noseV0); - - if (cylEnd != top) { - for (int i = 0; i <= baseConeSegments; ++i, ++vi) { - float t = (float)i / baseConeSegments; - var p = baseSlope.interp(1 - t); - shape[vi] = new Vector3(p.x * topConeRad + topRad, Mathf.Lerp(top, cylEnd, p.y), - Mathf.Lerp(baseV1, baseV0, t)); - } - } - - return shape; - } - - - - PayloadScan scanPayload() - { - // scan payload and build its profile - var scan = new PayloadScan(part, verticalStep, extraRadius); - - - AttachNode node = part.FindAttachNode("top"); - if (node != null) { - scan.ofs = node.position.y; - if (node.attachedPart != null) scan.addPart(node.attachedPart, part); - } - - - AttachNode[] nodes = part.FindAttachNodes("interstage"); - if (nodes != null) { - for (int j = 0; j < nodes.Length; j++) { - node = nodes[j]; - - if (node != null) { - if (node.attachedPart != null) scan.addPart(node.attachedPart, part); - } - } - } - - for (int i = 0; i < scan.payload.Count; ++i) { - var cp = scan.payload[i]; - - // add connected payload parts - scan.addPart(cp.parent, cp); - for (int j = 0; j < cp.children.Count; j++) - scan.addPart(cp.children[j], cp); - - // scan part colliders - var colls = cp.FindModelComponents(); - for (int j = 0; j < colls.Count; j++) { - var coll = colls[j]; - if (coll.tag != "Untagged") continue; // skip ladders etc. - scan.addPayload(coll); - } - } - - - return scan; - } - - AttachNode HasNodeComponent(AttachNode[] nodes) - { - if (nodes != null) { - for (int i = 0; i < nodes.Length; i++) { - var part = nodes[i].attachedPart; - if (part == null) - continue; - var comp = part.GetComponent(); - if (comp != null) { - return nodes[i]; - } - } - } - - return null; - } - - void recalcShape() - { - var scan = scanPayload(); - - // check for reversed base for inline fairings - float topY = 0; - float topRad = 0; - AttachNode topSideNode = null; - bool isInline = false; - - var adapter = part.GetComponent(); - - if (adapter) { - isInline = true; - topY = adapter.height + adapter.extraHeight; - if (topY < scan.ofs) topY = scan.ofs; - topRad = adapter.topRadius; - - // if (scan.profile.Count<=0) scan.profile.Add(extraRadius); - } - else if (scan.targets.Count > 0) { - isInline = true; - var topBase = scan.targets[0].GetComponent(); - topY = scan.w2l.MultiplyPoint3x4(topBase.part.transform.position).y; - if (topY < scan.ofs) topY = scan.ofs; - - topSideNode = HasNodeComponent(topBase.part.FindAttachNodes("connect")); - - topRad = topBase.baseSize * 0.5f; - } - - // no payload case - if (scan.profile.Count <= 0) scan.profile.Add(extraRadius); - - // fill profile outline (for debugging) - if (line) { - line.SetVertexCount(scan.profile.Count * 2 + 2); - - float prevRad = 0; - int hi = 0; - for (int i = 0; i < scan.profile.Count; i++) { - var r = scan.profile[i]; - line.SetPosition(hi * 2, new Vector3(prevRad, hi * verticalStep + scan.ofs, 0)); - line.SetPosition(hi * 2 + 1, new Vector3(r, hi * verticalStep + scan.ofs, 0)); - hi++; prevRad = r; - } - - line.SetPosition(hi * 2, new Vector3(prevRad, hi * verticalStep + scan.ofs, 0)); - line.SetPosition(hi * 2 + 1, new Vector3(0, hi * verticalStep + scan.ofs, 0)); - } - - // check attached side parts and get params - var attached = part.FindAttachNodes("connect"); - // get number of available nodes from numbertweaker - var nnt = part.GetComponent(); - int numSideParts = nnt.numNodes; - //int numSideParts=attached.Length; - - - var sideNode = HasNodeComponent(attached); - - float noseHeightRatio = 2; - float minBaseConeAngle = 20; - Vector4 baseConeShape = new Vector4(0.5f, 0, 1, 0.5f); - Vector4 noseConeShape = new Vector4(0.5f, 0, 1, 0.5f); - int baseConeSegments = 1; - int noseConeSegments = 1; - Vector2 mappingScale = new Vector2(1024, 1024); - Vector2 stripMapping = new Vector2(992, 1024); - Vector4 horMapping = new Vector4(0, 480, 512, 992); - Vector4 vertMapping = new Vector4(0, 160, 704, 1024); - - if (sideNode != null) { - var sf = sideNode.attachedPart.GetComponent(); - noseHeightRatio = sf.noseHeightRatio; - minBaseConeAngle = sf.minBaseConeAngle; - baseConeShape = sf.baseConeShape; - noseConeShape = sf.noseConeShape; - baseConeSegments = sf.baseConeSegments; - noseConeSegments = sf.noseConeSegments; - mappingScale = sf.mappingScale; - stripMapping = sf.stripMapping; - horMapping = sf.horMapping; - vertMapping = sf.vertMapping; - } - - // compute fairing shape - float baseRad = baseSize * 0.5f; - float minBaseConeTan = Mathf.Tan(minBaseConeAngle * Mathf.Deg2Rad); - - float cylStart = 0; - float maxRad; - int profTop = scan.profile.Count; - - if (isInline) { - profTop = Mathf.CeilToInt((topY - scan.ofs) / verticalStep); - if (profTop > scan.profile.Count) profTop = scan.profile.Count; - - maxRad = 0; - for (int i = 0; i < profTop; ++i) - maxRad = Mathf.Max(maxRad, scan.profile[i]); - - maxRad = Mathf.Max(maxRad, topRad); - } - else - maxRad = PFUtils.GetMaxValueFromList(scan.profile); - - if (maxRad > baseRad) { - // try to fit base cone as high as possible - cylStart = scan.ofs; - for (int i = 1; i < scan.profile.Count; ++i) { - float y = i * verticalStep + scan.ofs; - float r0 = baseRad; - float k = (maxRad - r0) / y; - if (k < minBaseConeTan) break; - - bool ok = true; - float r = r0 + k * scan.ofs; - for (int j = 0; j < i; ++j, r += k * verticalStep) - if (scan.profile[j] > r) { ok = false; break; } - - if (!ok) break; - cylStart = y; - } - } - else - maxRad = baseRad; // no base cone, just cylinder and nose - - float cylEnd = scan.profile.Count * verticalStep + scan.ofs; - - if (isInline) { - float r0 = topRad; - if (profTop > 0 && profTop < scan.profile.Count) { - r0 = Mathf.Max(r0, scan.profile[profTop - 1]); - if (profTop - 2 >= 0) r0 = Mathf.Max(r0, scan.profile[profTop - 2]); - } - - if (maxRad > r0) { - if (cylEnd > topY) cylEnd = topY - verticalStep; - - // try to fit top cone as low as possible - for (int i = profTop - 1; i >= 0; --i) { - float y = i * verticalStep + scan.ofs; - float k = (maxRad - r0) / (y - topY); - - bool ok = true; - float r = maxRad + k * verticalStep; - for (int j = i; j < profTop; ++j, r += k * verticalStep) { - if (r < r0) r = r0; - if (scan.profile[j] > r) { ok = false; break; } - } - - if (!ok) break; - - cylEnd = y; - } - } - else - cylEnd = topY; - } - else { - // try to fit nose cone as low as possible - for (int i = scan.profile.Count - 1; i >= 0; --i) { - float s = verticalStep / noseHeightRatio; - - bool ok = true; - float r = maxRad - s; - for (int j = i; j < scan.profile.Count; ++j, r -= s) - if (scan.profile[j] > r) { ok = false; break; } - - if (!ok) break; - - float y = i * verticalStep + scan.ofs; - cylEnd = y; - } - } - - if (autoShape) { - manualMaxSize = maxRad * 2; - manualCylStart = cylStart; - manualCylEnd = cylEnd; - } - else { - maxRad = manualMaxSize * 0.5f; - cylStart = manualCylStart; - cylEnd = manualCylEnd; - } - - if (cylStart > cylEnd) cylStart = cylEnd; - - // build fairing shape line - Vector3[] shape; - - if (isInline) - shape = buildInlineFairingShape(baseRad, maxRad, topRad, cylStart, cylEnd, topY, - baseConeShape, baseConeSegments, - vertMapping, mappingScale.y); - else - shape = buildFairingShape(baseRad, maxRad, cylStart, cylEnd, noseHeightRatio, - baseConeShape, noseConeShape, baseConeSegments, noseConeSegments, - vertMapping, mappingScale.y); - - if (sideNode == null && topSideNode == null) { - // no side parts - fill fairing outlines - for (int j = 0; j < outline.Count; j++) { - var lr = outline[j]; - lr.SetVertexCount(shape.Length); - for (int i = 0; i < shape.Length; ++i) - lr.SetPosition(i, new Vector3(shape[i].x, shape[i].y)); - - - } - } - else { - for (int j = 0; j < outline.Count; j++) { - var lr = outline[j]; - lr.SetVertexCount(0); - } - } - - // rebuild side parts - int numSegs = circleSegments / numSideParts; - if (numSegs < 2) numSegs = 2; - - for (int i = 0; i < attached.Length; i++) { - var sn = attached[i]; - var sp = sn.attachedPart; - if (!sp) continue; - var sf = sp.GetComponent(); - if (!sf) continue; - - if (sf.shapeLock) continue; - - var mf = sp.FindModelComponent("model"); - if (!mf) { Debug.LogError("[ProceduralFairingBase] no model in side fairing", sp); continue; } - - var nodePos = sn.position; - - mf.transform.position = part.transform.position; - mf.transform.rotation = part.transform.rotation; - float ra = Mathf.Atan2(-nodePos.z, nodePos.x) * Mathf.Rad2Deg; - mf.transform.Rotate(0, ra, 0); - - if (sf.meshPos == mf.transform.localPosition - && sf.meshRot == mf.transform.localRotation - && sf.numSegs == numSegs - && sf.numSideParts == numSideParts - && sf.baseRad == baseRad - && sf.maxRad == maxRad - && sf.cylStart == cylStart - && sf.cylEnd == cylEnd - && sf.topRad == topRad - && sf.inlineHeight == topY - && sf.sideThickness == sideThickness) - continue; - - sf.meshPos = mf.transform.localPosition; - sf.meshRot = mf.transform.localRotation; - sf.numSegs = numSegs; - sf.numSideParts = numSideParts; - sf.baseRad = baseRad; - sf.maxRad = maxRad; - sf.cylStart = cylStart; - sf.cylEnd = cylEnd; - sf.topRad = topRad; - sf.inlineHeight = topY; - sf.sideThickness = sideThickness; - sf.rebuildMesh(); - } - - var shielding = part.GetComponent(); - if (shielding) shielding.reset(); - } - } - - - //ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ// - - -} // namespace - diff --git a/Source/FairingDecoupler.cs b/Source/FairingDecoupler.cs deleted file mode 100644 index c13ed8c..0000000 --- a/Source/FairingDecoupler.cs +++ /dev/null @@ -1,197 +0,0 @@ -// Procedural Fairings plug-in by Alexey Volynskov -// Licensed under CC BY 3.0 terms: http://creativecommons.org/licenses/by/3.0/ -using System; -using System.Collections.Generic; -using System.Text; -using UnityEngine; -using KSP.UI.Screens; - - -namespace Keramzit -{ - - - //ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ// - - - public class ProceduralFairingDecoupler : PartModule - { - [KSPField] public float ejectionDv = 15; - [KSPField] public float ejectionTorque = 10; - [KSPField] public float ejectionLowDv = 0; - [KSPField] public float ejectionLowTorque = 0; - - [KSPField(isPersistant = true, guiActiveEditor = true, guiName = "Ejection power")] - [UI_FloatRange(minValue = 0, maxValue = 1, stepIncrement = 0.01f)] - public float ejectionPower = 0.32f; - - [KSPField(isPersistant = true, guiActiveEditor = true, guiName = "Ejection torque")] - [UI_FloatRange(minValue = 0, maxValue = 1, stepIncrement = 0.01f)] - public float torqueAmount = 0.01f; - - [KSPField] public string ejectSoundUrl = "Squad/Sounds/sound_decoupler_fire"; - public FXGroup ejectFx; - - [KSPField] public string transformName = "nose_collider"; - [KSPField] public Vector3 forceVector = Vector3.right; - [KSPField] public Vector3 torqueVector = -Vector3.forward; - - [KSPField(isPersistant = true)] bool decoupled = false; - bool didForce = false; - - - public override void OnStart(StartState state) - { - if (state == StartState.None) return; - - ejectFx.audio = part.gameObject.AddComponent(); - ejectFx.audio.volume = GameSettings.SHIP_VOLUME; - ejectFx.audio.rolloffMode = AudioRolloffMode.Logarithmic; - // ejectFx.audio.panLevel=1; - ejectFx.audio.maxDistance = 100; - ejectFx.audio.loop = false; - ejectFx.audio.playOnAwake = false; - - if (GameDatabase.Instance.ExistsAudioClip(ejectSoundUrl)) - ejectFx.audio.clip = GameDatabase.Instance.GetAudioClip(ejectSoundUrl); - else - Debug.LogError("[ProceduralFairingSide] can't find sound: " + ejectSoundUrl, this); - - //GameEvents.onPartJointBreak.Add(OnPartJointBreak); - } - - - public override void OnLoad(ConfigNode cfg) - { - base.OnLoad(cfg); - didForce = decoupled; - } - - public void OnDestroy() - { - //GameEvents.onPartJointBreak.Remove(OnPartJointBreak); - } - - //void OnPartJointBreak(PartJoint joint, float data) - //{ - // Part p = joint.GetComponent(); - // bool isstaging = false; - - // if (p == this.part) - // { - // var mod = this.part.GetComponent(); - // if (mod != null) - // { - // isstaging = mod.StagingEnabled(); - // if (isstaging == true) - // { - // mod.ToggleStaging(); - // mod.ToggleStaging(); - // } - - // isstaging = mod.StagingEnabled(); - // } - // } - - //} - - public void FixedUpdate() - { - if (decoupled) { - if (part.parent) { - var pfa = part.parent.GetComponent(); - for (int i = 0; i < part.parent.children.Count; i++) { - var p = part.parent.children[i]; - - // check if top node allows for decoupling when fairing is gone - if (pfa) { - if (!pfa.topNodeDecouplesWhenFairingsGone) { - var isFairing = p.GetComponent(); - if (!isFairing) - continue; - } - } - - var joints = p.GetComponents(); - for (int j = 0; j < joints.Length; j++) { - var joint = joints[j]; - if (joint != null && (joint.GetComponent() == part.Rigidbody || joint.connectedBody == part.Rigidbody)) - Destroy(joint); - } - } - - part.decouple(0); - - ejectFx.audio.Play(); - } - else if (!didForce) { - var tr = part.FindModelTransform(transformName); - if (tr) { - part.Rigidbody.AddForce(tr.TransformDirection(forceVector) - * Mathf.Lerp(ejectionLowDv, ejectionDv, ejectionPower), - ForceMode.VelocityChange); - part.Rigidbody.AddTorque(tr.TransformDirection(torqueVector) - * Mathf.Lerp(ejectionLowTorque, ejectionTorque, torqueAmount), - ForceMode.VelocityChange); - } - else - Debug.LogError("[ProceduralFairingDecoupler] no '" + transformName + "' transform in part", part); - - didForce = true; - decoupled = false; - } - } - } - - - [KSPEvent(name = "Jettison", active = true, guiActive = true, guiActiveUnfocused = false, guiName = "Jettison")] - public void Jettison() - { - decoupled = true; - - // if (part.parent) - // { - // foreach (var p in part.parent.children) - // foreach (var joint in p.GetComponents()) - // if (joint!=null && (joint.rigidbody==part.Rigidbody || joint.connectedBody==part.Rigidbody)) - // Destroy(joint); - - // part.decouple(0); - - // var tr=part.FindModelTransform(transformName); - // if (tr) - // { - // part.Rigidbody.AddForce(tr.TransformDirection(forceVector) - // *Mathf.Lerp(ejectionLowDv, ejectionDv, ejectionPower), - // ForceMode.VelocityChange); - // part.Rigidbody.AddTorque(tr.TransformDirection(torqueVector) - // *Mathf.Lerp(ejectionLowTorque, ejectionTorque, torqueAmount), - // ForceMode.VelocityChange); - // } - // else - // Debug.LogError("[ProceduralFairingDecoupler] no '"+transformName+"' transform in part", part); - - // ejectFx.audio.Play(); - // } - } - - - public override void OnActive() - { - Jettison(); - } - - - [KSPAction("Jettison", actionGroup = KSPActionGroup.None)] - public void ActionJettison(KSPActionParam param) - { - Jettison(); - } - } - - - //ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ// - - -} // namespace - diff --git a/Source/FairingShielding.cs b/Source/FairingShielding.cs deleted file mode 100644 index 977aba4..0000000 --- a/Source/FairingShielding.cs +++ /dev/null @@ -1,341 +0,0 @@ -// Procedural Fairings plug-in by Alexey Volynskov -// Licensed under CC BY 3.0 terms: http://creativecommons.org/licenses/by/3.0/ -using System; -using System.Collections.Generic; -using System.Text; -using UnityEngine; - - -namespace Keramzit -{ - - - //ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ// - - - public class KzFairingBaseShielding : PartModule, IAirstreamShield - { - List shieldedParts; - - ProceduralFairingSide sideFairing; - float boundCylY0, boundCylY1, boundCylRad; - Vector3 lookupCenter; - float lookupRad; - Vector3[] shape; - - [KSPField(isPersistant = false, guiActive = true, guiActiveEditor = true, guiName = "Parts shielded")] - public int numShieldedDisplay; - - bool needReset = false; - - - public bool ClosedAndLocked() { return true; } - public Vessel GetVessel() { return vessel; } - public Part GetPart() { return part; } - - - public override void OnAwake() - { - shieldedParts = new List(); - } - - - public override void OnStart(StartState state) - { - if (!HighLogic.LoadedSceneIsEditor && !HighLogic.LoadedSceneIsFlight) return; - - reset(); - - // GameEvents.onEditorShipModified.Add(new EventData.OnEvent(onEditorVesselModified)); - GameEvents.onVesselWasModified.Add(new EventData.OnEvent(onVesselModified)); - GameEvents.onVesselGoOffRails.Add(new EventData.OnEvent(onVesselUnpack)); - GameEvents.onPartDie.Add(new EventData.OnEvent(OnPartDestroyed)); - } - - - void OnDestroy() - { - // GameEvents.onEditorShipModified.Remove(new EventData.OnEvent(onEditorVesselModified)); - GameEvents.onVesselWasModified.Remove(new EventData.OnEvent(onVesselModified)); - GameEvents.onVesselGoOffRails.Remove(new EventData.OnEvent(onVesselUnpack)); - GameEvents.onPartDie.Remove(new EventData.OnEvent(OnPartDestroyed)); - } - - - public void FixedUpdate() - { - if (needReset) { - needReset = false; - getFairingParams(); - bool shield = (HighLogic.LoadedSceneIsEditor || (HighLogic.LoadedSceneIsFlight && !vessel.packed)); - if (shield) enableShielding(); - } - } - - - public void reset() - { - needReset = true; - } - - - AttachNode[] getFairingParams() - { - var nnt = part.GetComponent(); - - // check attached side parts and get params - var attached = part.FindAttachNodes("connect"); - ProceduralFairingSide sf = null; - - //for (int i = 0; i < attached.Length; ++i) - for (int i = 0; i < nnt.numNodes; ++i) { - var n = attached[i]; - if (!n.attachedPart) { sf = null; break; } - sf = n.attachedPart.GetComponent(); - if (!sf) break; - } - - sideFairing = sf; - - if (!sf) { - shape = null; - boundCylY0 = boundCylY1 = boundCylRad = 0; - lookupCenter = Vector3.zero; - lookupRad = 0; - return null; - } - - // get shape polyline - if (sf.inlineHeight <= 0) - shape = ProceduralFairingBase.buildFairingShape( - sf.baseRad, sf.maxRad, sf.cylStart, sf.cylEnd, sf.noseHeightRatio, - sf.baseConeShape, sf.noseConeShape, sf.baseConeSegments, sf.noseConeSegments, - sf.vertMapping, sf.mappingScale.y); - else - shape = ProceduralFairingBase.buildInlineFairingShape( - sf.baseRad, sf.maxRad, sf.topRad, sf.cylStart, sf.cylEnd, sf.inlineHeight, - sf.baseConeShape, sf.baseConeSegments, - sf.vertMapping, sf.mappingScale.y); - - // offset shape by thickness - for (int i = 0; i < shape.Length; ++i) { - if (i == 0 || i == shape.Length - 1) - shape[i] += new Vector3(sf.sideThickness, 0, 0); - else { - Vector2 n = shape[i + 1] - shape[i - 1]; - n.Set(n.y, -n.x); - n.Normalize(); - shape[i] += new Vector3(n.x, n.y, 0) * sf.sideThickness; - } - } - - // compute bounds - float y0, y1, mr; - y0 = y1 = shape[0].y; - mr = shape[0].x; - - for (int i = 0; i < shape.Length; ++i) { - var p = shape[i]; - if (p.x > mr) mr = p.x; - if (p.y < y0) y0 = p.y; - else if (p.y > y1) y1 = p.y; - } - - boundCylY0 = y0; - boundCylY1 = y1; - boundCylRad = mr; - - lookupCenter = new Vector3(0, (y0 + y1) * 0.5f, 0); - lookupRad = new Vector2(mr, (y1 - y0) * 0.5f).magnitude; - - return attached; - } - - - void enableShielding() - { - // print("enableShielding()"); - disableShielding(); - - var attached = getFairingParams(); - if (!sideFairing) return; - - // get all parts in range - var parts = new List(); - var colliders = Physics.OverlapSphere(part.transform.TransformPoint(lookupCenter), lookupRad, 1); - for (int i = colliders.Length - 1; i >= 0; --i) { - var p = colliders[i].gameObject.GetComponentUpwards(); - if (p != null) parts.AddUnique(p); - } - // print("got "+parts.Count+" nearby parts"); - - // filter parts - float sizeSqr = lookupRad * lookupRad * 4; - float boundCylRadSq = boundCylRad * boundCylRad; - - bool isInline = (sideFairing.inlineHeight > 0); - bool topClosed = false; - - Matrix4x4 w2l = Matrix4x4.identity, w2lb = Matrix4x4.identity; - Bounds topBounds = default(Bounds); - - if (isInline) { - w2l = part.transform.worldToLocalMatrix; - w2lb = w2l; - for (int i = 0; i < 3; ++i) - for (int j = 0; j < 3; ++j) - w2lb[i, j] = Mathf.Abs(w2lb[i, j]); - - topBounds = new Bounds(new Vector3(0, boundCylY1, 0), new Vector3(sideFairing.topRad * 2, sideFairing.sideThickness, sideFairing.topRad * 2)); - } - - for (int pi = 0; pi < parts.Count; ++pi) { - var pt = parts[pi]; - - // check special cases - if (pt == part) { shieldedParts.Add(pt); continue; } - - bool isSide = false; - for (int i = 0; i < attached.Length; ++i) if (attached[i].attachedPart == pt) { isSide = true; break; } - if (isSide) continue; - - // print("checking part "+pt.partName+" "+pt.partInfo.title); - - // check if the top is closed in the inline case - var bounds = pt.GetRendererBounds(); - var box = PartGeometryUtil.MergeBounds(bounds, pt.transform); - - if (isInline && !topClosed && pt.vessel == vessel) { - var wb = box; wb.Expand(sideFairing.sideThickness * 4); - var b = new Bounds(w2l.MultiplyPoint3x4(wb.center), w2lb.MultiplyVector(wb.size)); - if (b.Contains(topBounds.min) && b.Contains(topBounds.max)) topClosed = true; - } - - // check if too big to fit - // if (box.size.sqrMagnitude>sizeSqr) continue; - - // check if the centroid is within fairing bounds - var c = part.transform.InverseTransformPoint(PartGeometryUtil.FindBoundsCentroid(bounds, null)); - - float y = c.y; - if (y < boundCylY0 || y > boundCylY1) continue; - - float xsq = new Vector2(c.x, c.z).sqrMagnitude; - if (xsq > boundCylRadSq) continue; - - // accurate centroid check - float x = Mathf.Sqrt(xsq); - bool inside = false; - - for (int i = 1; i < shape.Length; ++i) { - var p0 = shape[i - 1]; - var p1 = shape[i]; - if (p0.y > p1.y) { var p = p0; p0 = p1; p1 = p; } - - if (y < p0.y || y > p1.y) continue; - - float dy = p1.y - p0.y, r; - if (dy <= 1e-6f) r = (p0.x + p1.x) * 0.5f; - else r = (p1.x - p0.x) * (y - p0.y) / dy + p0.x; - - if (x > r) continue; - - inside = true; - break; - } - - if (!inside) continue; - - shieldedParts.Add(pt); - // print("shielded "+pt.partName); - } - - if (isInline && !topClosed) { disableShielding(); return; } - - // add shielding - for (int i = 0; i < shieldedParts.Count; ++i) - shieldedParts[i].AddShield(this); - - numShieldedDisplay = shieldedParts.Count; - - var fbase = part.GetComponent(); - if (fbase != null) fbase.onShieldingEnabled(shieldedParts); - } - - - void disableShielding() - { - if (shieldedParts != null) { - var fbase = part.GetComponent(); - if (fbase != null) fbase.onShieldingDisabled(shieldedParts); - - for (int i = shieldedParts.Count - 1; i >= 0; --i) - if (shieldedParts[i] != null) shieldedParts[i].RemoveShield(this); - shieldedParts.Clear(); - } - - numShieldedDisplay = 0; - } - - - void onEditorVesselModified(ShipConstruct ship) - { - // print("onEditorVesselModified"); - // reset(); - } - - - void onVesselModified(Vessel v) - { - // print("onVesselModified"); - if (v != vessel) { - var dp = v.vesselTransform.position - part.transform.TransformPoint(lookupCenter); - if (dp.sqrMagnitude > lookupRad * lookupRad) return; - } - enableShielding(); - } - - - void onVesselUnpack(Vessel v) - { - // print("onVesselUnpack"); - if (v == vessel) enableShielding(); - } - - - void onVesselPack(Vessel v) - { - // print("onVesselPack"); - if (v == vessel) disableShielding(); - } - - - void OnPartDestroyed(Part p) - { - var nnt = part.GetComponent(); - // print("OnPartDestroyed"); - if (p == part) { disableShielding(); return; } - - // check for side fairing parts - var attached = part.FindAttachNodes("connect"); - //for (int i = 0; i < attached.Length; ++i) - for (int i = 0; i < nnt.numNodes; ++i) - if (p == attached[i].attachedPart) { disableShielding(); return; } - - // check for top parts in inline/adapter case - if (p.vessel == vessel && sideFairing && sideFairing.inlineHeight > 0) - enableShielding(); - } - - public void OnPartPack() - { - disableShielding(); - } - } - - - //ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ// - - -} // namespace - diff --git a/Source/FairingSide.cs b/Source/FairingSide.cs deleted file mode 100644 index a723d21..0000000 --- a/Source/FairingSide.cs +++ /dev/null @@ -1,512 +0,0 @@ -// Procedural Fairings plug-in by Alexey Volynskov -// Licensed under CC BY 3.0 terms: http://creativecommons.org/licenses/by/3.0/ -using System; -using System.Collections.Generic; -using System.Text; -using UnityEngine; - - -namespace Keramzit -{ - - - //ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ// - - - public class ProceduralFairingSide : PartModule, IPartCostModifier, IPartMassModifier - { - [KSPField] public float noseHeightRatio = 2; - [KSPField] public float minBaseConeAngle = 20; - [KSPField] public Vector4 baseConeShape = new Vector4(0.5f, 0, 1, 0.5f); - [KSPField] public Vector4 noseConeShape = new Vector4(0.5f, 0, 1, 0.5f); - [KSPField] public int baseConeSegments = 5; - [KSPField] public int noseConeSegments = 7; - - [KSPField] public Vector2 mappingScale = new Vector2(1024, 1024); - [KSPField] public Vector2 stripMapping = new Vector2(992, 1024); - [KSPField] public Vector4 horMapping = new Vector4(0, 480, 512, 992); - [KSPField] public Vector4 vertMapping = new Vector4(0, 160, 704, 1024); - - [KSPField] public float density = 0.2f; - [KSPField] public float costPerTonne = 2000; - [KSPField] public float specificBreakingForce = 2000; - [KSPField] public float specificBreakingTorque = 2000; - - [KSPField(isPersistant = true)] public int numSegs = 12; - [KSPField(isPersistant = true)] public int numSideParts = 2; - [KSPField(isPersistant = true)] public float baseRad = 0; - [KSPField(isPersistant = true)] public float maxRad = 1.50f; - [KSPField(isPersistant = true)] public float cylStart = 0.5f; - [KSPField(isPersistant = true)] public float cylEnd = 2.5f; - [KSPField(isPersistant = true)] public float topRad = 0; - [KSPField(isPersistant = true)] public float inlineHeight = 0; - [KSPField(isPersistant = true)] public float sideThickness = 0.05f; - [KSPField(isPersistant = true)] public Vector3 meshPos = Vector3.zero; - [KSPField(isPersistant = true)] public Quaternion meshRot = Quaternion.identity; - - [KSPField(isPersistant = true, guiActiveEditor = true, guiName = "Shape")] - [UI_Toggle(disabledText = "Unlocked", enabledText = "Locked")] - public bool shapeLock = false; - - [KSPField(isPersistant = false, guiActive = false, guiActiveEditor = true, guiName = "Mass")] - public string massDisplay; - - [KSPField(isPersistant = false, guiActive = false, guiActiveEditor = true, guiName = "Cost")] - public string costDisplay; - - - public ModifierChangeWhen GetModuleCostChangeWhen() { return ModifierChangeWhen.FIXED; } - public ModifierChangeWhen GetModuleMassChangeWhen() { return ModifierChangeWhen.FIXED; } - - public float GetModuleCost(float defcost, ModifierStagingSituation sit) - { - return totalMass * costPerTonne - defcost; - } - - public float GetModuleMass(float defmass, ModifierStagingSituation sit) - { - return totalMass - defmass; - } - - - public override string GetInfo() - { - string s = "Attach to Keramzit's fairing base to reshape."; - - return s; - } - - - public void Start() - { - part.mass = totalMass; - } - public override void OnStart(StartState state) - { - if (state == StartState.None) return; - - if (state != StartState.Editor || shapeLock) rebuildMesh(); - part.mass = totalMass; - } - - - public override void OnLoad(ConfigNode cfg) - { - base.OnLoad(cfg); - if (HighLogic.LoadedSceneIsEditor || HighLogic.LoadedSceneIsFlight) rebuildMesh(); - } - - - public void updateNodeSize() - { - var node = part.FindAttachNode("connect"); - if (node != null) { - int s = Mathf.RoundToInt(baseRad * 2 / 1.25f) - 1; - if (s < 0) s = 0; - node.size = s; - } - } - - - public void FixedUpdate() - { - if (HighLogic.LoadedSceneIsEditor) { - int nsym = part.symmetryCounterparts.Count; - if (nsym == 0) { - massDisplay = PFUtils.formatMass(totalMass); - costDisplay = PFUtils.formatCost(part.partInfo.cost + GetModuleCost(part.partInfo.cost, ModifierStagingSituation.CURRENT)); - } - else if (nsym == 1) { - massDisplay = PFUtils.formatMass(totalMass * 2) + " (both)"; - costDisplay = PFUtils.formatCost((part.partInfo.cost + GetModuleCost(part.partInfo.cost, ModifierStagingSituation.CURRENT)) * 2) + " (both)"; - } - else { - massDisplay = PFUtils.formatMass(totalMass * (nsym + 1)) + " (all " + (nsym + 1) + ")"; - costDisplay = PFUtils.formatCost((part.partInfo.cost + GetModuleCost(part.partInfo.cost, ModifierStagingSituation.CURRENT)) * (nsym + 1)) + " (all " + (nsym + 1) + ")"; - } - } - } - - public float totalMass; - public void rebuildMesh() - { - var mf = part.FindModelComponent("model"); - if (!mf) { Debug.LogError("[ProceduralFairingSide] no model in side fairing", part); return; } - - Mesh m = mf.mesh; - if (!m) { Debug.LogError("[ProceduralFairingSide] no mesh in side fairing", part); return; } - - mf.transform.localPosition = meshPos; - mf.transform.localRotation = meshRot; - - updateNodeSize(); - - // build fairing shape line - float tip = maxRad * noseHeightRatio; - - Vector3[] shape; - if (inlineHeight <= 0) - shape = ProceduralFairingBase.buildFairingShape( - baseRad, maxRad, cylStart, cylEnd, noseHeightRatio, - baseConeShape, noseConeShape, baseConeSegments, noseConeSegments, - vertMapping, mappingScale.y); - else - shape = ProceduralFairingBase.buildInlineFairingShape( - baseRad, maxRad, topRad, cylStart, cylEnd, inlineHeight, - baseConeShape, baseConeSegments, - vertMapping, mappingScale.y); - - // set up params - var dirs = new Vector3[numSegs + 1]; - for (int i = 0; i <= numSegs; ++i) { - float a = Mathf.PI * 2 * (i - numSegs * 0.5f) / (numSideParts * numSegs); - dirs[i] = new Vector3(Mathf.Cos(a), 0, Mathf.Sin(a)); - } - - float segOMappingScale = (horMapping.y - horMapping.x) / (mappingScale.x * numSegs); - float segIMappingScale = (horMapping.w - horMapping.z) / (mappingScale.x * numSegs); - float segOMappingOfs = horMapping.x / mappingScale.x; - float segIMappingOfs = horMapping.z / mappingScale.x; - - if (numSideParts > 2) { - segOMappingOfs += segOMappingScale * numSegs * (0.5f - 1f / numSideParts); - segOMappingScale *= 2f / numSideParts; - segIMappingOfs += segIMappingScale * numSegs * (0.5f - 1f / numSideParts); - segIMappingScale *= 2f / numSideParts; - } - - float stripU0 = stripMapping.x / mappingScale.x; - float stripU1 = stripMapping.y / mappingScale.x; - - float ringSegLen = baseRad * Mathf.PI * 2 / (numSegs * numSideParts); - float topRingSegLen = topRad * Mathf.PI * 2 / (numSegs * numSideParts); - - float collWidth = maxRad * Mathf.PI * 2 / (numSideParts * 3); - - int numMainVerts = (numSegs + 1) * (shape.Length - 1) + 1; - int numMainFaces = numSegs * ((shape.Length - 2) * 2 + 1); - - int numSideVerts = shape.Length * 2; - int numSideFaces = (shape.Length - 1) * 2; - - int numRingVerts = (numSegs + 1) * 2; - int numRingFaces = numSegs * 2; - - if (inlineHeight > 0) { - numMainVerts = (numSegs + 1) * shape.Length; - numMainFaces = numSegs * (shape.Length - 1) * 2; - } - - int totalVerts = numMainVerts * 2 + numSideVerts * 2 + numRingVerts; - int totalFaces = numMainFaces * 2 + numSideFaces * 2 + numRingFaces; - - if (inlineHeight > 0) { - totalVerts += numRingVerts; - totalFaces += numRingFaces; - } - - var p = shape[shape.Length - 1]; - float topY = p.y, topV = p.z; - - float collCenter = (cylStart + cylEnd) / 2, collHeight = cylEnd - cylStart; - if (collHeight <= 0) collHeight = Mathf.Min(topY - cylEnd, cylStart) / 2; - - // compute area - double area = 0; - for (int i = 1; i < shape.Length; ++i) - area += (shape[i - 1].x + shape[i].x) * (shape[i].y - shape[i - 1].y) * Mathf.PI / numSideParts; - - // set params based on volume - float volume = (float)(area * sideThickness); - part.mass = totalMass = volume * density; - part.breakingForce = part.mass * specificBreakingForce; - part.breakingTorque = part.mass * specificBreakingTorque; - - var offset = new Vector3(maxRad * 0.7f, topY * 0.5f, 0); - part.CoMOffset = part.transform.InverseTransformPoint(mf.transform.TransformPoint(offset)); - - // remove old colliders - var colls = part.FindModelComponents(); - for (int i = 0; i < colls.Count; i++) { - var c = colls[i]; - // if (c.transform.parent!=mf.transform || c.transform.parent!=mf.transform.parent) continue; - UnityEngine.Object.Destroy(c.gameObject); - } - - // add new colliders - for (int i = -1; i <= 1; ++i) { - var obj = new GameObject("collider"); - obj.transform.parent = mf.transform; - obj.transform.localPosition = Vector3.zero; - obj.transform.localRotation = Quaternion.AngleAxis(90f * i / numSideParts, Vector3.up); - var coll = obj.AddComponent(); - coll.center = new Vector3(maxRad + sideThickness * 0.5f, collCenter, 0); - coll.size = new Vector3(sideThickness, collHeight, collWidth); - } - - { - // nose collider - float r = maxRad * 0.2f; - var obj = new GameObject("nose_collider"); - obj.transform.parent = mf.transform; - obj.transform.localPosition = new Vector3(r, cylEnd + tip - r * 1.2f, 0); - obj.transform.localRotation = Quaternion.identity; - - if (inlineHeight > 0) { - r = sideThickness * 0.5f; - obj.transform.localPosition = new Vector3(maxRad + r, collCenter, 0); - } - - var coll = obj.AddComponent(); - coll.center = Vector3.zero; - coll.radius = r; - } - - // build mesh - m.Clear(); - - var verts = new Vector3[totalVerts]; - var uv = new Vector2[totalVerts]; - var norm = new Vector3[totalVerts]; - var tang = new Vector4[totalVerts]; - - if (inlineHeight <= 0) { - // tip vertex - verts[numMainVerts - 1].Set(0, topY + sideThickness, 0); // outside - verts[numMainVerts * 2 - 1].Set(0, topY, 0); // inside - uv[numMainVerts - 1].Set(segOMappingScale * 0.5f * numSegs + segOMappingOfs, topV); - uv[numMainVerts * 2 - 1].Set(segIMappingScale * 0.5f * numSegs + segIMappingOfs, topV); - norm[numMainVerts - 1] = Vector3.up; - norm[numMainVerts * 2 - 1] = -Vector3.up; - // tang[numMainVerts -1]= Vector3.forward; - // tang[numMainVerts*2-1]=-Vector3.forward; - tang[numMainVerts - 1] = Vector3.zero; - tang[numMainVerts * 2 - 1] = Vector3.zero; - } - - // main vertices - float noseV0 = vertMapping.z / mappingScale.y; - float noseV1 = vertMapping.w / mappingScale.y; - float noseVScale = 1f / (noseV1 - noseV0); - float oCenter = (horMapping.x + horMapping.y) / (mappingScale.x * 2); - float iCenter = (horMapping.z + horMapping.w) / (mappingScale.x * 2); - - int vi = 0; - for (int i = 0; i < shape.Length - (inlineHeight <= 0 ? 1 : 0); ++i) { - p = shape[i]; - - Vector2 n; - if (i == 0) n = shape[1] - shape[0]; - else if (i == shape.Length - 1) n = shape[i] - shape[i - 1]; - else n = shape[i + 1] - shape[i - 1]; - n.Set(n.y, -n.x); - n.Normalize(); - - for (int j = 0; j <= numSegs; ++j, ++vi) { - var d = dirs[j]; - var dp = d * p.x + Vector3.up * p.y; - var dn = d * n.x + Vector3.up * n.y; - if (i == 0 || i == shape.Length - 1) verts[vi] = dp + d * sideThickness; - else verts[vi] = dp + dn * sideThickness; - verts[vi + numMainVerts] = dp; - - float v = (p.z - noseV0) * noseVScale; - float uo = j * segOMappingScale + segOMappingOfs; - float ui = (numSegs - j) * segIMappingScale + segIMappingOfs; - if (v > 0 && v < 1) { - float us = 1 - v; - uo = (uo - oCenter) * us + oCenter; - ui = (ui - iCenter) * us + iCenter; - } - - uv[vi].Set(uo, p.z); - uv[vi + numMainVerts].Set(ui, p.z); - - norm[vi] = dn; - norm[vi + numMainVerts] = -dn; - tang[vi].Set(-d.z, 0, d.x, 0); - tang[vi + numMainVerts].Set(d.z, 0, -d.x, 0); - } - } - - // side strip vertices - float stripScale = Mathf.Abs(stripMapping.y - stripMapping.x) / (sideThickness * mappingScale.y); - - vi = numMainVerts * 2; - float o = 0; - for (int i = 0; i < shape.Length; ++i, vi += 2) { - int si = i * (numSegs + 1); - - var d = dirs[0]; - verts[vi] = verts[si]; - uv[vi].Set(stripU0, o); - norm[vi].Set(d.z, 0, -d.x); - - verts[vi + 1] = verts[si + numMainVerts]; - uv[vi + 1].Set(stripU1, o); - norm[vi + 1] = norm[vi]; - tang[vi] = tang[vi + 1] = (verts[vi + 1] - verts[vi]).normalized; - - if (i + 1 < shape.Length) o += ((Vector2)shape[i + 1] - (Vector2)shape[i]).magnitude * stripScale; - } - - vi += numSideVerts - 2; - for (int i = shape.Length - 1; i >= 0; --i, vi -= 2) { - int si = i * (numSegs + 1) + numSegs; - if (i == shape.Length - 1 && inlineHeight <= 0) si = numMainVerts - 1; - - var d = dirs[numSegs]; - verts[vi] = verts[si]; - uv[vi].Set(stripU0, o); - norm[vi].Set(-d.z, 0, d.x); - - verts[vi + 1] = verts[si + numMainVerts]; - uv[vi + 1].Set(stripU1, o); - norm[vi + 1] = norm[vi]; - tang[vi] = tang[vi + 1] = (verts[vi + 1] - verts[vi]).normalized; - - if (i > 0) o += ((Vector2)shape[i] - (Vector2)shape[i - 1]).magnitude * stripScale; - } - - // ring vertices - vi = numMainVerts * 2 + numSideVerts * 2; - o = 0; - for (int j = numSegs; j >= 0; --j, vi += 2, o += ringSegLen * stripScale) { - verts[vi] = verts[j]; - uv[vi].Set(stripU0, o); - norm[vi] = -Vector3.up; - - verts[vi + 1] = verts[j + numMainVerts]; - uv[vi + 1].Set(stripU1, o); - norm[vi + 1] = -Vector3.up; - tang[vi] = tang[vi + 1] = (verts[vi + 1] - verts[vi]).normalized; - } - - if (inlineHeight > 0) { - // top ring vertices - o = 0; - int si = (shape.Length - 1) * (numSegs + 1); - for (int j = 0; j <= numSegs; ++j, vi += 2, o += topRingSegLen * stripScale) { - verts[vi] = verts[si + j]; - uv[vi].Set(stripU0, o); - norm[vi] = Vector3.up; - - verts[vi + 1] = verts[si + j + numMainVerts]; - uv[vi + 1].Set(stripU1, o); - norm[vi + 1] = Vector3.up; - tang[vi] = tang[vi + 1] = (verts[vi + 1] - verts[vi]).normalized; - } - } - - // set vertex data to mesh - for (int i = 0; i < totalVerts; ++i) tang[i].w = 1; - m.vertices = verts; - m.uv = uv; - m.normals = norm; - m.tangents = tang; - - m.uv2 = null; - m.colors32 = null; - - var tri = new int[totalFaces * 3]; - - // main faces - vi = 0; - int ti1 = 0, ti2 = numMainFaces * 3; - for (int i = 0; i < shape.Length - (inlineHeight <= 0 ? 2 : 1); ++i, ++vi) { - p = shape[i]; - for (int j = 0; j < numSegs; ++j, ++vi) { - tri[ti1++] = vi; - tri[ti1++] = vi + 1 + numSegs + 1; - tri[ti1++] = vi + 1; - - tri[ti1++] = vi; - tri[ti1++] = vi + numSegs + 1; - tri[ti1++] = vi + 1 + numSegs + 1; - - tri[ti2++] = numMainVerts + vi; - tri[ti2++] = numMainVerts + vi + 1; - tri[ti2++] = numMainVerts + vi + 1 + numSegs + 1; - - tri[ti2++] = numMainVerts + vi; - tri[ti2++] = numMainVerts + vi + 1 + numSegs + 1; - tri[ti2++] = numMainVerts + vi + numSegs + 1; - } - } - - if (inlineHeight <= 0) { - // main tip faces - for (int j = 0; j < numSegs; ++j, ++vi) { - tri[ti1++] = vi; - tri[ti1++] = numMainVerts - 1; - tri[ti1++] = vi + 1; - - tri[ti2++] = numMainVerts + vi; - tri[ti2++] = numMainVerts + vi + 1; - tri[ti2++] = numMainVerts + numMainVerts - 1; - } - } - - // side strip faces - vi = numMainVerts * 2; - ti1 = numMainFaces * 2 * 3; - ti2 = ti1 + numSideFaces * 3; - for (int i = 0; i < shape.Length - 1; ++i, vi += 2) { - tri[ti1++] = vi; - tri[ti1++] = vi + 1; - tri[ti1++] = vi + 3; - - tri[ti1++] = vi; - tri[ti1++] = vi + 3; - tri[ti1++] = vi + 2; - - tri[ti2++] = numSideVerts + vi; - tri[ti2++] = numSideVerts + vi + 3; - tri[ti2++] = numSideVerts + vi + 1; - - tri[ti2++] = numSideVerts + vi; - tri[ti2++] = numSideVerts + vi + 2; - tri[ti2++] = numSideVerts + vi + 3; - } - - // ring faces - vi = numMainVerts * 2 + numSideVerts * 2; - ti1 = (numMainFaces + numSideFaces) * 2 * 3; - for (int j = 0; j < numSegs; ++j, vi += 2) { - tri[ti1++] = vi; - tri[ti1++] = vi + 1; - tri[ti1++] = vi + 3; - - tri[ti1++] = vi; - tri[ti1++] = vi + 3; - tri[ti1++] = vi + 2; - } - - if (inlineHeight > 0) { - // top ring faces - vi += 2; - for (int j = 0; j < numSegs; ++j, vi += 2) { - tri[ti1++] = vi; - tri[ti1++] = vi + 1; - tri[ti1++] = vi + 3; - - tri[ti1++] = vi; - tri[ti1++] = vi + 3; - tri[ti1++] = vi + 2; - } - } - - m.triangles = tri; - - if (!HighLogic.LoadedSceneIsEditor) m.Optimize(); - - StartCoroutine(PFUtils.updateDragCubeCoroutine(part, 1)); - } - } - - - //ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ// - - -} // namespace - diff --git a/Source/NodeNumberTweaker.cs b/Source/NodeNumberTweaker.cs deleted file mode 100644 index baa9666..0000000 --- a/Source/NodeNumberTweaker.cs +++ /dev/null @@ -1,301 +0,0 @@ -// Procedural Fairings plug-in by Alexey Volynskov -// Licensed under CC BY 3.0 terms: http://creativecommons.org/licenses/by/3.0/ -using System; -using System.Collections.Generic; -using System.Text; -using UnityEngine; - - -namespace Keramzit -{ - - - - public class KzNodeNumberTweaker : PartModule - { - [KSPField] public string nodePrefix = "bottom"; - [KSPField] public int maxNumber = 0; - - [KSPField(guiActiveEditor = true, guiName = "Fairing Nodes")] - [UI_FloatRange(minValue = 1, maxValue = 8, stepIncrement = 1)] - public float uiNumNodes = 2; - - [KSPField(isPersistant = true)] - public int numNodes = 2; - private int numNodesBefore = 0; - - - [KSPField(isPersistant = true, guiActiveEditor = true, guiName = "Node offset", guiFormat = "S4", guiUnits = "m")] - [UI_FloatEdit(sigFigs = 3, unit = "m", minValue = 0.1f, maxValue = 5, incrementLarge = 0.625f, incrementSmall = 0.125f, incrementSlide = 0.001f)] - public float radius = 1.25f; - - [KSPField] public float radiusStepLarge = 0.625f; - [KSPField] public float radiusStepSmall = 0.125f; - - - [KSPField] public bool shouldResizeNodes = true; - - - protected float oldRadius = -1000; - protected bool justLoaded = false; - - - public override string GetInfo() - { - return "Max. Nodes: " + maxNumber; - } - - - //[KSPEvent(name="IncrementNodes", active=true, guiActive=false, guiActiveEditor=true, - // guiActiveUnfocused=false, guiName="More Fairing Nodes")] - //public void IncrementNodes() - //{ - // if (numNodes>=maxNumber) return; - // if (checkNodeAttachments()) return; - - // ++numNodes; - // updateNodes(); - //} - - - //[KSPEvent(name = "DecrementNodes", active = true, guiActive = false, guiActiveEditor = true, - // guiActiveUnfocused = false, guiName = "Fewer Fairing Nodes")] - //public void DecrementNodes() - //{ - // if (numNodes <= 1) return; - // if (checkNodeAttachments()) return; - - // --numNodes; - // updateNodes(); - //} - - [KSPField(isPersistant = true, guiActiveEditor = true, guiName = "Interstage Nodes")] - [UI_Toggle(disabledText = "Off", enabledText = "On")] - public bool showInterstageNodes = true; - - - - - - - - public virtual void FixedUpdate() - { - if (radius != oldRadius) { - oldRadius = radius; - updateNodePositions(); - } - - justLoaded = false; - } - - - public void Update() - { - if (HighLogic.LoadedSceneIsEditor) { - if ((int)uiNumNodes != numNodesBefore) { - if (checkNodeAttachments()) { - uiNumNodes = numNodesBefore; - } - else { - numNodes = (int)uiNumNodes; - numNodesBefore = numNodes; - updateNodes(); - - bool removed = false; - - for (int i = numNodes + 1; i <= maxNumber; ++i) { - var node = findNode(i); - if (node == null) - continue; - - // fix for ghost node when inserting a new pf base in VAB. - // do not delete unused nodes, move them away instead - // be careful to check references to maximum number of nodes - // mentioned elsewhere retreived from 'Findattachnodes("connect")' ! - // slightly hacky, but works. - //part.attachNodes.Remove(node); - HideUnusedNode(node); - removed = true; - } - - if (removed) { - var fbase = part.GetComponent(); - if (fbase) { fbase.needShapeUpdate = true; fbase.updateDelay = 0; } - } - } - } - - - } - } - - // slightly hacky.. - // but it removes the ghost nodes - private void HideUnusedNode(AttachNode node) - { - node.position.x = 10000; - } - - - public override void OnStart(StartState state) - { - // print("NNT: OnStart "+state); - base.OnStart(state); - if (state == StartState.None) return; - - ((UI_FloatEdit)Fields["radius"].uiControlEditor).incrementLarge = radiusStepLarge; - ((UI_FloatEdit)Fields["radius"].uiControlEditor).incrementSmall = radiusStepSmall; - - if (!shouldResizeNodes) { - Fields["radius"].guiActiveEditor = false; - } - - // hide interstage toggle if there are no interstage nodes - var nodes = part.FindAttachNodes("interstage"); - if (nodes == null) { - this.Fields["showInterstageNodes"].guiActiveEditor = false; - } - - // change gui text if there are no fairing connect nodes - nodes = part.FindAttachNodes("connect"); - if (nodes == null) { - this.Fields["uiNumNodes"].guiName = "Side Nodes"; - } - - ((UI_FloatRange)Fields["uiNumNodes"].uiControlEditor).maxValue = maxNumber; - uiNumNodes = numNodes; - numNodesBefore = numNodes; - - - updateNodes(); - } - - - public override void OnLoad(ConfigNode cfg) - { - // print("NNT: OnLoad"); - base.OnLoad(cfg); - justLoaded = true; - - uiNumNodes = numNodes; - numNodesBefore = numNodes; - - if (HighLogic.LoadedSceneIsEditor || HighLogic.LoadedSceneIsFlight) updateNodes(); - } - - - void updateNodes() - { - addRemoveNodes(); - updateNodePositions(); - } - - - string nodeName(int i) { return string.Format("{0}{1:d2}", nodePrefix, i); } - AttachNode findNode(int i) { return part.FindAttachNode(nodeName(i)); } - - - bool checkNodeAttachments() - { - for (int i = 1; i <= maxNumber; ++i) { - var node = findNode(i); - if (node != null && node.attachedPart != null) { - EditorScreenMessager.showMessage("Please detach parts before changing number of nodes", 1); - return true; - } - } - - return false; - } - - - void addRemoveNodes() - { - // print("NNT: setting nodes to "+numNodes); - part.stackSymmetry = numNodes - 1; - - float y = 0; - bool gotY = false; - int nodeSize = 0; - Vector3 dir = Vector3.up; - - int i; - for (i = 1; i <= maxNumber; ++i) { - var node = findNode(i); - if (node == null) continue; - y = node.position.y; - nodeSize = node.size; - dir = node.orientation; - gotY = true; - break; - } - - if (!gotY) { - var node = part.FindAttachNode("bottom"); - if (node != null) y = node.position.y; - } - - for (i = 1; i <= numNodes; ++i) { - var node = findNode(i); - if (node != null) continue; - - // create node - node = new AttachNode(); - node.id = nodeName(i); - node.owner = part; - node.nodeType = AttachNode.NodeType.Stack; - node.position = new Vector3(0, y, 0); - node.orientation = dir; - node.originalPosition = node.position; - node.originalOrientation = node.orientation; - node.size = nodeSize; - part.attachNodes.Add(node); - } - - for (; i <= maxNumber; ++i) { - var node = findNode(i); - if (node == null) continue; - - if (HighLogic.LoadedSceneIsEditor) - node.position.x = 10000; //new Vector3(10000, 0, 0); - else - part.attachNodes.Remove(node); - } - - var fbase = part.GetComponent(); - if (fbase) fbase.needShapeUpdate = true; - } - - - void updateNodePositions() - { - float d = Mathf.Sin(Mathf.PI / numNodes) * radius * 2; - int size = Mathf.RoundToInt(d / (radiusStepLarge * 2)); - - for (int i = 1; i <= numNodes; ++i) { - var node = findNode(i); - if (node == null) continue; - - float a = Mathf.PI * 2 * (i - 1) / numNodes; - node.position.x = Mathf.Cos(a) * radius; - node.position.z = Mathf.Sin(a) * radius; - if (shouldResizeNodes) node.size = size; - - if (!justLoaded) PFUtils.updateAttachedPartPos(node, part); - } - - for (int i = numNodes + 1; i <= maxNumber; ++i) { - var node = findNode(i); - if (node == null) continue; - - node.position.x = 10000; //new Vector3(10000, 0, 0); - } - - - } - } - - -} // namespace - diff --git a/Source/PFKMJoint.cs b/Source/PFKMJoint.cs deleted file mode 100644 index 28340ea..0000000 --- a/Source/PFKMJoint.cs +++ /dev/null @@ -1,433 +0,0 @@ -using System; -using System.Collections.Generic; -using UnityEngine; - -namespace Keramzit -{ - public class PFKMJoints : PartModule - { - [KSPField(isPersistant = true)] - public float breakingForce = 500000f; - - [KSPField(guiActive = true, guiName = "View Joints")] - [UI_Toggle(disabledText = "Off", enabledText = "On")] - public bool viewJoints = false; - - private float w1 = 0.05f; - - private float w2 = 0.15f; - - private List jointLines = new List(); - - private bool morejointsadded = false; - - private Part bottomNodePart = null; - - private List nodeParts = new List(); - - public PFKMJoints() - { - } - - private void AddMoreJoints() - { - int i; - AttachNode attachNode; - if (!this.morejointsadded) { - AttachNode attachNode1 = base.part.FindAttachNode("bottom"); - AttachNode[] attachNodeArray = base.part.FindAttachNodes("interstage"); - AttachNode[] attachNodeArray1 = base.part.FindAttachNodes("top"); - string[] _vessel = new string[] { "[ProceduralFairings] Adding Joints to Vessel: ", base.vessel.vesselName, " (Launch ID: ", base.part.launchID.ToString(), ") (GUID: ", base.vessel.id.ToString(), ")" }; - Debug.Log(string.Concat(_vessel)); - _vessel = new string[] { "[ProceduralFairings] For PF Part: ", base.part.name, " (", base.part.craftID.ToString(), ")" }; - Debug.Log(string.Concat(_vessel)); - Part part = null; - if ((attachNode1 == null ? false : attachNode1.attachedPart != null)) { - part = attachNode1.attachedPart; - this.bottomNodePart = part; - this.addStrut(part, base.part, Vector3.zero); - _vessel = new string[] { "[ProceduralFairings] Bottom Part: ", part.name, " (", part.craftID.ToString(), ")" }; - Debug.Log(string.Concat(_vessel)); - } - Debug.Log("[ProceduralFairings] Top Parts:"); - if (attachNodeArray1 != null) { - for (i = 0; i < (int)attachNodeArray1.Length; i++) { - attachNode = attachNodeArray1[i]; - if (!(attachNode.attachedPart == null)) { - if (part != null) { - this.AddPartJoint(attachNode.attachedPart, part, attachNode.FindOpposingNode(), attachNode1.FindOpposingNode()); - } - this.addStrut(attachNode.attachedPart, base.part, Vector3.zero); - this.nodeParts.Add(attachNode.attachedPart); - _vessel = new string[] { "[ProceduralFairings] ", attachNode.attachedPart.name, " (", attachNode.attachedPart.craftID.ToString(), ")" }; - Debug.Log(string.Concat(_vessel)); - } - } - } - if (attachNodeArray != null) { - for (i = 0; i < (int)attachNodeArray.Length; i++) { - attachNode = attachNodeArray[i]; - if (!(attachNode.attachedPart == null)) { - if (part != null) { - this.AddPartJoint(attachNode.attachedPart, part, attachNode.FindOpposingNode(), attachNode1.FindOpposingNode()); - } - this.addStrut(attachNode.attachedPart, base.part, Vector3.zero); - this.nodeParts.Add(attachNode.attachedPart); - _vessel = new string[] { "[ProceduralFairings] ", attachNode.attachedPart.name, " (", attachNode.attachedPart.craftID.ToString(), ")" }; - Debug.Log(string.Concat(_vessel)); - } - } - } - this.morejointsadded = true; - } - } - - private void AddPartJoint(Part p, Part pp, AttachNode pnode, AttachNode ppnode) - { - PartJoint partJoint = PartJoint.Create(p, pp, pnode, ppnode, 0); - partJoint.SetBreakingForces(this.breakingForce, this.breakingForce); - PartJoint partJoint1 = p.gameObject.AddComponent(); - partJoint1 = partJoint; - } - - private ConfigurableJoint addStrut(Part p, Part pp, Vector3 pos) - { - ConfigurableJoint configurableJoint; - if (!(p == pp)) { - Rigidbody rigidbody = pp.Rigidbody; - if ((rigidbody == null ? false : !(rigidbody == p.Rigidbody))) { - ConfigurableJoint configurableJoint1 = p.gameObject.AddComponent(); - configurableJoint1.xMotion = 0; - configurableJoint1.yMotion = 0; - configurableJoint1.zMotion = 0; - configurableJoint1.angularXMotion = 0; - configurableJoint1.angularYMotion = 0; - configurableJoint1.angularZMotion = 0; - configurableJoint1.projectionDistance = 0.1f; - configurableJoint1.projectionAngle = 5f; - configurableJoint1.breakForce = this.breakingForce; - configurableJoint1.breakTorque = this.breakingForce; - configurableJoint1.connectedBody = rigidbody; - configurableJoint1.targetPosition = pos; - configurableJoint = configurableJoint1; - } - else { - configurableJoint = null; - } - } - else { - configurableJoint = null; - } - return configurableJoint; - } - - private void ClearJointLines() - { - for (int i = 0; i < this.jointLines.Count; i++) { - UnityEngine.Object.Destroy(this.jointLines[i].gameObject); - } - this.jointLines.Clear(); - } - - public virtual void FixedUpdate() - { - if (!this.morejointsadded) { - if ((!FlightGlobals.ready || base.vessel.packed ? false : base.vessel.loaded)) { - this.AddMoreJoints(); - } - } - } - - private LineRenderer JointLine(Vector3 posp, Vector3 pospp, Color col, float width) - { - LineRenderer lineRenderer = this.makeLineRenderer("JointLine", col, width); - lineRenderer.SetVertexCount(2); - lineRenderer.SetPosition(0, posp); - lineRenderer.SetPosition(1, pospp); - lineRenderer.useWorldSpace = true; - return lineRenderer; - } - - public void ListJoints() - { - int j; - string[] _name; - float _breakForce; - Vector3 _anchor; - string str; - string str1; - this.ClearJointLines(); - List activeVessel = FlightGlobals.ActiveVessel.parts; - for (int i = 0; i < activeVessel.Count; i++) { - ConfigurableJoint[] components = activeVessel[i].gameObject.GetComponents(); - if (components != null) { - for (j = 0; j < (int)components.Length; j++) { - ConfigurableJoint configurableJoint = components[j]; - _name = new string[18]; - _name[0] = "[PF] , "; - _name[1] = activeVessel[i].name; - _name[2] = ", "; - _name[3] = (configurableJoint.connectedBody == null ? "" : configurableJoint.connectedBody.name); - _name[4] = ", "; - _breakForce = configurableJoint.breakForce; - _name[5] = _breakForce.ToString(); - _name[6] = ", "; - _breakForce = configurableJoint.breakTorque; - _name[7] = _breakForce.ToString(); - _name[8] = ", "; - _anchor = configurableJoint.anchor; - _name[9] = _anchor.ToString(); - _name[10] = ", "; - _anchor = configurableJoint.connectedAnchor; - _name[11] = _anchor.ToString(); - _name[12] = ", "; - string[] strArrays = _name; - if (configurableJoint.connectedBody == null) { - str1 = "--"; - } - else { - _anchor = activeVessel[i].transform.position - configurableJoint.connectedBody.position; - str1 = _anchor.ToString(); - } - strArrays[13] = str1; - _name[14] = ", "; - _breakForce = configurableJoint.linearLimitSpring.damper; - _name[15] = _breakForce.ToString("F2"); - _name[16] = ", "; - _breakForce = configurableJoint.linearLimitSpring.spring; - _name[17] = _breakForce.ToString("F2"); - Debug.Log(string.Concat(_name)); - } - } - PartJoint[] partJointArray = activeVessel[i].gameObject.GetComponents(); - if (partJointArray != null) { - for (j = 0; j < (int)partJointArray.Length; j++) { - PartJoint partJoint = partJointArray[j]; - if ((partJoint.Host != null ? true : !(partJoint.Target == null))) { - _name = new string[] { "[PF] , ", partJoint.Host.name, ", ", null, null, null, null, null, null, null, null }; - _name[3] = (partJoint.Target == null ? "" : partJoint.Target.name); - string[] strArrays1 = _name; - if (partJoint.Joint == null) { - int count = partJoint.joints.Count; - str = string.Concat(" (", count.ToString(), ")"); - } - else { - _breakForce = partJoint.Joint.breakForce; - string str2 = _breakForce.ToString(); - _breakForce = partJoint.Joint.breakTorque; - str = string.Concat(", ", str2, ", ", _breakForce.ToString()); - } - strArrays1[4] = str; - _name[5] = ", "; - _breakForce = partJoint.stiffness; - _name[6] = _breakForce.ToString("F2"); - _name[7] = ", "; - _anchor = partJoint.HostAnchor; - _name[8] = _anchor.ToString(); - _name[9] = ", "; - _anchor = partJoint.TgtAnchor; - _name[10] = _anchor.ToString(); - Debug.Log(string.Concat(_name)); - } - else { - Debug.Log("[PF] , , "); - } - if (partJoint.Target) { - AttachNode attachNode = activeVessel[i].FindAttachNodeByPart(partJoint.Target); - if (attachNode != null) { - object[] objArray = new object[] { "[PF] , ", partJoint.Host.name, ", ", partJoint.Target.name, ", ", attachNode.breakingForce.ToString(), ", ", attachNode.breakingTorque.ToString(), ", ", attachNode.contactArea.ToString("F2"), ", ", attachNode.attachMethod, ", ", attachNode.rigid.ToString(), ", ", attachNode.radius.ToString("F2") }; - Debug.Log(string.Concat(objArray)); - AttachNode attachNode1 = attachNode.FindOpposingNode(); - if ((attachNode1 == null ? false : attachNode1.owner != null)) { - objArray = new object[] { "[PF] , ", attachNode1.owner.name, ", ", (attachNode1.attachedPart != null ? attachNode1.attachedPart.name : ""), ", ", attachNode1.breakingForce.ToString(), ", ", attachNode1.breakingTorque.ToString(), ", ", attachNode1.contactArea.ToString("F2"), ", ", attachNode1.attachMethod, ", ", attachNode1.rigid.ToString(), ", ", attachNode1.radius.ToString("F2") }; - Debug.Log(string.Concat(objArray)); - } - } - } - } - } - FixedJoint[] fixedJointArray = activeVessel[i].gameObject.GetComponents(); - if (fixedJointArray != null) { - for (j = 0; j < (int)fixedJointArray.Length; j++) { - FixedJoint fixedJoint = fixedJointArray[j]; - _name = new string[] { "[PF] , ", fixedJoint.name, ", ", null, null, null, null, null, null, null, null, null }; - _name[3] = (fixedJoint.connectedBody == null ? "" : fixedJoint.connectedBody.name); - _name[4] = ", "; - _breakForce = fixedJoint.breakForce; - _name[5] = _breakForce.ToString(); - _name[6] = ", "; - _breakForce = fixedJoint.breakTorque; - _name[7] = _breakForce.ToString(); - _name[8] = ", "; - _anchor = fixedJoint.anchor; - _name[9] = _anchor.ToString(); - _name[10] = ", "; - _anchor = fixedJoint.connectedAnchor; - _name[11] = _anchor.ToString(); - Debug.Log(string.Concat(_name)); - } - } - } - } - - private LineRenderer makeLineRenderer(string name, Color color, float wd) - { - GameObject gameObject = new GameObject(name); - gameObject.transform.parent = base.part.transform; - gameObject.transform.localPosition = Vector3.zero; - gameObject.transform.localRotation = Quaternion.identity; - LineRenderer lineRenderer = gameObject.AddComponent(); - lineRenderer.useWorldSpace = true; - lineRenderer.material = new Material(Shader.Find("Particles/Additive")); - lineRenderer.SetColors(color, color); - lineRenderer.SetWidth(wd, wd); - lineRenderer.SetVertexCount(0); - return lineRenderer; - } - - public void OnDestroy() - { - this.viewJoints = false; - this.ClearJointLines(); - GameEvents.onGameSceneLoadRequested.Remove(new EventData.OnEvent(OnGameSceneLoadRequested)); - GameEvents.onVesselWasModified.Remove(new EventData.OnEvent(OnVesselModified)); - GameEvents.onPartJointBreak.Remove(new EventData.OnEvent(OnPartJointBreak)); - GameEvents.onVesselGoOffRails.Remove(new EventData.OnEvent(OnVesselGoOffRails)); - } - - private void OnGameSceneLoadRequested(GameScenes scene) - { - if ((scene == GameScenes.FLIGHT ? false : this.viewJoints)) { - this.viewJoints = false; - this.ClearJointLines(); - } - } - - private void OnPartJointBreak(PartJoint pj, float value) - { - Part host; - int i; - bool flag; - if (!(pj.Host == base.part)) { - if (pj.Target == base.part) { - host = pj.Host; - if (this.nodeParts.Contains(host)) { - if (this.bottomNodePart != null) { - this.RemoveJoints(host, this.bottomNodePart); - } - this.RemoveJoints(host, base.part); - flag = this.nodeParts.Remove(host); - } - else if (host == this.bottomNodePart) { - for (i = 0; i < this.nodeParts.Count; i++) { - this.RemoveJoints(this.nodeParts[i], this.bottomNodePart); - } - this.RemoveJoints(this.bottomNodePart, base.part); - } - return; - } - return; - } - else { - host = pj.Target; - } - if (this.nodeParts.Contains(host)) { - if (this.bottomNodePart != null) { - this.RemoveJoints(host, this.bottomNodePart); - } - this.RemoveJoints(host, base.part); - flag = this.nodeParts.Remove(host); - } - else if (host == this.bottomNodePart) { - for (i = 0; i < this.nodeParts.Count; i++) { - this.RemoveJoints(this.nodeParts[i], this.bottomNodePart); - } - this.RemoveJoints(this.bottomNodePart, base.part); - } - } - - public override void OnStart(PartModule.StartState state) - { - base.OnStart(state); - UI_Toggle _uiControlFlight = (UI_Toggle)base.Fields["viewJoints"].uiControlFlight; - _uiControlFlight.onFieldChanged = (Callback)Delegate.Combine(_uiControlFlight.onFieldChanged, new Callback(UIviewJoints_changed)); - GameEvents.onGameSceneLoadRequested.Add(new EventData.OnEvent(OnGameSceneLoadRequested)); - GameEvents.onVesselWasModified.Add(new EventData.OnEvent(OnVesselModified)); - GameEvents.onPartJointBreak.Add(new EventData.OnEvent(OnPartJointBreak)); - GameEvents.onVesselGoOffRails.Add(new EventData.OnEvent(OnVesselGoOffRails)); - } - - private void OnVesselGoOffRails(Vessel v) - { - if ((v == null ? true : this == null)) { - } - } - - private void OnVesselModified(Vessel v) - { - if (v == base.vessel) { - if (this.viewJoints) { - this.ViewJoints(); - } - } - } - - private void RemoveJoints(Part p, Part pp) - { - if ((p == null || p.Rigidbody == null || pp == null ? false : !(pp.Rigidbody == null))) { - ConfigurableJoint[] components = p.gameObject.GetComponents(); - for (int i = 0; i < (int)components.Length; i++) { - ConfigurableJoint configurableJoint = components[i]; - if (configurableJoint.connectedBody == pp.Rigidbody) { - try { - UnityEngine.Object.Destroy(configurableJoint); - } - catch (Exception exception1) { - Exception exception = exception1; - string[] str = new string[] { "[PF] RemoveJoint Anomaly (", p.ToString(), ", ", pp.ToString(), "): ", exception.Message }; - Debug.Log(string.Concat(str)); - } - } - } - } - } - - private void UIviewJoints_changed(BaseField bf, object obj) - { - if (this.viewJoints) { - this.ListJoints(); - this.ViewJoints(); - } - else { - this.ClearJointLines(); - } - } - - public void ViewJoints() - { - this.ClearJointLines(); - List activeVessel = FlightGlobals.ActiveVessel.parts; - for (int i = 0; i < activeVessel.Count; i++) { - ConfigurableJoint[] components = activeVessel[i].gameObject.GetComponents(); - if (components != null) { - for (int j = 0; j < (int)components.Length; j++) { - ConfigurableJoint configurableJoint = components[j]; - if (!(configurableJoint.connectedBody == null)) { - Vector3 vector3 = new Vector3(0f, 5f, 0f); - Vector3 vector31 = new Vector3(0.25f, 0f, 0f); - Vector3 _position = activeVessel[i].transform.position + vector3; - Vector3 _position1 = configurableJoint.connectedBody.position + vector3; - Vector3 _position2 = (activeVessel[i].transform.position + (activeVessel[i].transform.rotation * configurableJoint.anchor)) + vector3; - Vector3 vector32 = (configurableJoint.connectedBody.position + (configurableJoint.connectedBody.rotation * configurableJoint.connectedAnchor)) + vector3; - this.jointLines.Add(this.JointLine(_position, _position1, Color.blue, this.w1)); - this.jointLines.Add(this.JointLine(_position2, vector32 + vector31, Color.yellow, this.w2)); - this.jointLines.Add(this.JointLine(_position, _position2, Color.gray, 0.03f)); - this.jointLines.Add(this.JointLine(_position1, vector32, Color.gray, 0.03f)); - } - } - } - } - } - } -} - diff --git a/Source/PayloadScan.cs b/Source/PayloadScan.cs deleted file mode 100644 index 560be55..0000000 --- a/Source/PayloadScan.cs +++ /dev/null @@ -1,179 +0,0 @@ -// Procedural Fairings plug-in by Alexey Volynskov -// Licensed under CC BY 3.0 terms: http://creativecommons.org/licenses/by/3.0/ -using System; -using System.Collections.Generic; -using System.Text; -using UnityEngine; - - -namespace Keramzit -{ - - - //ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ// - - - struct PayloadScan - { - public List profile; - public List payload; - public HashSet hash; - - public List targets; - - public Matrix4x4 w2l; - - public float ofs, verticalStep, extraRadius; - - public int nestedBases; - - - public PayloadScan(Part p, float vs, float er) - { - profile = new List(); - payload = new List(); - targets = new List(); - hash = new HashSet(); - hash.Add(p); - w2l = p.transform.worldToLocalMatrix; - ofs = 0; - verticalStep = vs; - extraRadius = er; - nestedBases = 0; - } - - - public void addPart(Part p, Part prevPart) - { - if (p == null || hash.Contains(p)) return; - hash.Add(p); - - if (p.GetComponent() != null) return; - if (p == prevPart.parent && prevPart.srfAttachNode.attachedPart == p) return; - - // check for another fairing base - if (p.GetComponent() != null - && p.GetComponent() == null) { - AttachNode node = p.FindAttachNode("top"); - if (node != null && node.attachedPart == prevPart) { - // reversed base - potential inline fairing target - if (nestedBases <= 0) { targets.Add(p); return; } - else --nestedBases; - } - else ++nestedBases; - } - - payload.Add(p); - } - - - public void addPayloadEdge(Vector3 v0, Vector3 v1) - { - float r0 = Mathf.Sqrt(v0.x * v0.x + v0.z * v0.z) + extraRadius; - float r1 = Mathf.Sqrt(v1.x * v1.x + v1.z * v1.z) + extraRadius; - - float y0 = (v0.y - ofs) / verticalStep; - float y1 = (v1.y - ofs) / verticalStep; - - if (y0 > y1) { - float tmp; - tmp = y0; y0 = y1; y1 = tmp; - tmp = r0; r0 = r1; r1 = tmp; - } - - int h0 = Mathf.FloorToInt(y0); - int h1 = Mathf.FloorToInt(y1); - if (h1 < 0) return; - - - - if (h1 >= profile.Count) { - float[] farray = new float[h1 - profile.Count + 1]; - for (int i = 0; i < farray.Length; i++) - farray[i] = 0; - - profile.AddRange(farray); - } - - if (h0 >= 0) profile[h0] = Mathf.Max(profile[h0], r0); - profile[h1] = Mathf.Max(profile[h1], r1); - - - if (h0 != h1) { - float k = (r1 - r0) / (y1 - y0); - float b = r0 + k * (h0 + 1 - y0); - float maxR = Mathf.Max(r0, r1); - - for (int h = Math.Max(h0, 0); h < h1; ++h) { - float r = Mathf.Min(k * (h - h0) + b, maxR); - profile[h] = Mathf.Max(profile[h], r); - profile[h + 1] = Mathf.Max(profile[h + 1], r); - } - } - } - - - public void addPayload(Bounds box, Matrix4x4 boxTm) - { - Matrix4x4 m = w2l * boxTm; - - Vector3 p0 = box.min, p1 = box.max; - var verts = new Vector3[8]; - for (int i = 0; i < 8; ++i) - verts[i] = m.MultiplyPoint3x4(new Vector3( - (i & 1) != 0 ? p1.x : p0.x, - (i & 2) != 0 ? p1.y : p0.y, - (i & 4) != 0 ? p1.z : p0.z)); - - addPayloadEdge(verts[0], verts[1]); - addPayloadEdge(verts[2], verts[3]); - addPayloadEdge(verts[4], verts[5]); - addPayloadEdge(verts[6], verts[7]); - - addPayloadEdge(verts[0], verts[2]); - addPayloadEdge(verts[1], verts[3]); - addPayloadEdge(verts[4], verts[6]); - addPayloadEdge(verts[5], verts[7]); - - addPayloadEdge(verts[0], verts[4]); - addPayloadEdge(verts[1], verts[5]); - addPayloadEdge(verts[2], verts[6]); - addPayloadEdge(verts[3], verts[7]); - } - - - public void addPayload(Collider c) - { - var mc = c as MeshCollider; - var bc = c as BoxCollider; - if (mc) { - // addPayload(mc.sharedMesh.bounds, - // c.GetComponent().localToWorldMatrix); - var m = w2l * mc.transform.localToWorldMatrix; - var verts = mc.sharedMesh.vertices; - var faces = mc.sharedMesh.triangles; - for (int i = 0; i < faces.Length; i += 3) { - var v0 = m.MultiplyPoint3x4(verts[faces[i]]); - var v1 = m.MultiplyPoint3x4(verts[faces[i + 1]]); - var v2 = m.MultiplyPoint3x4(verts[faces[i + 2]]); - addPayloadEdge(v0, v1); - addPayloadEdge(v1, v2); - addPayloadEdge(v2, v0); - } - } - else if (bc) - addPayload(new Bounds(bc.center, bc.size), - bc.transform.localToWorldMatrix); - else { - // Debug.Log("generic collider "+c); - addPayload(c.bounds, Matrix4x4.identity); - } - } - } - - - //ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ// - - -} // namespace - diff --git a/Source/ProcAdapter.cs b/Source/ProcAdapter.cs deleted file mode 100644 index 53eb030..0000000 --- a/Source/ProcAdapter.cs +++ /dev/null @@ -1,569 +0,0 @@ -// Procedural Fairings plug-in by Alexey Volynskov -// Licensed under CC BY 3.0 terms: http://creativecommons.org/licenses/by/3.0/ -using System; -using System.Collections.Generic; -using System.Text; -using UnityEngine; -using KSP.UI.Screens; - -namespace Keramzit -{ - - - //ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ// - - - abstract class ProceduralAdapterBase : PartModule - { - [KSPField(isPersistant = true, guiActiveEditor = true, guiName = "Base", guiFormat = "S4", guiUnits = "m")] - [UI_FloatEdit(sigFigs = 3, unit = "m", minValue = 0.1f, maxValue = 5, incrementLarge = 1.25f, incrementSmall = 0.125f, incrementSlide = 0.001f)] - public float baseSize = 1.25f; - - [KSPField(isPersistant = true, guiActiveEditor = true, guiName = "Top", guiFormat = "S4", guiUnits = "m")] - [UI_FloatEdit(sigFigs = 3, unit = "m", minValue = 0.1f, maxValue = 5, incrementLarge = 1.25f, incrementSmall = 0.125f, incrementSlide = 0.001f)] - public float topSize = 1.25f; - - [KSPField(isPersistant = true, guiActiveEditor = true, guiName = "Height", guiFormat = "S4", guiUnits = "m")] - [UI_FloatEdit(sigFigs = 3, unit = "m", minValue = 0.1f, maxValue = 50, incrementLarge = 1.0f, incrementSmall = 0.1f, incrementSlide = 0.001f)] - public float height = 1; - - [KSPField] public string topNodeName = "top1"; - - [KSPField] public float diameterStepLarge = 1.25f; - [KSPField] public float diameterStepSmall = 0.125f; - - [KSPField] public float heightStepLarge = 1.0f; - [KSPField] public float heightStepSmall = 0.1f; - - public bool changed = true; - - abstract public float minHeight { get; } - - - private float lastBaseSize = -1000; - private float lastTopSize = -1000; - private float lastHeight = -1000; - - protected bool justLoaded = false; - - - public virtual void checkTweakables() - { - if (baseSize != lastBaseSize) { lastBaseSize = baseSize; changed = true; } - if (topSize != lastTopSize) { lastTopSize = topSize; changed = true; } - if (height != lastHeight) { lastHeight = height; changed = true; } - } - - public virtual void FixedUpdate() - { - checkTweakables(); - if (changed) - updateShape(); - justLoaded = false; - } - - public virtual void updateShape() - { - changed = false; - float topheight = 0; - float topnodeheight = 0; - - var node = part.FindAttachNode("bottom"); - if (node != null) node.size = Mathf.RoundToInt(baseSize / diameterStepLarge); - - node = part.FindAttachNode("top"); - if (node != null) { - node.size = Mathf.RoundToInt(baseSize / diameterStepLarge); - topheight = node.position.y; - } - - node = part.FindAttachNode(topNodeName); - if (node != null) { - node.position = new Vector3(0, height, 0); - node.size = Mathf.RoundToInt(topSize / diameterStepLarge); - if (!justLoaded) PFUtils.updateAttachedPartPos(node, part); - topnodeheight = height; - } - else - Debug.LogError("[ProceduralAdapterBase] No '" + topNodeName + "' node in part", this); - - var internodes = part.FindAttachNodes("interstage"); - if (internodes != null) { - var inc = (topnodeheight - topheight) / (internodes.Length / 2 + 1); - - for (int i = 0, j = 0; i < internodes.Length; i = i + 2) { - var height = topheight + (j + 1) * inc; - j++; - - node = internodes[i]; - node.position.y = height; - node.size = node.size = Mathf.RoundToInt(topSize / diameterStepLarge) - 1; - if (!justLoaded) PFUtils.updateAttachedPartPos(node, part); - - node = internodes[i + 1]; - node.position.y = height; - node.size = node.size = Mathf.RoundToInt(topSize / diameterStepLarge) - 1; - if (!justLoaded) PFUtils.updateAttachedPartPos(node, part); - } - } - } - - - - - public override void OnStart(StartState state) - { - base.OnStart(state); - - if (state == StartState.None) return; - - StartCoroutine(FireFirstChanged()); - - } - - public IEnumerator FireFirstChanged() - { - while(!(part.editorStarted || part.started)) { - yield return new WaitForFixedUpdate(); - } - //wait a little more - yield return new WaitForSeconds(.01f); - changed = true; - } - - public override void OnLoad(ConfigNode cfg) - { - base.OnLoad(cfg); - justLoaded = true; - changed = true; - } - - } - - - //ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ// - - - class ProceduralFairingAdapter : ProceduralAdapterBase, IPartCostModifier, IPartMassModifier - { - [KSPField] public float sideThickness = 0.05f / 1.25f; - [KSPField] public Vector4 specificMass = new Vector4(0.005f, 0.011f, 0.009f, 0f); - [KSPField] public float specificBreakingForce = 6050; - [KSPField] public float specificBreakingTorque = 6050; - [KSPField] public float costPerTonne = 2000; - - [KSPField] public float dragAreaScale = 1; - - [KSPField(isPersistant = true)] - public bool topNodeDecouplesWhenFairingsGone = false; - - public bool isTopNodePartPresent = true; - public bool isFairingPresent = true; - - [KSPEvent(name = "decNoFairings", active = true, guiActive = true, guiActiveEditor = true, guiActiveUnfocused = true, guiName = "text")] - public void UIToggleTopNodeDecouple() - { - if (topNodeDecouplesWhenFairingsGone) - topNodeDecouplesWhenFairingsGone = false; - else - topNodeDecouplesWhenFairingsGone = true; - - UpdateUIdecNoFairingsText(topNodeDecouplesWhenFairingsGone); - } - - void UpdateUIdecNoFairingsText(bool flag) - { - if (flag) - this.Events["UIToggleTopNodeDecouple"].guiName = "Decouple when Fairing gone: Yes"; - else - this.Events["UIToggleTopNodeDecouple"].guiName = "Decouple when Fairing gone: No"; - } - - - public override float minHeight { get { return baseSize * 0.2f; } } - - public ModifierChangeWhen GetModuleCostChangeWhen() { return ModifierChangeWhen.FIXED; } - public ModifierChangeWhen GetModuleMassChangeWhen() { return ModifierChangeWhen.FIXED; } - - public float GetModuleCost(float defcost, ModifierStagingSituation sit) - { - return totalMass * costPerTonne - defcost; - } - - public float GetModuleMass(float defmass, ModifierStagingSituation sit) - { - return totalMass - defmass; - } - - public float calcSideThickness() - { - return Mathf.Min( - sideThickness * Mathf.Max(baseSize, topSize), - Mathf.Min(baseSize, topSize) * 0.25f); - } - - public float topRadius { get { return topSize * 0.5f - calcSideThickness(); } } - - [KSPField(isPersistant = true, guiActiveEditor = true, guiName = "Extra height", guiFormat = "S4", guiUnits = "m")] - [UI_FloatEdit(sigFigs = 3, unit = "m", minValue = 0, maxValue = 50, incrementLarge = 1.0f, incrementSmall = 0.1f, incrementSlide = 0.001f)] - public float extraHeight = 0; - - public bool engineFairingRemoved = false; - - [KSPField(isPersistant = false, guiActive = false, guiActiveEditor = true, guiName = "Mass")] - public string massDisplay; - - [KSPField(isPersistant = false, guiActive = false, guiActiveEditor = true, guiName = "Cost")] - public string costDisplay; - - - private bool limitsSet = false; - - - private float lastExtraHt = -1000; - - public override void checkTweakables() - { - base.checkTweakables(); - if (extraHeight != lastExtraHt) { lastExtraHt = extraHeight; changed = true; } - } - - private void RemoveTopPartJoints() - { - Part topPart = this.getTopPart(); - Part bottomPart = this.getBottomPart(); - if ((topPart == null ? false : !(bottomPart == null))) { - ConfigurableJoint[] components = topPart.gameObject.GetComponents(); - for (int i = 0; i < (int)components.Length; i++) { - ConfigurableJoint configurableJoint = components[i]; - if (configurableJoint.connectedBody == bottomPart.Rigidbody) { - UnityEngine.Object.Destroy(configurableJoint); - } - } - } - } - - public void Start() - { - part.mass = totalMass; - } - - public override void OnStart(StartState state) - { - base.OnStart(state); - limitsSet = false; - part.mass = totalMass; - - isFairingPresent = CheckForFairingPresent(); - isTopNodePartPresent = (getTopPart() != null) ? true : false; - - UpdateUIdecNoFairingsText(topNodeDecouplesWhenFairingsGone); - - GameEvents.onEditorShipModified.Add(OnEditorShipModified); - GameEvents.onVesselWasModified.Add(OnVesselWasModified); - GameEvents.onVesselCreate.Add(OnVesselCreate); - GameEvents.onVesselGoOffRails.Add(OnVesselGoOffRails); - GameEvents.onVesselLoaded.Add(OnVesselLoaded); - GameEvents.onStageActivate.Add(OnStageActivate); - } - - - public void OnDestroy() - { - GameEvents.onEditorShipModified.Remove(OnEditorShipModified); - GameEvents.onVesselWasModified.Remove(OnVesselWasModified); - GameEvents.onVesselCreate.Remove(OnVesselCreate); - GameEvents.onVesselGoOffRails.Remove(OnVesselGoOffRails); - GameEvents.onVesselLoaded.Remove(OnVesselLoaded); - GameEvents.onStageActivate.Remove(OnStageActivate); - } - - - bool isShipModified = true; - bool isStaged = false; - int stageNum = 0; - - - // lets catch some events.. - void OnEditorShipModified(ShipConstruct sc) - { - isShipModified = true; - } - - void OnVesselWasModified(Vessel ves) - { - isShipModified = true; - } - - void OnVesselCreate(Vessel ves) - { - isShipModified = true; - } - - void OnVesselGoOffRails(Vessel ves) - { - isShipModified = true; - } - - void OnVesselLoaded(Vessel ves) - { - isShipModified = true; - } - - void OnStageActivate(int stage) - { - isStaged = true; - stageNum = stage; - } - - - - - public float totalMass; - public override void updateShape() - { - base.updateShape(); - - float sth = calcSideThickness(); - float br = baseSize * 0.5f - sth; - float scale = br * 2; - - part.mass = totalMass = ((specificMass.x * scale + specificMass.y) * scale + specificMass.z) * scale + specificMass.w; - massDisplay = PFUtils.formatMass(totalMass); - costDisplay = PFUtils.formatCost(part.partInfo.cost + GetModuleCost(part.partInfo.cost, ModifierStagingSituation.CURRENT)); - part.breakingForce = specificBreakingForce * Mathf.Pow(br, 2); - part.breakingTorque = specificBreakingTorque * Mathf.Pow(br, 2); - - var model = part.FindModelTransform("model"); - if (model != null) model.localScale = Vector3.one * scale; - else Debug.LogError("[ProceduralFairingAdapter] No 'model' transform in the part", this); - part.rescaleFactor = scale; - - var node = part.FindAttachNode("top"); - node.position = node.originalPosition * scale; - if (!justLoaded) PFUtils.updateAttachedPartPos(node, part); - - var topNode = part.FindAttachNode("top"); - var bottomNode = part.FindAttachNode("bottom"); - - float y = (topNode.position.y + bottomNode.position.y) * 0.5f; - int sideNodeSize = Mathf.RoundToInt(scale / diameterStepLarge) - 1; - if (sideNodeSize < 0) sideNodeSize = 0; - - var nodes = part.FindAttachNodes("connect"); - if (nodes != null) { - for (int i = 0; i < nodes.Length; i++) { - var n = nodes[i]; - n.position.y = y; - n.size = sideNodeSize; - if (!justLoaded) PFUtils.updateAttachedPartPos(n, part); - } - } - - var topnode2 = part.FindAttachNode(topNodeName); - var internodes = part.FindAttachNodes("interstage"); - if (internodes != null && topnode2 != null) { - var topheight = topNode.position.y; - var topnode2height = topnode2.position.y; - var inc = (topnode2height - topheight) / (internodes.Length / 2 + 1); - - for (int i = 0, j = 0; i < internodes.Length; i = i + 2) { - var height = topheight + (j + 1) * inc; - j++; - - node = internodes[i]; - node.position.y = height; - node.size = topNode.size; - if (!justLoaded) PFUtils.updateAttachedPartPos(node, part); - - node = internodes[i + 1]; - node.position.y = height; - node.size = sideNodeSize; - if (!justLoaded) PFUtils.updateAttachedPartPos(node, part); - } - } - - var nnt = part.GetComponent(); - if (nnt) { - nnt.radius = baseSize * 0.5f; - } - - var fbase = part.GetComponent(); - if (fbase) { - fbase.baseSize = br * 2; - fbase.sideThickness = sth; - fbase.needShapeUpdate = true; - } - - StartCoroutine(PFUtils.updateDragCubeCoroutine(part, dragAreaScale)); - } - - - public override void FixedUpdate() - { - base.FixedUpdate(); - - if (!limitsSet && PFUtils.canCheckTech()) { - limitsSet = true; - float minSize = PFUtils.getTechMinValue("PROCFAIRINGS_MINDIAMETER", 0.25f); - float maxSize = PFUtils.getTechMaxValue("PROCFAIRINGS_MAXDIAMETER", 30); - - PFUtils.setFieldRange(Fields["baseSize"], minSize, maxSize); - PFUtils.setFieldRange(Fields["topSize"], minSize, maxSize); - - ((UI_FloatEdit)Fields["baseSize"].uiControlEditor).incrementLarge = diameterStepLarge; - ((UI_FloatEdit)Fields["baseSize"].uiControlEditor).incrementSmall = diameterStepSmall; - ((UI_FloatEdit)Fields["topSize"].uiControlEditor).incrementLarge = diameterStepLarge; - ((UI_FloatEdit)Fields["topSize"].uiControlEditor).incrementSmall = diameterStepSmall; - - ((UI_FloatEdit)Fields["height"].uiControlEditor).incrementLarge = heightStepLarge; - ((UI_FloatEdit)Fields["height"].uiControlEditor).incrementSmall = heightStepSmall; - ((UI_FloatEdit)Fields["extraHeight"].uiControlEditor).incrementLarge = heightStepLarge; - ((UI_FloatEdit)Fields["extraHeight"].uiControlEditor).incrementSmall = heightStepSmall; - } - - - if (isShipModified) { - - isShipModified = false; - // remove engine fairing if there is any from topmost node - if (!engineFairingRemoved) { - var node = part.FindAttachNode(topNodeName); - if (node != null && node.attachedPart != null) { - var tp = node.attachedPart; - - if (HighLogic.LoadedSceneIsEditor || !tp.packed) { - var comps = tp.GetComponents(); - for (int i = 0; i < comps.Length; i++) { - var mj = comps[i]; - // print("[ProceduralFairingAdapter] removing engine fairings "+mj); - var jt = tp.FindModelTransform(mj.jettisonName); - if (jt == null) - jt = mj.jettisonTransform; - if (jt != null) { - // print("[ProceduralFairingAdapter] disabling engine fairing "+jt); - jt.gameObject.SetActive(false); - } - - mj.jettisonName = null; - mj.jettisonTransform = null; - - // tp.RemoveModule(mj); - } - - if (!HighLogic.LoadedSceneIsEditor) engineFairingRemoved = true; - } - } - } - - - - if (!HighLogic.LoadedSceneIsEditor) { - if (isTopNodePartPresent) { - var tp = getTopPart(); - - if (tp == null) { - isTopNodePartPresent = false; - this.Events["UIToggleTopNodeDecouple"].guiActive = false; - } - else - if (topNodeDecouplesWhenFairingsGone && !CheckForFairingPresent()) { - - PartModule item = base.part.Modules["ModuleDecouple"]; - - if (item == null) { - Debug.LogError("[ProceduralFairingAdapter] Can't decouple from top part", this); - } - else { - this.RemoveTopPartJoints(); - ((ModuleDecouple)item).Decouple(); - this.part.stackIcon.RemoveIcon(); - StageManager.Instance.SortIcons(true); - - isFairingPresent = false; - isTopNodePartPresent = false; - this.Events["UIToggleTopNodeDecouple"].guiActive = false; - } - } - } - - - if (isStaged) { - isStaged = false; - - if (this.part != null) { - if (stageNum == this.part.inverseStage) { - this.part.stackIcon.RemoveIcon(true); - StageManager.Instance.SortIcons(true); - this.Events["UIToggleTopNodeDecouple"].guiActive = false; - } - } - } - - } - } - } - - - public Part getBottomPart() - { - Part part; - AttachNode attachNode = base.part.FindAttachNode("bottom"); - if (attachNode != null) { - part = attachNode.attachedPart; - } - else { - part = null; - } - return part; - } - - public bool CheckForFairingPresent() - { - if (!isFairingPresent) - return false; - - var nodes = part.FindAttachNodes("connect"); - if (nodes == null) - return false; - - for (int i = 0; i < nodes.Length; i++) { - var n = nodes[i]; - if (n.attachedPart != null) { - return true; - } - } - - return false; - } - - - public Part getTopPart() - { - var node = part.FindAttachNode(topNodeName); - if (node == null) return null; - return node.attachedPart; - } - - - public override void OnLoad(ConfigNode cfg) - { - base.OnLoad(cfg); - - if (cfg.HasValue("baseRadius") && cfg.HasValue("topRadius")) { - // load legacy settings - float br = float.Parse(cfg.GetValue("baseRadius")); - float tr = float.Parse(cfg.GetValue("topRadius")); - baseSize = (br + sideThickness * br) * 2; - topSize = (tr + sideThickness * br) * 2; - sideThickness *= 1.15f / 1.25f; - } - } - } - - - //ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ// - - -} // namespace - diff --git a/Source/ProceduralFairings.csproj b/Source/ProceduralFairings.csproj deleted file mode 100644 index 3131469..0000000 --- a/Source/ProceduralFairings.csproj +++ /dev/null @@ -1,62 +0,0 @@ - - - - - Debug - AnyCPU - {C14C9097-E14E-4DBD-B1C1-877B6C390F27} - Library - Properties - ProceduralFairings - ProceduralFairings - v3.5 - 512 - - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - - False - C:\Users\caske\Downloads\ksp-win64-1.3\KSP_win64\KSP_x64_Data\Managed\Assembly-CSharp.dll - - - - - - - - - False - C:\Users\caske\Downloads\ksp-win64-1.3\KSP_win64\KSP_x64_Data\Managed\UnityEngine.dll - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Source/ProceduralFairings.sln b/Source/ProceduralFairings.sln index 87a00bd..917ad90 100644 --- a/Source/ProceduralFairings.sln +++ b/Source/ProceduralFairings.sln @@ -1,22 +1,25 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.26430.4 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ProceduralFairings", "ProceduralFairings.csproj", "{C14C9097-E14E-4DBD-B1C1-877B6C390F27}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {C14C9097-E14E-4DBD-B1C1-877B6C390F27}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {C14C9097-E14E-4DBD-B1C1-877B6C390F27}.Debug|Any CPU.Build.0 = Debug|Any CPU - {C14C9097-E14E-4DBD-B1C1-877B6C390F27}.Release|Any CPU.ActiveCfg = Release|Any CPU - {C14C9097-E14E-4DBD-B1C1-877B6C390F27}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2012 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ProceduralFairings", "ProceduralFairings\ProceduralFairings.csproj", "{51CE67F2-5981-4AD2-97E8-0E2B44792AB2}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {51CE67F2-5981-4AD2-97E8-0E2B44792AB2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {51CE67F2-5981-4AD2-97E8-0E2B44792AB2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {51CE67F2-5981-4AD2-97E8-0E2B44792AB2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {51CE67F2-5981-4AD2-97E8-0E2B44792AB2}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(MonoDevelopProperties) = preSolution + BaseDirectory = .. + outputpath = References + Policies = $0 + $0.DotNetNamingPolicy = $1 + $1.DirectoryNamespaceAssociation = None + $1.ResourceNamePolicy = FileFormatDefault + EndGlobalSection +EndGlobal diff --git a/Source/ProceduralFairings/FairingBase.cs b/Source/ProceduralFairings/FairingBase.cs new file mode 100644 index 0000000..5100a85 --- /dev/null +++ b/Source/ProceduralFairings/FairingBase.cs @@ -0,0 +1,1091 @@ +// ================================================== +// Procedural Fairings plug-in by Alexey Volynskov. + +// Licensed under CC-BY-4.0 terms: https://creativecommons.org/licenses/by/4.0/legalcode +// ================================================== + +using System; +using System.Collections.Generic; +using UnityEngine; + +namespace Keramzit +{ + public class ProceduralFairingBase : PartModule + { + [KSPField] public float outlineWidth = 0.05f; + [KSPField] public int outlineSlices = 12; + [KSPField] public Vector4 outlineColor = new Vector4 (0, 0, 0.2f, 1); + [KSPField] public float verticalStep = 0.1f; + [KSPField] public float baseSize = 1.25f; + + [KSPField (isPersistant = true, guiActiveEditor = true, guiName = "Extra radius")] + [UI_FloatRange (minValue = -1, maxValue = 2, stepIncrement = 0.01f)] + public float extraRadius; + + [KSPField] public int circleSegments = 24; + + [KSPField] public float sideThickness = 0.05f; + + [KSPField (isPersistant = true, guiActiveEditor = true, guiName = "Fairing Auto-struts")] + [UI_Toggle (disabledText = "Off", enabledText = "On")] + public bool autoStrutSides = true; + + [KSPField (isPersistant = true, guiActiveEditor = true, guiName = "Fairing Auto-shape")] + [UI_Toggle (disabledText = "Off", enabledText = "On")] + public bool autoShape = true; + + [KSPField (isPersistant = true, guiActiveEditor = true, guiName = "Max. size", guiFormat = "S4", guiUnits = "m")] + [UI_FloatEdit (sigFigs = 3, unit = "m", minValue = 0.1f, maxValue = 5, incrementLarge = 1.25f, incrementSmall = 0.125f, incrementSlide = 0.001f)] + public float manualMaxSize = 0.625f; + + [KSPField (isPersistant = true, guiActiveEditor = true, guiName = "Cyl. start", guiFormat = "S4", guiUnits = "m")] + [UI_FloatEdit (sigFigs = 3, unit = "m", minValue = 0, maxValue = 50, incrementLarge = 1.0f, incrementSmall = 0.1f, incrementSlide = 0.001f)] + public float manualCylStart = 0; + + [KSPField (isPersistant = true, guiActiveEditor = true, guiName = "Cyl. end", guiFormat = "S4", guiUnits = "m")] + [UI_FloatEdit (sigFigs = 3, unit = "m", minValue = 0, maxValue = 50, incrementLarge = 1.0f, incrementSmall = 0.1f, incrementSlide = 0.001f)] + public float manualCylEnd = 1; + + bool limitsSet; + + [KSPField] public float diameterStepLarge = 1.25f; + [KSPField] public float diameterStepSmall = 0.125f; + + [KSPField] public float heightStepLarge = 1.0f; + [KSPField] public float heightStepSmall = 0.1f; + + public float updateDelay; + public bool needShapeUpdate = true; + + Part topBasePart; + + float lastManualMaxSize, lastManualCylStart, lastManualCylEnd; + + LineRenderer line; + + readonly List outline = new List(); + + List joints = new List(); + + public override string GetInfo () + { + const string infoString = "Attach side fairings and they will be shaped for your attached payload.\n" + "Remember to add a decoupler if you need one."; + + return infoString; + } + + public override void OnStart (StartState state) + { + limitsSet = false; + + if (!HighLogic.LoadedSceneIsEditor && !HighLogic.LoadedSceneIsFlight) + { + return; + } + + PFUtils.hideDragStuff (part); + + GameEvents.onEditorShipModified.Add (new EventData.OnEvent (onEditorVesselModified)); + + if (HighLogic.LoadedSceneIsEditor) + { + if (line) + { + line.transform.Rotate (0, 90, 0); + } + + DestroyAllLineRenderers (); + + destroyOutline (); + + for (int i = 0; i < outlineSlices; ++i) + { + var r = makeLineRenderer ("fairing outline", outlineColor, outlineWidth); + + outline.Add (r); + + r.transform.Rotate (0, i * 360f / outlineSlices, 0); + } + + ShowHideInterstageNodes (); + + updateDelay = 0.1f; + + needShapeUpdate = true; + } + else + { + topBasePart = null; + + var adapter = part.GetComponent(); + + if (adapter) + { + topBasePart = adapter.getTopPart (); + } + else + { + var scan = scanPayload (); + + if (scan.targets.Count > 0) + { + topBasePart = scan.targets [0]; + } + } + } + + SetUIChangedCallBacks (); + } + + void SetUIChangedCallBacks () + { + ((UI_Toggle) Fields["autoShape"].uiControlEditor).onFieldChanged += UIChanged; + + ((UI_FloatEdit) Fields["manualMaxSize"].uiControlEditor).onFieldChanged += UIChanged; + ((UI_FloatEdit) Fields["manualCylStart"].uiControlEditor).onFieldChanged += UIChanged; + ((UI_FloatEdit) Fields["manualCylEnd"].uiControlEditor).onFieldChanged += UIChanged; + } + + bool uiChanged_SomeFields = true; + + void UIChanged (BaseField bf, object obj) + { + uiChanged_SomeFields = true; + } + + void onEditorVesselModified (ShipConstruct ship) + { + needShapeUpdate = true; + + ShowHideInterstageNodes (); + } + + public void ShowHideInterstageNodes () + { + var nnt = part.GetComponent(); + + if (nnt) + { + var nodes = part.FindAttachNodes ("interstage"); + + if (nodes == null) + { + return; + } + + if (nnt.showInterstageNodes) + { + for (int i = 0; i < nodes.Length; i++) + { + var node = nodes [i]; + + if (node.attachedPart == null) + { + node.position.x = 0; + } + } + } + else + { + for (int i = 0; i < nodes.Length; i++) + { + var node = nodes [i]; + + if (node.attachedPart == null) + { + node.position.x = 10000; + } + } + } + } + } + + public void removeJoints () + { + while (joints.Count > 0) + { + int i = joints.Count - 1; + + var joint = joints [i]; joints.RemoveAt (i); + + UnityEngine.Object.Destroy (joint); + } + } + + public void OnPartPack () + { + removeJoints (); + } + + ConfigurableJoint addStrut (Part p, Part pp) + { + if (p == pp) + { + return null; + } + + var rb = pp.Rigidbody; + + if (rb == null || rb == p.Rigidbody) + { + return null; + } + + var joint = p.gameObject.AddComponent(); + + joint.xMotion = ConfigurableJointMotion.Locked; + joint.yMotion = ConfigurableJointMotion.Locked; + joint.zMotion = ConfigurableJointMotion.Locked; + joint.angularXMotion = ConfigurableJointMotion.Locked; + joint.angularYMotion = ConfigurableJointMotion.Locked; + joint.angularZMotion = ConfigurableJointMotion.Locked; + joint.projectionDistance = 0.1f; + joint.projectionAngle = 5; + joint.breakForce = p.breakingForce; + joint.breakTorque = p.breakingTorque; + joint.connectedBody = rb; + + joints.Add (joint); + + return joint; + } + + IEnumerator createAutoStruts (List shieldedParts) + { + while (!FlightGlobals.ready || vessel.packed || !vessel.loaded) + { + yield return new WaitForFixedUpdate(); + } + + var nnt = part.GetComponent(); + + var attached = part.FindAttachNodes ("connect"); + + for (int i = 0; i < nnt.numNodes; ++i) + { + var p = attached [i].attachedPart; + + if (p == null || p.Rigidbody == null) + { + continue; + } + + var pp = attached [i > 0 ? i - 1 : nnt.numNodes - 1].attachedPart; + + if (pp == null) + { + continue; + } + + addStrut (p, pp); + + if (topBasePart != null) + { + addStrut (p, topBasePart); + } + } + } + + public void onShieldingDisabled(List shieldedParts) + { + removeJoints (); + } + + public void onShieldingEnabled (List shieldedParts) + { + if (!HighLogic.LoadedSceneIsFlight) + { + return; + } + + if (autoStrutSides) + { + StartCoroutine(createAutoStruts (shieldedParts)); + } + } + + public virtual void FixedUpdate () + { + if (!limitsSet && PFUtils.canCheckTech ()) + { + limitsSet = true; + + float minSize = PFUtils.getTechMinValue ("PROCFAIRINGS_MINDIAMETER", 0.25f); + float maxSize = PFUtils.getTechMaxValue ("PROCFAIRINGS_MAXDIAMETER", 30); + + PFUtils.setFieldRange (Fields["manualMaxSize"], minSize, maxSize * 2); + + ((UI_FloatEdit) Fields["manualMaxSize"].uiControlEditor).incrementLarge = diameterStepLarge; + ((UI_FloatEdit) Fields["manualMaxSize"].uiControlEditor).incrementSmall = diameterStepSmall; + + ((UI_FloatEdit) Fields["manualCylStart"].uiControlEditor).incrementLarge = heightStepLarge; + ((UI_FloatEdit) Fields["manualCylStart"].uiControlEditor).incrementSmall = heightStepSmall; + ((UI_FloatEdit) Fields["manualCylEnd"].uiControlEditor).incrementLarge = heightStepLarge; + ((UI_FloatEdit) Fields["manualCylEnd"].uiControlEditor).incrementSmall = heightStepSmall; + } + + if (!part.packed && topBasePart != null) + { + var adapter = part.GetComponent(); + + if (adapter) + { + topBasePart = adapter.getTopPart (); + + if (topBasePart == null) + { + removeJoints (); + } + } + } + } + + LineRenderer makeLineRenderer (string name, Color color, float wd) + { + var o = new GameObject(name); + + o.transform.parent = part.transform; + o.transform.localPosition = Vector3.zero; + o.transform.localRotation = Quaternion.identity; + + var r = o.AddComponent(); + + r.useWorldSpace = false; + r.material = new Material (Shader.Find ("Particles/Additive")); + + r.SetColors (color, color); + r.SetWidth (wd, wd); + r.SetVertexCount (0); + + return r; + } + + void destroyOutline () + { + for (int i = 0; i < outline.Count; i++) + { + GameObject.Destroy (outline [i].gameObject); + } + + outline.Clear (); + } + + /// + /// Fix for the blue ghost lines showing invalid outlines when cloning or symmetry-placing fairing bases in the editor. + /// Find any already assigned (copied) LineRenderers and delete them. + /// + + void DestroyAllLineRenderers () + { + LineRenderer [] lr = GameObject.FindObjectsOfType(); + + if (lr != null) + { + for (int i = 0; i < lr.Length; i++) + { + Transform _transform = lr[i].transform; + + if (_transform != null) + { + Transform _parent = _transform.parent; + + if (_parent != null) + { + GameObject _gameObject = _parent.gameObject; + + if (_gameObject) + { + if ((_gameObject.Equals (this) ? true : _gameObject.Equals (gameObject))) + { + GameObjectExtension.DestroyGameObject (lr [i].gameObject); + } + } + } + } + } + } + } + + public void OnDestroy () + { + GameEvents.onEditorShipModified.Remove (new EventData.OnEvent (onEditorVesselModified)); + + if (line) + { + GameObject.Destroy (line.gameObject); + + line = null; + } + + DestroyAllLineRenderers (); + + destroyOutline (); + } + + public void Update () + { + if (HighLogic.LoadedSceneIsEditor) + { + if (uiChanged_SomeFields) + { + uiChanged_SomeFields = false; + + if (!lastManualMaxSize.Equals (manualMaxSize)) + { + needShapeUpdate = true; + } + + if (!lastManualCylStart.Equals (manualCylStart)) + { + needShapeUpdate = true; + } + + if (!lastManualCylEnd.Equals (manualCylEnd)) + { + needShapeUpdate = true; + } + + lastManualMaxSize = manualMaxSize; + lastManualCylStart = manualCylStart; + lastManualCylEnd = manualCylEnd; + + bool old = Fields["manualMaxSize"].guiActiveEditor; + + Fields["manualMaxSize"].guiActiveEditor = !autoShape; + Fields["manualCylStart"].guiActiveEditor = !autoShape; + Fields["manualCylEnd"].guiActiveEditor = !autoShape; + + PFUtils.refreshPartWindow (); + } + + if (updateDelay > 0) + { + updateDelay -= Time.deltaTime; + } + else + { + if (needShapeUpdate) + { + needShapeUpdate = false; + + recalcShape (); + + updateDelay = 0.5f; + } + } + } + } + + static public Vector3 [] buildFairingShape (float baseRad, float maxRad, float cylStart, float cylEnd, float noseHeightRatio, Vector4 baseConeShape, Vector4 noseConeShape, int baseConeSegments, int noseConeSegments, Vector4 vertMapping, float mappingScaleY) + { + float baseConeRad = maxRad - baseRad; + float tip = maxRad * noseHeightRatio; + + var baseSlope = new BezierSlope (baseConeShape); + var noseSlope = new BezierSlope (noseConeShape); + + float baseV0 = vertMapping.x / mappingScaleY; + float baseV1 = vertMapping.y / mappingScaleY; + float noseV0 = vertMapping.z / mappingScaleY; + float noseV1 = vertMapping.w / mappingScaleY; + + var shape = new Vector3 [1 + (cylStart.Equals (0) ? 0 : baseConeSegments) + 1 + noseConeSegments]; + + int vi = 0; + + if (!cylStart.Equals (0)) + { + for (int i = 0; i <= baseConeSegments; ++i, ++vi) + { + float t = (float) i / baseConeSegments; + + var p = baseSlope.interp (t); + + shape [vi] = new Vector3 (p.x * baseConeRad + baseRad, p.y * cylStart, Mathf.Lerp (baseV0, baseV1, t)); + } + } + else + { + shape [vi++] = new Vector3 (baseRad, 0, baseV1); + } + + for (int i = 0; i <= noseConeSegments; ++i, ++vi) + { + float t = (float)i / noseConeSegments; + + var p = noseSlope.interp (1 - t); + + shape [vi] = new Vector3 (p.x * maxRad, (1 - p.y) * tip + cylEnd, Mathf.Lerp (noseV0, noseV1, t)); + } + + return shape; + } + + static public Vector3 [] buildInlineFairingShape (float baseRad, float maxRad, float topRad, float cylStart, float cylEnd, float top, Vector4 baseConeShape, int baseConeSegments, Vector4 vertMapping, float mappingScaleY) + { + float baseConeRad = maxRad - baseRad; + float topConeRad = maxRad - topRad; + + var baseSlope = new BezierSlope (baseConeShape); + + float baseV0 = vertMapping.x / mappingScaleY; + float baseV1 = vertMapping.y / mappingScaleY; + float noseV0 = vertMapping.z / mappingScaleY; + + var shape = new Vector3 [2 + (cylStart.Equals (0) ? 0 : baseConeSegments + 1) + (cylEnd == top ? 0 : baseConeSegments + 1)]; + + int vi = 0; + + if (!cylStart.Equals(0)) + { + for (int i = 0; i <= baseConeSegments; ++i, ++vi) + { + float t = (float) i / baseConeSegments; + + var p = baseSlope.interp (t); + + shape [vi] = new Vector3(p.x * baseConeRad + baseRad, p.y * cylStart, Mathf.Lerp (baseV0, baseV1, t)); + } + } + + shape [vi++] = new Vector3 (maxRad, cylStart, baseV1); + shape [vi++] = new Vector3 (maxRad, cylEnd, noseV0); + + if (!cylEnd.Equals (top)) + { + for (int i = 0; i <= baseConeSegments; ++i, ++vi) + { + float t = (float) i / baseConeSegments; + + var p = baseSlope.interp (1 - t); + + shape [vi] = new Vector3 (p.x * topConeRad + topRad, Mathf.Lerp (top, cylEnd, p.y), Mathf.Lerp (baseV1, baseV0, t)); + } + } + + return shape; + } + + PayloadScan scanPayload () + { + // Scan the payload and build it's profile. + + var scan = new PayloadScan (part, verticalStep, extraRadius); + + AttachNode node = part.FindAttachNode ("top"); + + if (node != null) + { + scan.ofs = node.position.y; + + if (node.attachedPart != null) + { + scan.addPart (node.attachedPart, part); + } + } + + AttachNode [] nodes = part.FindAttachNodes ("interstage"); + + if (nodes != null) + { + for (int j = 0; j < nodes.Length; j++) + { + node = nodes [j]; + + if (node != null) + { + if (node.attachedPart != null) + { + scan.addPart (node.attachedPart, part); + } + } + } + } + + for (int i = 0; i < scan.payload.Count; ++i) + { + var cp = scan.payload [i]; + + // Add any connected payload parts. + + scan.addPart (cp.parent, cp); + + for (int j = 0; j < cp.children.Count; j++) + { + scan.addPart(cp.children[j], cp); + } + + // Scan for the part colliders. + + var colls = cp.FindModelComponents(); + + for (int j = 0; j < colls.Count; j++) + { + var coll = colls [j]; + + // Skip ladders etc... + + if (coll.tag != "Untagged") + { + continue; + } + + scan.addPayload (coll); + } + } + + return scan; + } + + AttachNode HasNodeComponent (AttachNode [] nodes) + { + if (nodes != null) + { + for (int i = 0; i < nodes.Length; i++) + { + var part = nodes [i].attachedPart; + + if (part == null) + { + continue; + } + + var comp = part.GetComponent(); + + if (comp != null) + { + return nodes [i]; + } + } + } + + return null; + } + + void recalcShape () + { + var scan = scanPayload (); + + // Check for reversed bases (inline fairings). + + float topY = 0; + float topRad = 0; + + AttachNode topSideNode = null; + + bool isInline = false; + + var adapter = part.GetComponent(); + + if (adapter) + { + isInline = true; + + topY = adapter.height + adapter.extraHeight; + + if (topY < scan.ofs) + { + topY = scan.ofs; + } + + topRad = adapter.topRadius; + } + else if (scan.targets.Count > 0) + { + isInline = true; + + var topBase = scan.targets [0].GetComponent(); + + topY = scan.w2l.MultiplyPoint3x4 (topBase.part.transform.position).y; + + if (topY < scan.ofs) + { + topY = scan.ofs; + } + + topSideNode = HasNodeComponent(topBase.part.FindAttachNodes ("connect")); + + topRad = topBase.baseSize * 0.5f; + } + + // No payload case. + + if (scan.profile.Count <= 0) + { + scan.profile.Add (extraRadius); + } + + // Fill profile outline (for debugging). + + if (line) + { + line.SetVertexCount (scan.profile.Count * 2 + 2); + + float prevRad = 0; + + int hi = 0; + + for (int i = 0; i < scan.profile.Count; i++) + { + var r = scan.profile [i]; + + line.SetPosition (hi * 2, new Vector3 (prevRad, hi * verticalStep + scan.ofs, 0)); + line.SetPosition (hi * 2 + 1, new Vector3 (r, hi * verticalStep + scan.ofs, 0)); + + hi++; prevRad = r; + } + + line.SetPosition (hi * 2, new Vector3 (prevRad, hi * verticalStep + scan.ofs, 0)); + line.SetPosition (hi * 2 + 1, new Vector3 (0, hi * verticalStep + scan.ofs, 0)); + } + + // Check attached side parts and get parameters. + + var attached = part.FindAttachNodes ("connect"); + + // Get number of available nodes from NodeNumberTweaker. + + var nnt = part.GetComponent(); + + int numSideParts = nnt.numNodes; + + var sideNode = HasNodeComponent(attached); + + float noseHeightRatio = 2; + float minBaseConeAngle = 20; + + var baseConeShape = new Vector4 (0.5f, 0, 1, 0.5f); + var noseConeShape = new Vector4 (0.5f, 0, 1, 0.5f); + + int baseConeSegments = 1; + int noseConeSegments = 1; + + var mappingScale = new Vector2 (1024, 1024); + var stripMapping = new Vector2 (992, 1024); + var horMapping = new Vector4 (0, 480, 512, 992); + var vertMapping = new Vector4 (0, 160, 704, 1024); + + if (sideNode != null) + { + var sf = sideNode.attachedPart.GetComponent(); + + noseHeightRatio = sf.noseHeightRatio; + minBaseConeAngle = sf.minBaseConeAngle; + baseConeShape = sf.baseConeShape; + noseConeShape = sf.noseConeShape; + baseConeSegments = sf.baseConeSegments; + noseConeSegments = sf.noseConeSegments; + mappingScale = sf.mappingScale; + stripMapping = sf.stripMapping; + horMapping = sf.horMapping; + vertMapping = sf.vertMapping; + } + + // Compute the fairing shape. + + float baseRad = baseSize * 0.5f; + float minBaseConeTan = Mathf.Tan (minBaseConeAngle * Mathf.Deg2Rad); + + float cylStart = 0; + float maxRad; + + int profTop = scan.profile.Count; + + if (isInline) + { + profTop = Mathf.CeilToInt ((topY - scan.ofs) / verticalStep); + + if (profTop > scan.profile.Count) + { + profTop = scan.profile.Count; + } + + maxRad = 0; + + for (int i = 0; i < profTop; ++i) + { + maxRad = Mathf.Max (maxRad, scan.profile [i]); + } + + maxRad = Mathf.Max (maxRad, topRad); + } + else + { + maxRad = PFUtils.GetMaxValueFromList (scan.profile); + } + + if (maxRad > baseRad) + { + // Try to fit the base cone as high as possible. + + cylStart = scan.ofs; + + for (int i = 1; i < scan.profile.Count; ++i) + { + float y = i * verticalStep + scan.ofs; + float r0 = baseRad; + float k = (maxRad - r0) / y; + + if (k < minBaseConeTan) + { + break; + } + + bool ok = true; + + float r = r0 + k * scan.ofs; + + for (int j = 0; j < i; ++j, r += k * verticalStep) + { + if (scan.profile [j] > r) + { + ok = false; + + break; + } + } + + if (!ok) + { + break; + } + + cylStart = y; + } + } + else + { + // No base cone, just a cylinder and a nose. + + maxRad = baseRad; + } + + float cylEnd = scan.profile.Count * verticalStep + scan.ofs; + + if (isInline) + { + float r0 = topRad; + + if (profTop > 0 && profTop < scan.profile.Count) + { + r0 = Mathf.Max (r0, scan.profile [profTop - 1]); + + if (profTop - 2 >= 0) r0 = Mathf.Max (r0, scan.profile [profTop - 2]); + } + + if (maxRad > r0) + { + if (cylEnd > topY) + { + cylEnd = topY - verticalStep; + } + + // Try to fit the top cone as low as possible. + + for (int i = profTop - 1; i >= 0; --i) + { + float y = i * verticalStep + scan.ofs; + float k = (maxRad - r0) / (y - topY); + + bool ok = true; + + float r = maxRad + k * verticalStep; + + for (int j = i; j < profTop; ++j, r += k * verticalStep) + { + if (r < r0) + { + r = r0; + } + + if (scan.profile [j] > r) + { + ok = false; + + break; + } + } + + if (!ok) + { + break; + } + + cylEnd = y; + } + } + else + { + cylEnd = topY; + } + } + else + { + // Try to fit the nose cone as low as possible. + + for (int i = scan.profile.Count - 1; i >= 0; --i) + { + float s = verticalStep / noseHeightRatio; + + bool ok = true; + + float r = maxRad - s; + + for (int j = i; j < scan.profile.Count; ++j, r -= s) + { + if (scan.profile [j] > r) + { + ok = false; + + break; + } + } + + if (!ok) break; + + float y = i * verticalStep + scan.ofs; + + cylEnd = y; + } + } + + if (autoShape) + { + manualMaxSize = maxRad * 2; + manualCylStart = cylStart; + manualCylEnd = cylEnd; + } + else + { + maxRad = manualMaxSize * 0.5f; + cylStart = manualCylStart; + cylEnd = manualCylEnd; + } + + if (cylStart > cylEnd) + { + cylStart = cylEnd; + } + + // Build the fairing shape line. + + Vector3 [] shape; + + if (isInline) + { + shape = buildInlineFairingShape (baseRad, maxRad, topRad, cylStart, cylEnd, topY, baseConeShape, baseConeSegments, vertMapping, mappingScale.y); + } + else + { + shape = buildFairingShape (baseRad, maxRad, cylStart, cylEnd, noseHeightRatio, baseConeShape, noseConeShape, baseConeSegments, noseConeSegments, vertMapping, mappingScale.y); + } + + if (sideNode == null && topSideNode == null) + { + // No side parts - fill fairing outlines. + + for (int j = 0; j < outline.Count; j++) + { + var lr = outline [j]; + + lr.SetVertexCount (shape.Length); + + for (int i = 0; i < shape.Length; ++i) + { + lr.SetPosition (i, new Vector3 (shape [i].x, shape [i].y)); + } + } + } + else + { + for (int j = 0; j < outline.Count; j++) + { + var lr = outline [j]; + + lr.SetVertexCount (0); + } + } + + // Rebuild the side parts. + + int numSegs = circleSegments / numSideParts; + + if (numSegs < 2) numSegs = 2; + + for (int i = 0; i < attached.Length; i++) + { + var sn = attached [i]; + var sp = sn.attachedPart; + + if (!sp) + { + continue; + } + + var sf = sp.GetComponent(); + + if (!sf) + { + continue; + } + + if (sf.shapeLock) + { + continue; + } + + var mf = sp.FindModelComponent("model"); + + if (!mf) + { + Debug.LogError ("[PF]: No model in side fairing!", sp); + + continue; + } + + var nodePos = sn.position; + + mf.transform.position = part.transform.position; + mf.transform.rotation = part.transform.rotation; + + float ra = Mathf.Atan2 (-nodePos.z, nodePos.x) * Mathf.Rad2Deg; + + mf.transform.Rotate (0, ra, 0); + + if (sf.meshPos == mf.transform.localPosition + && sf.meshRot == mf.transform.localRotation + && sf.numSegs == numSegs + && sf.numSideParts == numSideParts + && sf.baseRad.Equals (baseRad) + && sf.maxRad.Equals (maxRad) + && sf.cylStart.Equals (cylStart) + && sf.cylEnd.Equals (cylEnd) + && sf.topRad.Equals (topRad) + && sf.inlineHeight.Equals (topY) + && sf.sideThickness.Equals (sideThickness)) + continue; + + sf.meshPos = mf.transform.localPosition; + sf.meshRot = mf.transform.localRotation; + sf.numSegs = numSegs; + sf.numSideParts = numSideParts; + sf.baseRad = baseRad; + sf.maxRad = maxRad; + sf.cylStart = cylStart; + sf.cylEnd = cylEnd; + sf.topRad = topRad; + sf.inlineHeight = topY; + sf.sideThickness = sideThickness; + + sf.rebuildMesh (); + } + + var shielding = part.GetComponent(); + + if (shielding) + { + shielding.reset (); + } + } + } +} diff --git a/Source/ProceduralFairings/FairingDecoupler.cs b/Source/ProceduralFairings/FairingDecoupler.cs new file mode 100644 index 0000000..430222b --- /dev/null +++ b/Source/ProceduralFairings/FairingDecoupler.cs @@ -0,0 +1,229 @@ +// ================================================== +// Procedural Fairings plug-in by Alexey Volynskov. + +// Licensed under CC-BY-4.0 terms: https://creativecommons.org/licenses/by/4.0/legalcode +// ================================================== + +using KSP.UI.Screens; +using System; +using UnityEngine; + +namespace Keramzit +{ + public class ProceduralFairingDecoupler : PartModule + { + [KSPField] public float ejectionDv = 15; + [KSPField] public float ejectionTorque = 10; + [KSPField] public float ejectionLowDv; + [KSPField] public float ejectionLowTorque; + + bool decoupled; + bool didForce; + + public bool updateFightUICheck = true; + public bool updateEditorUICheck = true; + + [KSPField] public string ejectSoundUrl = "Squad/Sounds/sound_decoupler_fire"; + public FXGroup ejectFx; + + [KSPField] public string transformName = "nose_collider"; + [KSPField] public Vector3 forceVector = Vector3.right; + [KSPField] public Vector3 torqueVector = -Vector3.forward; + + [KSPField (isPersistant = true, guiActiveEditor = true, guiName = "Ejection power")] + [UI_FloatRange (minValue = 0, maxValue = 1, stepIncrement = 0.01f)] + public float ejectionPower = 0.32f; + + [KSPField (isPersistant = true, guiActiveEditor = true, guiName = "Ejection torque")] + [UI_FloatRange (minValue = 0, maxValue = 1, stepIncrement = 0.01f)] + public float torqueAmount = 0.01f; + + [KSPField (isPersistant = true, guiActiveEditor = true, guiName = "Fairing Decoupler")] + [UI_Toggle (disabledText = "Off", enabledText = "On")] + public bool fairingStaged = true; + + [KSPEvent (name = "Jettison", active = true, guiActive = true, guiActiveEditor = false, guiActiveUnfocused = false, guiName = "Jettison Fairing")] + public void OnJettisonFairing () + { + decoupled = fairingStaged; + + OnSetFairingStaging (fairingStaged); + } + + public void FixedUpdate () + { + // Set the staging icon visibility (editor only). + + if (HighLogic.LoadedSceneIsEditor) + { + if (updateEditorUICheck.Equals (true)) + { + if (fairingStaged.Equals (true)) + { + part.stackIcon.CreateIcon (); + } + else + { + part.stackIcon.RemoveIcon (); + } + + // Reorder the staging icons. + + StageManager.Instance.SortIcons (true); + + // Tag as done. + + updateEditorUICheck = false; + } + } + + // Set the staging icon visibility (flight only). + + if (HighLogic.LoadedSceneIsFlight) + { + if (updateFightUICheck.Equals (true)) + { + if (fairingStaged.Equals (true)) + { + part.stackIcon.CreateIcon (); + + } + else + { + part.stackIcon.RemoveIcon (); + } + + // Set the state of the "Jettison" button. + + OnSetFairingStaging (fairingStaged); + + // Reorder the staging icons. + + StageManager.Instance.SortIcons (true); + + // Tag as done. + + updateFightUICheck = false; + } + } + + // Do the decoupling. + + if (decoupled.Equals (true) && fairingStaged.Equals (true)) + { + if (part.parent) + { + var pfa = part.parent.GetComponent(); + + for (int i = 0; i < part.parent.children.Count; i++) + { + var p = part.parent.children [i]; + + // Check if the top node allows decoupling when the fairing is also decoupled. + + if (pfa) + { + if (!pfa.topNodeDecouplesWhenFairingsGone) + { + var isFairing = p.GetComponent(); + + if (!isFairing) + { + continue; + } + } + } + + var joints = p.GetComponents(); + + for (int j = 0; j < joints.Length; j++) + { + var joint = joints [j]; + + if (joint != null && (joint.GetComponent() == part.Rigidbody || joint.connectedBody == part.Rigidbody)) + { + Destroy (joint); + } + } + } + + part.decouple (0); + + ejectFx.audio.Play (); + } + else if (!didForce) + { + var tr = part.FindModelTransform (transformName); + + if (tr) + { + part.Rigidbody.AddForce (tr.TransformDirection (forceVector) * Mathf.Lerp (ejectionLowDv, ejectionDv, ejectionPower), ForceMode.VelocityChange); + part.Rigidbody.AddTorque (tr.TransformDirection (torqueVector) * Mathf.Lerp (ejectionLowTorque, ejectionTorque, torqueAmount), ForceMode.VelocityChange); + } + else + { + Debug.LogError ("[PF]: No '" + transformName + "' transform in part!", part); + } + + didForce = true; + decoupled = false; + } + } + } + + public override void OnActive () + { + OnJettisonFairing (); + } + + public override void OnLoad (ConfigNode node) + { + base.OnLoad (node); + + didForce = decoupled; + } + + void OnSetFairingStaging (bool bFairingStaged) + { + Events["OnJettisonFairing"].guiActive = bFairingStaged; + } + + public override void OnStart (StartState state) + { + if (state == StartState.None) + { + return; + } + + ejectFx.audio = part.gameObject.AddComponent(); + ejectFx.audio.volume = GameSettings.SHIP_VOLUME; + ejectFx.audio.rolloffMode = AudioRolloffMode.Logarithmic; + ejectFx.audio.maxDistance = 100; + ejectFx.audio.loop = false; + ejectFx.audio.playOnAwake = false; + + if (GameDatabase.Instance.ExistsAudioClip (ejectSoundUrl)) + { + ejectFx.audio.clip = GameDatabase.Instance.GetAudioClip (ejectSoundUrl); + } + else + { + Debug.LogError ("[PF]: Cannot find decoupler sound: " + ejectSoundUrl, this); + } + + // Set up the GUI update callbacks. + + OnUpdateFairingSideUI (); + } + + void OnUpdateFairingSideUI () + { + ((UI_Toggle) Fields["fairingStaged"].uiControlEditor).onFieldChanged += OnUpdateUI; + } + + void OnUpdateUI (BaseField bf, object obj) + { + updateEditorUICheck = true; + } + } +} diff --git a/Source/ProceduralFairings/FairingShielding.cs b/Source/ProceduralFairings/FairingShielding.cs new file mode 100644 index 0000000..9047314 --- /dev/null +++ b/Source/ProceduralFairings/FairingShielding.cs @@ -0,0 +1,482 @@ +// ================================================== +// Procedural Fairings plug-in by Alexey Volynskov. + +// Licensed under CC-BY-4.0 terms: https://creativecommons.org/licenses/by/4.0/legalcode +// ================================================== + +using System; +using System.Collections.Generic; +using UnityEngine; + +namespace Keramzit +{ + public class KzFairingBaseShielding : PartModule, IAirstreamShield + { + List shieldedParts; + + ProceduralFairingSide sideFairing; + + float boundCylY0, boundCylY1, boundCylRad; + float lookupRad; + + Vector3 lookupCenter; + Vector3 [] shape; + + [KSPField (isPersistant = false, guiActive = true, guiActiveEditor = true, guiName = "Parts shielded")] + public int numShieldedDisplay; + + bool needReset; + + public bool ClosedAndLocked () { return true; } + public Vessel GetVessel () { return vessel; } + public Part GetPart () { return part; } + + public override void OnAwake () + { + shieldedParts = new List(); + } + + public override void OnStart (StartState state) + { + if (!HighLogic.LoadedSceneIsEditor && !HighLogic.LoadedSceneIsFlight) + { + return; + } + + reset (); + + GameEvents.onVesselWasModified.Add (new EventData.OnEvent (onVesselModified)); + GameEvents.onVesselGoOffRails.Add (new EventData.OnEvent (onVesselUnpack)); + GameEvents.onPartDie.Add (new EventData.OnEvent (OnPartDestroyed)); + } + + void OnDestroy () + { + GameEvents.onVesselWasModified.Remove (new EventData.OnEvent (onVesselModified)); + GameEvents.onVesselGoOffRails.Remove (new EventData.OnEvent (onVesselUnpack)); + GameEvents.onPartDie.Remove (new EventData.OnEvent (OnPartDestroyed)); + } + + public void FixedUpdate () + { + if (needReset) + { + needReset = false; + + getFairingParams (); + + bool shield = (HighLogic.LoadedSceneIsEditor || (HighLogic.LoadedSceneIsFlight && !vessel.packed)); + + if (shield) + { + enableShielding (); + } + } + } + + public void reset () + { + needReset = true; + } + + AttachNode [] getFairingParams () + { + var nnt = part.GetComponent(); + + // Check for attached side parts and get their parameters. + + var attached = part.FindAttachNodes ("connect"); + + ProceduralFairingSide sf = null; + + for (int i = 0; i < nnt.numNodes; ++i) + { + var n = attached [i]; + + if (!n.attachedPart) + { + sf = null; + + break; + } + + sf = n.attachedPart.GetComponent(); + + if (!sf) + { + break; + } + } + + sideFairing = sf; + + if (!sf) + { + shape = null; + boundCylY0 = boundCylY1 = boundCylRad = 0; + lookupCenter = Vector3.zero; + lookupRad = 0; + + return null; + } + + // Get the polyline shape. + + if (sf.inlineHeight <= 0) + { + shape = ProceduralFairingBase.buildFairingShape (sf.baseRad, sf.maxRad, sf.cylStart, sf.cylEnd, sf.noseHeightRatio, sf.baseConeShape, sf.noseConeShape, sf.baseConeSegments, sf.noseConeSegments, sf.vertMapping, sf.mappingScale.y); + } + else + { + shape = ProceduralFairingBase.buildInlineFairingShape (sf.baseRad, sf.maxRad, sf.topRad, sf.cylStart, sf.cylEnd, sf.inlineHeight, sf.baseConeShape, sf.baseConeSegments, sf.vertMapping, sf.mappingScale.y); + } + + // Offset shape by thickness. + + for (int i = 0; i < shape.Length; ++i) + { + if (i == 0 || i == shape.Length - 1) + { + shape [i] += new Vector3 (sf.sideThickness, 0, 0); + } + else + { + Vector2 n = shape [i + 1] - shape [i - 1]; + + n.Set (n.y, -n.x); + + n.Normalize (); + + shape [i] += new Vector3 (n.x, n.y, 0) * sf.sideThickness; + } + } + + // Compute the bounds. + + float y0, y1, mr; + + y0 = y1 = shape [0].y; + mr = shape [0].x; + + for (int i = 0; i < shape.Length; ++i) + { + var p = shape [i]; + + if (p.x > mr) + { + mr = p.x; + } + + if (p.y < y0) + { + y0 = p.y; + } + else if (p.y > y1) + { + y1 = p.y; + } + } + + boundCylY0 = y0; + boundCylY1 = y1; + boundCylRad = mr; + + lookupCenter = new Vector3 (0, (y0 + y1) * 0.5f, 0); + lookupRad = new Vector2 (mr, (y1 - y0) * 0.5f).magnitude; + + return attached; + } + + void enableShielding () + { + disableShielding (); + + var attached = getFairingParams (); + + if (!sideFairing) + { + return; + } + + // Get all parts in range. + + var parts = new List(); + + var colliders = Physics.OverlapSphere (part.transform.TransformPoint (lookupCenter), lookupRad, 1); + + for (int i = colliders.Length - 1; i >= 0; --i) + { + var p = colliders [i].gameObject.GetComponentUpwards(); + + if (p != null) + { + parts.AddUnique (p); + } + } + + // Filter parts. + + float sizeSqr = lookupRad * lookupRad * 4; + float boundCylRadSq = boundCylRad * boundCylRad; + + bool isInline = (sideFairing.inlineHeight > 0); + bool topClosed = false; + + Matrix4x4 w2l = Matrix4x4.identity, w2lb = Matrix4x4.identity; + + Bounds topBounds = default (Bounds); + + if (isInline) + { + w2l = part.transform.worldToLocalMatrix; + w2lb = w2l; + + for (int i = 0; i < 3; ++i) + { + for (int j = 0; j < 3; ++j) + { + w2lb [i, j] = Mathf.Abs (w2lb [i, j]); + } + } + + topBounds = new Bounds (new Vector3 (0, boundCylY1, 0), new Vector3 (sideFairing.topRad * 2, sideFairing.sideThickness, sideFairing.topRad * 2)); + } + + for (int pi = 0; pi < parts.Count; ++pi) + { + var pt = parts [pi]; + + // Check special cases. + + if (pt == part) + { + shieldedParts.Add (pt); + + continue; + } + + bool isSide = false; + + for (int i = 0; i < attached.Length; ++i) + { + if (attached [i].attachedPart == pt) + { + isSide = true; + + break; + } + } + + if (isSide) + { + continue; + } + + // Check if the top is closed in the inline case. + + var bounds = pt.GetRendererBounds (); + + var box = PartGeometryUtil.MergeBounds (bounds, pt.transform); + + if (isInline && !topClosed && pt.vessel == vessel) + { + var wb = box; wb.Expand (sideFairing.sideThickness * 4); + + var b = new Bounds (w2l.MultiplyPoint3x4(wb.center), w2lb.MultiplyVector (wb.size)); + + if (b.Contains (topBounds.min) && b.Contains (topBounds.max)) + { + topClosed = true; + } + } + + // Check if the centroid is within the fairing bounds. + + var c = part.transform.InverseTransformPoint (PartGeometryUtil.FindBoundsCentroid (bounds, null)); + + float y = c.y; + + if (y < boundCylY0 || y > boundCylY1) + { + continue; + } + + float xsq = new Vector2 (c.x, c.z).sqrMagnitude; + + if (xsq > boundCylRadSq) + { + continue; + } + + // Accurate centroid check. + + float x = Mathf.Sqrt (xsq); + + bool inside = false; + + for (int i = 1; i < shape.Length; ++i) + { + var p0 = shape [i - 1]; + var p1 = shape [i]; + + if (p0.y > p1.y) + { + var p = p0; + + p0 = p1; + p1 = p; + } + + if (y < p0.y || y > p1.y) + { + continue; + } + + float dy = p1.y - p0.y, r; + + if (dy <= 1e-6f) + { + r = (p0.x + p1.x) * 0.5f; + } + else + { + r = (p1.x - p0.x) * (y - p0.y) / dy + p0.x; + } + + if (x > r) + { + continue; + } + + inside = true; + + break; + } + + if (!inside) + { + continue; + } + + shieldedParts.Add (pt); + } + + if (isInline && !topClosed) + { + disableShielding (); + + return; + } + + // Add shielding. + + for (int i = 0; i < shieldedParts.Count; ++i) + { + shieldedParts [i].AddShield (this); + } + + numShieldedDisplay = shieldedParts.Count; + + var fbase = part.GetComponent(); + + if (fbase != null) + { + fbase.onShieldingEnabled (shieldedParts); + } + } + + void disableShielding () + { + if (shieldedParts != null) + { + var fbase = part.GetComponent(); + + if (fbase != null) + { + fbase.onShieldingDisabled (shieldedParts); + } + + for (int i = shieldedParts.Count - 1; i >= 0; --i) + { + if (shieldedParts [i] != null) + { + shieldedParts [i].RemoveShield (this); + } + } + + shieldedParts.Clear (); + } + + numShieldedDisplay = 0; + } + + void onVesselModified (Vessel v) + { + if (v != vessel) + { + var dp = v.vesselTransform.position - part.transform.TransformPoint (lookupCenter); + + if (dp.sqrMagnitude > lookupRad * lookupRad) + { + return; + } + } + + enableShielding (); + } + + void onVesselUnpack (Vessel v) + { + if (v == vessel) + { + enableShielding (); + } + } + + void onVesselPack (Vessel v) + { + if (v == vessel) + { + disableShielding (); + } + } + + void OnPartDestroyed (Part p) + { + var nnt = part.GetComponent(); + + if (p == part) + { + disableShielding (); + + return; + } + + // Check for attached side fairing parts. + + var attached = part.FindAttachNodes ("connect"); + + for (int i = 0; i < nnt.numNodes; ++i) + { + if (p == attached [i].attachedPart) + { + disableShielding (); + + return; + } + } + + // Check for top parts in the inline/adapter case. + + if (p.vessel == vessel && sideFairing && sideFairing.inlineHeight > 0) + { + enableShielding (); + } + } + + public void OnPartPack () + { + disableShielding (); + } + } +} diff --git a/Source/ProceduralFairings/FairingSide.cs b/Source/ProceduralFairings/FairingSide.cs new file mode 100644 index 0000000..0484c02 --- /dev/null +++ b/Source/ProceduralFairings/FairingSide.cs @@ -0,0 +1,730 @@ +// ================================================== +// Procedural Fairings plug-in by Alexey Volynskov. + +// Licensed under CC-BY-4.0 terms: https://creativecommons.org/licenses/by/4.0/legalcode +// ================================================== + +using System; +using System.Collections.Generic; +using UnityEngine; + +namespace Keramzit +{ + public class ProceduralFairingSide : PartModule, IPartCostModifier, IPartMassModifier + { + [KSPField] public float minBaseConeAngle = 20; + [KSPField] public Vector4 baseConeShape = new Vector4 (0, 0, 0, 0); + [KSPField] public Vector4 noseConeShape = new Vector4 (0, 0, 0, 0); + [KSPField] public int baseConeSegments = 5; + [KSPField] public int noseConeSegments = 7; + + [KSPField] public Vector2 mappingScale = new Vector2 (1024, 1024); + [KSPField] public Vector2 stripMapping = new Vector2 (992, 1024); + [KSPField] public Vector4 horMapping = new Vector4 (0, 480, 512, 992); + [KSPField] public Vector4 vertMapping = new Vector4 (0, 160, 704, 1024); + + [KSPField] public float costPerTonne = 2000; + [KSPField] public float specificBreakingForce = 2000; + [KSPField] public float specificBreakingTorque = 2000; + + [KSPField (isPersistant = true)] public int numSegs = 12; + [KSPField (isPersistant = true)] public int numSideParts = 2; + [KSPField (isPersistant = true)] public float baseRad; + [KSPField (isPersistant = true)] public float maxRad = 1.50f; + [KSPField (isPersistant = true)] public float cylStart = 0.5f; + [KSPField (isPersistant = true)] public float cylEnd = 2.5f; + [KSPField (isPersistant = true)] public float topRad; + [KSPField (isPersistant = true)] public float inlineHeight; + [KSPField (isPersistant = true)] public float sideThickness = 0.05f; + [KSPField (isPersistant = true)] public Vector3 meshPos = Vector3.zero; + [KSPField (isPersistant = true)] public Quaternion meshRot = Quaternion.identity; + + [KSPField (isPersistant = true, guiActiveEditor = true, guiActive = false, guiName = "Nose Curve Point A", guiFormat = "S4")] + [UI_FloatEdit (sigFigs = 2, minValue = 0.0f, maxValue = 1.0f, incrementLarge = 0.1f, incrementSmall = 0.01f, incrementSlide = 0.01f)] + public float noseCurveStartX = 0.5f; + + [KSPField (isPersistant = true, guiActiveEditor = true, guiActive = false, guiName = "Nose Curve Point B", guiFormat = "S4")] + [UI_FloatEdit (sigFigs = 2, minValue = 0.0f, maxValue = 1.0f, incrementLarge = 0.1f, incrementSmall = 0.01f, incrementSlide = 0.01f)] + public float noseCurveStartY = 0.0f; + + [KSPField (isPersistant = true, guiActiveEditor = true, guiActive = false, guiName = "Nose Curve Point C", guiFormat = "S4")] + [UI_FloatEdit (sigFigs = 2, minValue = 0.0f, maxValue = 1.0f, incrementLarge = 0.1f, incrementSmall = 0.01f, incrementSlide = 0.01f)] + public float noseCurveEndX = 1.0f; + + [KSPField (isPersistant = true, guiActiveEditor = true, guiActive = false, guiName = "Nose Curve Point D", guiFormat = "S4")] + [UI_FloatEdit (sigFigs = 2, minValue = 0.0f, maxValue = 1.0f, incrementLarge = 0.1f, incrementSmall = 0.01f, incrementSlide = 0.01f)] + public float noseCurveEndY = 0.5f; + + [KSPField (isPersistant = true, guiActiveEditor = true, guiActive = false, guiName = "Nose-height Ratio", guiFormat = "S4")] + [UI_FloatEdit (sigFigs = 2, minValue = 0.1f, maxValue = 5.0f, incrementLarge = 1.0f, incrementSmall = 0.1f, incrementSlide = 0.01f)] + public float noseHeightRatio = 2.0f; + + [KSPField (isPersistant = true, guiActiveEditor = true, guiName = "Density")] + [UI_FloatRange (minValue = 0.1f, maxValue = 1.0f, stepIncrement = 0.01f)] + public float density = 0.2f; + + [KSPField (isPersistant = true, guiActiveEditor = true, guiName = "Shape")] + [UI_Toggle (disabledText = "Unlocked", enabledText = "Locked")] + public bool shapeLock; + + [KSPField (isPersistant = false, guiActive = false, guiActiveEditor = true, guiName = "Mass")] + public string massDisplay; + + [KSPField (isPersistant = false, guiActive = false, guiActiveEditor = true, guiName = "Cost")] + public string costDisplay; + + public ModifierChangeWhen GetModuleCostChangeWhen () { return ModifierChangeWhen.FIXED; } + public ModifierChangeWhen GetModuleMassChangeWhen () { return ModifierChangeWhen.FIXED; } + + public bool updateUICheck; + + public float totalMass; + + public float GetModuleCost (float defcost, ModifierStagingSituation sit) + { + return totalMass * costPerTonne - defcost; + } + + public float GetModuleMass (float defmass, ModifierStagingSituation sit) + { + return totalMass - defmass; + } + + public override string GetInfo () + { + const string infoString = "Attach to a procedural fairing base to reshape. Right-click it to set it's parameters."; + + return infoString; + } + + public void Start () + { + part.mass = totalMass; + } + + public override void OnStart (StartState state) + { + if (state == StartState.None) + { + return; + } + + if (state != StartState.Editor || shapeLock) + { + rebuildMesh (); + } + + // Set the initial fairing side nose curve values from the part config. + + noseCurveStartX = noseConeShape.x; + noseCurveStartY = noseConeShape.y; + noseCurveEndX = noseConeShape.z; + noseCurveEndY = noseConeShape.w; + + // Set the initial fairing side mass value. + + part.mass = totalMass; + + // Set up the GUI update callbacks. + + OnUpdateFairingSideUI (); + } + + public override void OnLoad (ConfigNode cfg) + { + base.OnLoad (cfg); + + if (HighLogic.LoadedSceneIsEditor || HighLogic.LoadedSceneIsFlight) + { + rebuildMesh (); + } + } + + void OnUpdateFairingSideUI () + { + ((UI_FloatEdit) Fields["noseCurveStartX"].uiControlEditor).onFieldChanged += OnUpdateUI; + ((UI_FloatEdit) Fields["noseCurveStartY"].uiControlEditor).onFieldChanged += OnUpdateUI; + + ((UI_FloatEdit) Fields["noseCurveEndX"].uiControlEditor).onFieldChanged += OnUpdateUI; + ((UI_FloatEdit) Fields["noseCurveEndY"].uiControlEditor).onFieldChanged += OnUpdateUI; + + ((UI_FloatEdit) Fields["noseHeightRatio"].uiControlEditor).onFieldChanged += OnUpdateUI; + + ((UI_FloatRange) Fields["density"].uiControlEditor).onFieldChanged += OnUpdateUI; + } + + void OnUpdateUI (BaseField bf, object obj) + { + updateUICheck = true; + } + + public void updateNodeSize () + { + var node = part.FindAttachNode ("connect"); + + if (node != null) + { + int s = Mathf.RoundToInt (baseRad * 2 / 1.25f) - 1; + + if (s < 0) + { + s = 0; + } + + node.size = s; + } + } + + public void FixedUpdate () + { + if (HighLogic.LoadedSceneIsEditor) + { + int nsym = part.symmetryCounterparts.Count; + + if (nsym == 0) + { + massDisplay = PFUtils.formatMass (totalMass); + costDisplay = PFUtils.formatCost (part.partInfo.cost + GetModuleCost (part.partInfo.cost, ModifierStagingSituation.CURRENT)); + } + else if (nsym == 1) + { + massDisplay = PFUtils.formatMass (totalMass * 2) + " (both)"; + costDisplay = PFUtils.formatCost ((part.partInfo.cost + GetModuleCost (part.partInfo.cost, ModifierStagingSituation.CURRENT)) * 2) + " (both)"; + } + else + { + massDisplay = PFUtils.formatMass (totalMass * (nsym + 1)) + " (all " + (nsym + 1) + ")"; + costDisplay = PFUtils.formatCost ((part.partInfo.cost + GetModuleCost (part.partInfo.cost, ModifierStagingSituation.CURRENT)) * (nsym + 1)) + " (all " + (nsym + 1) + ")"; + } + + // Check for GUI changes and update the fairing mesh if applicable. + + if (updateUICheck.Equals (true)) + { + // Rebuild the fairing mesh. + + rebuildMesh (); + + // Update the part window. + + PFUtils.refreshPartWindow (); + + // Tag as done. + + updateUICheck = false; + } + } + } + + public void rebuildMesh () + { + var mf = part.FindModelComponent("model"); + + if (!mf) + { + Debug.LogError ("[PF]: No model for side fairing!", part); + + return; + } + + Mesh m = mf.mesh; + + if (!m) + { + Debug.LogError ("[PF]: No mesh in side fairing model!", part); + + return; + } + + mf.transform.localPosition = meshPos; + mf.transform.localRotation = meshRot; + + updateNodeSize (); + + // Build fairing shape line. + + float tip = maxRad * noseHeightRatio; + + Vector3 [] shape; + + noseConeShape = new Vector4 (noseCurveStartX, noseCurveStartY, noseCurveEndX, noseCurveEndY); + + if (inlineHeight <= 0) + { + shape = ProceduralFairingBase.buildFairingShape (baseRad, maxRad, cylStart, cylEnd, noseHeightRatio, baseConeShape, noseConeShape, baseConeSegments, noseConeSegments, vertMapping, mappingScale.y); + } + else + { + shape = ProceduralFairingBase.buildInlineFairingShape (baseRad, maxRad, topRad, cylStart, cylEnd, inlineHeight, baseConeShape, baseConeSegments, vertMapping, mappingScale.y); + } + + // Set up parameters. + + var dirs = new Vector3 [numSegs + 1]; + + for (int i = 0; i <= numSegs; ++i) + { + float a = Mathf.PI * 2 * (i - numSegs * 0.5f) / (numSideParts * numSegs); + + dirs[i] = new Vector3 (Mathf.Cos(a), 0, Mathf.Sin(a)); + } + + float segOMappingScale = (horMapping.y - horMapping.x) / (mappingScale.x * numSegs); + float segIMappingScale = (horMapping.w - horMapping.z) / (mappingScale.x * numSegs); + float segOMappingOfs = horMapping.x / mappingScale.x; + float segIMappingOfs = horMapping.z / mappingScale.x; + + if (numSideParts > 2) + { + segOMappingOfs += segOMappingScale * numSegs * (0.5f - 1f / numSideParts); + segOMappingScale *= 2f / numSideParts; + + segIMappingOfs += segIMappingScale * numSegs * (0.5f - 1f / numSideParts); + segIMappingScale *= 2f / numSideParts; + } + + float stripU0 = stripMapping.x / mappingScale.x; + float stripU1 = stripMapping.y / mappingScale.x; + + float ringSegLen = baseRad * Mathf.PI * 2 / (numSegs * numSideParts); + float topRingSegLen = topRad * Mathf.PI * 2 / (numSegs * numSideParts); + + float collWidth = maxRad * Mathf.PI * 2 / (numSideParts * 3); + + int numMainVerts = (numSegs + 1) * (shape.Length - 1) + 1; + int numMainFaces = numSegs * ((shape.Length - 2) * 2 + 1); + + int numSideVerts = shape.Length * 2; + int numSideFaces = (shape.Length - 1) * 2; + + int numRingVerts = (numSegs + 1) * 2; + int numRingFaces = numSegs * 2; + + if (inlineHeight > 0) + { + numMainVerts = (numSegs + 1) * shape.Length; + numMainFaces = numSegs * (shape.Length - 1) * 2; + } + + int totalVerts = numMainVerts * 2 + numSideVerts * 2 + numRingVerts; + int totalFaces = numMainFaces * 2 + numSideFaces * 2 + numRingFaces; + + if (inlineHeight > 0) + { + totalVerts += numRingVerts; + totalFaces += numRingFaces; + } + + var p = shape [shape.Length - 1]; + + float topY = p.y, topV = p.z; + + float collCenter = (cylStart + cylEnd) / 2, collHeight = cylEnd - cylStart; + + if (collHeight <= 0) + { + collHeight = Mathf.Min (topY - cylEnd, cylStart) / 2; + } + + // Compute the area. + + double area = 0; + + for (int i = 1; i < shape.Length; ++i) + { + area += (shape [i - 1].x + shape [i].x) * (shape [i].y - shape [i - 1].y) * Mathf.PI / numSideParts; + } + + // Set the parameters based on volume. + + float volume = (float) (area * sideThickness); + + part.mass = totalMass = volume * density; + part.breakingForce = part.mass * specificBreakingForce; + part.breakingTorque = part.mass * specificBreakingTorque; + + var offset = new Vector3 (maxRad * 0.7f, topY * 0.5f, 0); + + part.CoMOffset = part.transform.InverseTransformPoint (mf.transform.TransformPoint (offset)); + + // Remove any old colliders. + + var colls = part.FindModelComponents(); + + for (int i = 0; i < colls.Count; i++) + { + var c = colls [i]; + + UnityEngine.Object.Destroy (c.gameObject); + } + + // Add the new colliders. + + for (int i = -1; i <= 1; ++i) + { + var obj = new GameObject ("collider"); + + obj.transform.parent = mf.transform; + obj.transform.localPosition = Vector3.zero; + obj.transform.localRotation = Quaternion.AngleAxis (90f * i / numSideParts, Vector3.up); + + var coll = obj.AddComponent(); + + coll.center = new Vector3 (maxRad + sideThickness * 0.5f, collCenter, 0); + coll.size = new Vector3 (sideThickness, collHeight, collWidth); + } + { + // Nose collider. + + float r = maxRad * 0.2f; + + var obj = new GameObject ("nose_collider"); + + obj.transform.parent = mf.transform; + obj.transform.localPosition = new Vector3 (r, cylEnd + tip - r * 1.2f, 0); + obj.transform.localRotation = Quaternion.identity; + + if (inlineHeight > 0) + { + r = sideThickness * 0.5f; + + obj.transform.localPosition = new Vector3 (maxRad + r, collCenter, 0); + } + + var coll = obj.AddComponent(); + + coll.center = Vector3.zero; + coll.radius = r; + } + + // Build the fairing mesh. + + m.Clear (); + + var verts = new Vector3 [totalVerts]; + var uv = new Vector2 [totalVerts]; + var norm = new Vector3 [totalVerts]; + var tang = new Vector4 [totalVerts]; + + if (inlineHeight <= 0) + { + // Tip vertex. + + verts [numMainVerts - 1].Set (0, topY + sideThickness, 0); // Outside. + verts [numMainVerts * 2 - 1].Set (0, topY, 0); // Inside. + + uv [numMainVerts - 1].Set (segOMappingScale * 0.5f * numSegs + segOMappingOfs, topV); + uv [numMainVerts * 2 - 1].Set (segIMappingScale * 0.5f * numSegs + segIMappingOfs, topV); + + norm [numMainVerts - 1] = Vector3.up; + norm [numMainVerts * 2 - 1] = -Vector3.up; + + tang [numMainVerts - 1] = Vector3.zero; + tang [numMainVerts * 2 - 1] = Vector3.zero; + } + + // Main vertices. + + float noseV0 = vertMapping.z / mappingScale.y; + float noseV1 = vertMapping.w / mappingScale.y; + float noseVScale = 1f / (noseV1 - noseV0); + float oCenter = (horMapping.x + horMapping.y) / (mappingScale.x * 2); + float iCenter = (horMapping.z + horMapping.w) / (mappingScale.x * 2); + + int vi = 0; + + for (int i = 0; i < shape.Length - (inlineHeight <= 0 ? 1 : 0); ++i) + { + p = shape [i]; + + Vector2 n; + + if (i == 0) + { + n = shape [1] - shape [0]; + } + else if (i == shape.Length - 1) + { + n = shape [i] - shape [i - 1]; + } + else + { + n = shape [i + 1] - shape [i - 1]; + } + + n.Set (n.y, -n.x); + + n.Normalize (); + + for (int j = 0; j <= numSegs; ++j, ++vi) + { + var d = dirs [j]; + + var dp = d * p.x + Vector3.up * p.y; + var dn = d * n.x + Vector3.up * n.y; + + if (i == 0 || i == shape.Length - 1) + { + verts [vi] = dp + d * sideThickness; + } + else + { + verts [vi] = dp + dn * sideThickness; + } + + verts[vi + numMainVerts] = dp; + + float v = (p.z - noseV0) * noseVScale; + float uo = j * segOMappingScale + segOMappingOfs; + float ui = (numSegs - j) * segIMappingScale + segIMappingOfs; + + if (v > 0 && v < 1) + { + float us = 1 - v; + + uo = (uo - oCenter) * us + oCenter; + ui = (ui - iCenter) * us + iCenter; + } + + uv [vi].Set (uo, p.z); + + uv [vi + numMainVerts].Set (ui, p.z); + + norm [vi] = dn; + norm [vi + numMainVerts] = -dn; + + tang [vi].Set (-d.z, 0, d.x, 0); + tang [vi + numMainVerts].Set (d.z, 0, -d.x, 0); + } + } + + // Side strip vertices. + + float stripScale = Mathf.Abs (stripMapping.y - stripMapping.x) / (sideThickness * mappingScale.y); + + vi = numMainVerts * 2; + + float o = 0; + + for (int i = 0; i < shape.Length; ++i, vi += 2) + { + int si = i * (numSegs + 1); + + var d = dirs [0]; + + verts [vi] = verts [si]; + + uv [vi].Set (stripU0, o); + norm [vi].Set (d.z, 0, -d.x); + + verts [vi + 1] = verts [si + numMainVerts]; + uv [vi + 1].Set (stripU1, o); + norm [vi + 1] = norm[vi]; + tang [vi] = tang [vi + 1] = (verts [vi + 1] - verts [vi]).normalized; + + if (i + 1 < shape.Length) + { + o += ((Vector2) shape [i + 1] - (Vector2) shape [i]).magnitude * stripScale; + } + } + + vi += numSideVerts - 2; + + for (int i = shape.Length - 1; i >= 0; --i, vi -= 2) + { + int si = i * (numSegs + 1) + numSegs; + + if (i == shape.Length - 1 && inlineHeight <= 0) + { + si = numMainVerts - 1; + } + + var d = dirs [numSegs]; + + verts [vi] = verts [si]; + uv [vi].Set (stripU0, o); + norm [vi].Set (-d.z, 0, d.x); + + verts [vi + 1] = verts [si + numMainVerts]; + uv [vi + 1].Set (stripU1, o); + norm [vi + 1] = norm [vi]; + tang [vi] = tang [vi + 1] = (verts [vi + 1] - verts [vi]).normalized; + + if (i > 0) + { + o += ((Vector2) shape [i] - (Vector2) shape [i - 1]).magnitude * stripScale; + } + } + + // Ring vertices. + + vi = numMainVerts * 2 + numSideVerts * 2; + + o = 0; + + for (int j = numSegs; j >= 0; --j, vi += 2, o += ringSegLen * stripScale) + { + verts [vi] = verts [j]; + uv [vi].Set (stripU0, o); + norm [vi] = -Vector3.up; + + verts [vi + 1] = verts [j + numMainVerts]; + uv [vi + 1].Set (stripU1, o); + norm [vi + 1] = -Vector3.up; + tang [vi] = tang [vi + 1] = (verts [vi + 1] - verts [vi]).normalized; + } + + if (inlineHeight > 0) + { + // Top ring vertices. + + o = 0; + + int si = (shape.Length - 1) * (numSegs + 1); + + for (int j = 0; j <= numSegs; ++j, vi += 2, o += topRingSegLen * stripScale) + { + verts [vi] = verts [si + j]; + uv [vi].Set (stripU0, o); + norm [vi] = Vector3.up; + + verts [vi + 1] = verts [si + j + numMainVerts]; + uv [vi + 1].Set (stripU1, o); + norm [vi + 1] = Vector3.up; + tang [vi] = tang [vi + 1] = (verts [vi + 1] - verts [vi]).normalized; + } + } + + // Set vertex data to mesh. + + for (int i = 0; i < totalVerts; ++i) + { + tang [i].w = 1; + } + + m.vertices = verts; + m.uv = uv; + m.normals = norm; + m.tangents = tang; + + m.uv2 = null; + m.colors32 = null; + + var tri = new int [totalFaces * 3]; + + // Main faces. + + vi = 0; + + int ti1 = 0, ti2 = numMainFaces * 3; + + for (int i = 0; i < shape.Length - (inlineHeight <= 0 ? 2 : 1); ++i, ++vi) + { + p = shape [i]; + + for (int j = 0; j < numSegs; ++j, ++vi) + { + tri [ti1++] = vi; + tri [ti1++] = vi + 1 + numSegs + 1; + tri [ti1++] = vi + 1; + + tri [ti1++] = vi; + tri [ti1++] = vi + numSegs + 1; + tri [ti1++] = vi + 1 + numSegs + 1; + + tri [ti2++] = numMainVerts + vi; + tri [ti2++] = numMainVerts + vi + 1; + tri [ti2++] = numMainVerts + vi + 1 + numSegs + 1; + + tri [ti2++] = numMainVerts + vi; + tri [ti2++] = numMainVerts + vi + 1 + numSegs + 1; + tri [ti2++] = numMainVerts + vi + numSegs + 1; + } + } + + if (inlineHeight <= 0) + { + // Main tip faces. + + for (int j = 0; j < numSegs; ++j, ++vi) + { + tri [ti1++] = vi; + tri [ti1++] = numMainVerts - 1; + tri [ti1++] = vi + 1; + + tri [ti2++] = numMainVerts + vi; + tri [ti2++] = numMainVerts + vi + 1; + tri [ti2++] = numMainVerts + numMainVerts - 1; + } + } + + // Side strip faces. + + vi = numMainVerts * 2; + ti1 = numMainFaces * 2 * 3; + ti2 = ti1 + numSideFaces * 3; + + for (int i = 0; i < shape.Length - 1; ++i, vi += 2) + { + tri [ti1++] = vi; + tri [ti1++] = vi + 1; + tri [ti1++] = vi + 3; + + tri [ti1++] = vi; + tri [ti1++] = vi + 3; + tri [ti1++] = vi + 2; + + tri [ti2++] = numSideVerts + vi; + tri [ti2++] = numSideVerts + vi + 3; + tri [ti2++] = numSideVerts + vi + 1; + + tri [ti2++] = numSideVerts + vi; + tri [ti2++] = numSideVerts + vi + 2; + tri [ti2++] = numSideVerts + vi + 3; + } + + // Ring faces. + + vi = numMainVerts * 2 + numSideVerts * 2; + ti1 = (numMainFaces + numSideFaces) * 2 * 3; + + for (int j = 0; j < numSegs; ++j, vi += 2) + { + tri [ti1++] = vi; + tri [ti1++] = vi + 1; + tri [ti1++] = vi + 3; + + tri [ti1++] = vi; + tri [ti1++] = vi + 3; + tri [ti1++] = vi + 2; + } + + if (inlineHeight > 0) + { + // Top ring faces. + + vi += 2; + + for (int j = 0; j < numSegs; ++j, vi += 2) + { + tri [ti1++] = vi; + tri [ti1++] = vi + 1; + tri [ti1++] = vi + 3; + + tri [ti1++] = vi; + tri [ti1++] = vi + 3; + tri [ti1++] = vi + 2; + } + } + + m.triangles = tri; + + if (!HighLogic.LoadedSceneIsEditor) + { + m.Optimize (); + } + + StartCoroutine (PFUtils.updateDragCubeCoroutine (part, 1)); + } + } +} diff --git a/Source/ProceduralFairings/NodeNumberTweaker.cs b/Source/ProceduralFairings/NodeNumberTweaker.cs new file mode 100644 index 0000000..5711bf3 --- /dev/null +++ b/Source/ProceduralFairings/NodeNumberTweaker.cs @@ -0,0 +1,351 @@ +// ================================================== +// Procedural Fairings plug-in by Alexey Volynskov. + +// Licensed under CC-BY-4.0 terms: https://creativecommons.org/licenses/by/4.0/legalcode +// ================================================== + +using System; +using UnityEngine; + +namespace Keramzit +{ + public class KzNodeNumberTweaker : PartModule + { + [KSPField] public string nodePrefix = "bottom"; + [KSPField] public int maxNumber; + + [KSPField (guiActiveEditor = true, guiName = "Fairing Nodes")] + [UI_FloatRange (minValue = 1, maxValue = 8, stepIncrement = 1)] + public float uiNumNodes = 2; + + [KSPField (isPersistant = true)] + public int numNodes = 2; + + int numNodesBefore; + + [KSPField (isPersistant = true, guiActiveEditor = true, guiName = "Node offset", guiFormat = "S4", guiUnits = "m")] + [UI_FloatEdit (sigFigs = 3, unit = "m", minValue = 0.1f, maxValue = 5, incrementLarge = 0.625f, incrementSmall = 0.125f, incrementSlide = 0.001f)] + public float radius = 1.25f; + + [KSPField] public float radiusStepLarge = 0.625f; + [KSPField] public float radiusStepSmall = 0.125f; + + [KSPField] public bool shouldResizeNodes = true; + + protected float oldRadius = -1000; + protected bool justLoaded; + + public override string GetInfo () + { + return "Max. Nodes: " + maxNumber; + } + + [KSPField (isPersistant = true, guiActiveEditor = true, guiName = "Interstage Nodes")] + [UI_Toggle (disabledText = "Off", enabledText = "On")] + public bool showInterstageNodes = true; + + public virtual void FixedUpdate () + { + if (!radius.Equals (oldRadius)) + { + oldRadius = radius; + + updateNodePositions (); + } + + justLoaded = false; + } + + public void Update () + { + if (HighLogic.LoadedSceneIsEditor) + { + if ((int) uiNumNodes != numNodesBefore) + { + if (checkNodeAttachments ()) + { + uiNumNodes = numNodesBefore; + } + else + { + numNodes = (int) uiNumNodes; + numNodesBefore = numNodes; + + updateNodes (); + + bool removed = false; + + for (int i = numNodes + 1; i <= maxNumber; ++i) + { + var node = findNode (i); + + if (node == null) + { + continue; + } + + // Fix for ghost node when inserting a new pf base in VAB. + // do not delete unused nodes, move them away instead + // be careful to check references to maximum number of nodes + // mentioned elsewhere retrieved from 'Findattachnodes("connect")'! + // Slightly hacky, but works... + + HideUnusedNode (node); + + removed = true; + } + + if (removed) + { + var fbase = part.GetComponent(); + + if (fbase) + { + fbase.needShapeUpdate = true; + fbase.updateDelay = 0; + } + } + } + } + } + } + + // Slightly hacky...but it removes the ghost nodes. + + void HideUnusedNode (AttachNode node) + { + node.position.x = 10000; + } + + public override void OnStart (StartState state) + { + base.OnStart (state); + + if (state == StartState.None) + { + return; + } + + ((UI_FloatEdit) Fields["radius"].uiControlEditor).incrementLarge = radiusStepLarge; + ((UI_FloatEdit) Fields["radius"].uiControlEditor).incrementSmall = radiusStepSmall; + + if (!shouldResizeNodes) + { + Fields["radius"].guiActiveEditor = false; + } + + // Hide the interstage toggle button if there are no interstage nodes. + + var nodes = part.FindAttachNodes("interstage"); + + if (nodes == null) + { + Fields["showInterstageNodes"].guiActiveEditor = false; + } + + // Change the GUI text if there are no fairing attachment nodes. + + nodes = part.FindAttachNodes ("connect"); + + if (nodes == null) + { + Fields["uiNumNodes"].guiName = "Side Nodes"; + } + + ((UI_FloatRange) Fields["uiNumNodes"].uiControlEditor).maxValue = maxNumber; + + uiNumNodes = numNodes; + numNodesBefore = numNodes; + + updateNodes (); + } + + public override void OnLoad (ConfigNode cfg) + { + base.OnLoad (cfg); + + justLoaded = true; + + uiNumNodes = numNodes; + numNodesBefore = numNodes; + + if (HighLogic.LoadedSceneIsEditor || HighLogic.LoadedSceneIsFlight) + { + updateNodes (); + } + } + + void updateNodes () + { + addRemoveNodes (); + + updateNodePositions (); + } + + string nodeName (int i) + { + return string.Format ("{0}{1:d2}", nodePrefix, i); + } + + AttachNode findNode (int i) + { + return part.FindAttachNode (nodeName (i)); + } + + bool checkNodeAttachments() + { + for (int i = 1; i <= maxNumber; ++i) + { + var node = findNode (i); + + if (node != null && node.attachedPart != null) + { + EditorScreenMessager.showMessage ("Please detach any fairing parts before changing the number of nodes!", 1); + + return true; + } + } + + return false; + } + + void addRemoveNodes () + { + part.stackSymmetry = numNodes - 1; + + float y = 0; + + bool gotY = false; + + int nodeSize = 0; + + Vector3 dir = Vector3.up; + + int i; + + for (i = 1; i <= maxNumber; ++i) + { + var node = findNode (i); + + if (node == null) + { + continue; + } + + y = node.position.y; + nodeSize = node.size; + dir = node.orientation; + + gotY = true; + + break; + } + + if (!gotY) + { + var node = part.FindAttachNode ("bottom"); + + if (node != null) + { + y = node.position.y; + } + } + + for (i = 1; i <= numNodes; ++i) + { + var node = findNode (i); + + if (node != null) + { + continue; + } + + // Create node. + + node = new AttachNode (); + + node.id = nodeName (i); + node.owner = part; + node.nodeType = AttachNode.NodeType.Stack; + node.position = new Vector3 (0, y, 0); + node.orientation = dir; + node.originalPosition = node.position; + node.originalOrientation = node.orientation; + node.size = nodeSize; + + part.attachNodes.Add (node); + } + + for (; i <= maxNumber; ++i) + { + var node = findNode (i); + + if (node == null) + { + continue; + } + + if (HighLogic.LoadedSceneIsEditor) + { + node.position.x = 10000; + } + else + { + part.attachNodes.Remove (node); + } + } + + var fbase = part.GetComponent(); + + if (fbase) + { + fbase.needShapeUpdate = true; + } + } + + void updateNodePositions () + { + float d = Mathf.Sin (Mathf.PI / numNodes) * radius * 2; + + int size = Mathf.RoundToInt (d / (radiusStepLarge * 2)); + + for (int i = 1; i <= numNodes; ++i) + { + var node = findNode (i); + + if (node == null) + { + continue; + } + + float a = Mathf.PI * 2 * (i - 1) / numNodes; + + node.position.x = Mathf.Cos (a) * radius; + node.position.z = Mathf.Sin (a) * radius; + + if (shouldResizeNodes) + { + node.size = size; + } + + if (!justLoaded) + { + PFUtils.updateAttachedPartPos (node, part); + } + } + + for (int i = numNodes + 1; i <= maxNumber; ++i) + { + var node = findNode (i); + + if (node == null) + { + continue; + } + + node.position.x = 10000; + } + } + } + +} diff --git a/Source/ProceduralFairings/PFKMJoint.cs b/Source/ProceduralFairings/PFKMJoint.cs new file mode 100644 index 0000000..4cd719b --- /dev/null +++ b/Source/ProceduralFairings/PFKMJoint.cs @@ -0,0 +1,605 @@ +// ================================================== +// Procedural Fairings plug-in by Alexey Volynskov. + +// Licensed under CC-BY-4.0 terms: https://creativecommons.org/licenses/by/4.0/legalcode +// ================================================== + +using System; +using System.Collections.Generic; +using UnityEngine; + +namespace Keramzit +{ + public class PFKMJoints : PartModule + { + [KSPField (isPersistant = true)] + public float breakingForce = 500000f; + + [KSPField (guiActive = true, guiName = "View Joints")] + [UI_Toggle (disabledText = "Off", enabledText = "On")] + public bool viewJoints; + + const float w1 = 0.05f; + + const float w2 = 0.15f; + + readonly List jointLines = new List(); + + bool morejointsadded; + + Part bottomNodePart; + + List nodeParts = new List(); + + void AddMoreJoints () + { + int i; + + AttachNode attachNode; + + if (!morejointsadded) + { + AttachNode attachNode1 = base.part.FindAttachNode ("bottom"); + + AttachNode [] attachNodeArray = base.part.FindAttachNodes ("interstage"); + AttachNode [] attachNodeArray1 = base.part.FindAttachNodes ("top"); + + string [] _vessel = { "[PF]: Adding Joints to Vessel: ", vessel.vesselName, " (Launch ID: ", base.part.launchID.ToString(), ") (GUID: ", base.vessel.id.ToString(), ")" }; + + Debug.Log (string.Concat (_vessel)); + + _vessel = new [] { "[PF]: For PF Part: ", base.part.name, " (", base.part.craftID.ToString (), ")" }; + + Debug.Log (string.Concat (_vessel)); + + Part part = null; + + if ((attachNode1 == null ? false : attachNode1.attachedPart != null)) + { + part = attachNode1.attachedPart; + + bottomNodePart = part; + + addStrut (part, base.part, Vector3.zero); + + _vessel = new [] { "[PF]: Bottom Part: ", part.name, " (", part.craftID.ToString (), ")" }; + + Debug.Log (string.Concat (_vessel)); + } + + Debug.Log ("[PF]: Top Parts:"); + + if (attachNodeArray1 != null) + { + for (i = 0; i < (int)attachNodeArray1.Length; i++) + { + attachNode = attachNodeArray1 [i]; + + if (attachNode.attachedPart != null) + { + if (part != null) + { + AddPartJoint (attachNode.attachedPart, part, attachNode.FindOpposingNode (), attachNode1.FindOpposingNode ()); + } + + addStrut (attachNode.attachedPart, base.part, Vector3.zero); + + nodeParts.Add (attachNode.attachedPart); + + _vessel = new [] { "[PF]:", attachNode.attachedPart.name, " (", attachNode.attachedPart.craftID.ToString (), ")" }; + + Debug.Log (string.Concat (_vessel)); + } + } + } + + if (attachNodeArray != null) + { + for (i = 0; i < (int)attachNodeArray.Length; i++) + { + attachNode = attachNodeArray [i]; + + if (attachNode.attachedPart != null) + { + if (part != null) + { + AddPartJoint (attachNode.attachedPart, part, attachNode.FindOpposingNode (), attachNode1.FindOpposingNode ()); + } + + addStrut (attachNode.attachedPart, base.part, Vector3.zero); + + nodeParts.Add (attachNode.attachedPart); + + _vessel = new [] { "[PF]:", attachNode.attachedPart.name, " (", attachNode.attachedPart.craftID.ToString (), ")" }; + + Debug.Log (string.Concat (_vessel)); + } + } + } + + morejointsadded = true; + } + } + + void AddPartJoint (Part p, Part pp, AttachNode pnode, AttachNode ppnode) + { + PartJoint partJoint = PartJoint.Create (p, pp, pnode, ppnode, 0); + + partJoint.SetBreakingForces (breakingForce, breakingForce); + + PartJoint partJoint1 = p.gameObject.AddComponent(); + + partJoint1 = partJoint; + } + + ConfigurableJoint addStrut (Part p, Part pp, Vector3 pos) + { + ConfigurableJoint configurableJoint; + + if (p != pp) + { + Rigidbody rigidbody = pp.Rigidbody; + + if ((rigidbody == null ? false : !(rigidbody == p.Rigidbody))) + { + ConfigurableJoint configurableJoint1 = p.gameObject.AddComponent(); + + configurableJoint1.xMotion = 0; + configurableJoint1.yMotion = 0; + configurableJoint1.zMotion = 0; + configurableJoint1.angularXMotion = 0; + configurableJoint1.angularYMotion = 0; + configurableJoint1.angularZMotion = 0; + configurableJoint1.projectionDistance = 0.1f; + configurableJoint1.projectionAngle = 5f; + configurableJoint1.breakForce = breakingForce; + configurableJoint1.breakTorque = breakingForce; + configurableJoint1.connectedBody = rigidbody; + configurableJoint1.targetPosition = pos; + configurableJoint = configurableJoint1; + } + else + { + configurableJoint = null; + } + } + else + { + configurableJoint = null; + } + + return configurableJoint; + } + + void ClearJointLines () + { + for (int i = 0; i < jointLines.Count; i++) + { + UnityEngine.Object.Destroy (jointLines [i].gameObject); + } + + jointLines.Clear (); + } + + public virtual void FixedUpdate () + { + if (!morejointsadded) + { + if ((!FlightGlobals.ready || vessel.packed ? false : vessel.loaded)) + { + AddMoreJoints (); + } + } + } + + LineRenderer JointLine (Vector3 posp, Vector3 pospp, Color col, float width) + { + LineRenderer lineRenderer = makeLineRenderer ("JointLine", col, width); + + lineRenderer.SetVertexCount (2); + + lineRenderer.SetPosition (0, posp); + lineRenderer.SetPosition (1, pospp); + + lineRenderer.useWorldSpace = true; + + return lineRenderer; + } + + public void ListJoints () + { + int j; + + string [] _name; + + float _breakForce; + + Vector3 _anchor; + + string str; + string str1; + + ClearJointLines (); + + List activeVessel = FlightGlobals.ActiveVessel.parts; + + for (int i = 0; i < activeVessel.Count; i++) + { + ConfigurableJoint [] components = activeVessel[i].gameObject.GetComponents(); + + if (components != null) + { + for (j = 0; j < (int)components.Length; j++) + { + ConfigurableJoint configurableJoint = components [j]; + + _name = new string [18]; + + _name[0] = "[PF]: , "; + _name[1] = activeVessel [i].name; + _name[2] = ", "; + _name[3] = (configurableJoint.connectedBody == null ? "" : configurableJoint.connectedBody.name); + _name[4] = ", "; + _breakForce = configurableJoint.breakForce; + _name[5] = _breakForce.ToString (); + _name[6] = ", "; + _breakForce = configurableJoint.breakTorque; + _name[7] = _breakForce.ToString (); + _name[8] = ", "; + _anchor = configurableJoint.anchor; + _name[9] = _anchor.ToString (); + _name[10] = ", "; + _anchor = configurableJoint.connectedAnchor; + _name[11] = _anchor.ToString (); + _name[12] = ", "; + + string [] strArrays = _name; + + if (configurableJoint.connectedBody == null) + { + str1 = "--"; + } + else + { + _anchor = activeVessel [i].transform.position - configurableJoint.connectedBody.position; + + str1 = _anchor.ToString (); + } + + strArrays [13] = str1; + + _name [14] = ", "; + _breakForce = configurableJoint.linearLimitSpring.damper; + _name [15] = _breakForce.ToString ("F2"); + _name [16] = ", "; + _breakForce = configurableJoint.linearLimitSpring.spring; + _name [17] = _breakForce.ToString ("F2"); + + Debug.Log (string.Concat (_name)); + } + } + + PartJoint [] partJointArray = activeVessel[i].gameObject.GetComponents(); + + if (partJointArray != null) + { + for (j = 0; j < (int)partJointArray.Length; j++) + { + PartJoint partJoint = partJointArray [j]; + + if ((partJoint.Host != null ? true : !(partJoint.Target == null))) + { + _name = new string [] { "[PF]: , ", partJoint.Host.name, ", ", null, null, null, null, null, null, null, null }; + + _name[3] = (partJoint.Target == null ? "" : partJoint.Target.name); + + string [] strArrays1 = _name; + + if (partJoint.Joint == null) + { + int count = partJoint.joints.Count; + + str = string.Concat (" (", count.ToString (), ")"); + } + else + { + _breakForce = partJoint.Joint.breakForce; + + string str2 = _breakForce.ToString (); + + _breakForce = partJoint.Joint.breakTorque; + + str = string.Concat (", ", str2, ", ", _breakForce.ToString ()); + } + + strArrays1 [4] = str; + + _name [5] = ", "; + _breakForce = partJoint.stiffness; + _name [6] = _breakForce.ToString ("F2"); + _name [7] = ", "; + _anchor = partJoint.HostAnchor; + _name [8] = _anchor.ToString (); + _name [9] = ", "; + _anchor = partJoint.TgtAnchor; + _name [10] = _anchor.ToString (); + + Debug.Log (string.Concat (_name)); + } + else + { + Debug.Log ("[PF]: , , "); + } + + if (partJoint.Target) + { + AttachNode attachNode = activeVessel [i].FindAttachNodeByPart (partJoint.Target); + + if (attachNode != null) + { + object [] objArray = { "[PF]: , ", partJoint.Host.name, ", ", partJoint.Target.name, ", ", attachNode.breakingForce.ToString(), ", ", attachNode.breakingTorque.ToString(), ", ", attachNode.contactArea.ToString("F2"), ", ", attachNode.attachMethod, ", ", attachNode.rigid.ToString (), ", ", attachNode.radius.ToString ("F2") }; + + Debug.Log (string.Concat (objArray)); + + AttachNode attachNode1 = attachNode.FindOpposingNode (); + + if ((attachNode1 == null ? false : attachNode1.owner != null)) + { + objArray = new object [] { "[PF]: , ", attachNode1.owner.name, ", ", (attachNode1.attachedPart != null ? attachNode1.attachedPart.name : ""), ", ", attachNode1.breakingForce.ToString(), ", ", attachNode1.breakingTorque.ToString(), ", ", attachNode1.contactArea.ToString("F2"), ", ", attachNode1.attachMethod, ", ", attachNode1.rigid.ToString (), ", ", attachNode1.radius.ToString ("F2") }; + + Debug.Log (string.Concat (objArray)); + } + } + } + } + } + + FixedJoint [] fixedJointArray = activeVessel [i].gameObject.GetComponents(); + + if (fixedJointArray != null) + { + for (j = 0; j < (int)fixedJointArray.Length; j++) + { + FixedJoint fixedJoint = fixedJointArray [j]; + + _name = new string [] { "[PF]: , ", fixedJoint.name, ", ", null, null, null, null, null, null, null, null, null }; + + _name [3] = (fixedJoint.connectedBody == null ? "" : fixedJoint.connectedBody.name); + _name [4] = ", "; + _breakForce = fixedJoint.breakForce; + _name [5] = _breakForce.ToString (); + _name [6] = ", "; + _breakForce = fixedJoint.breakTorque; + _name [7] = _breakForce.ToString (); + _name [8] = ", "; + _anchor = fixedJoint.anchor; + _name [9] = _anchor.ToString (); + _name [10] = ", "; + _anchor = fixedJoint.connectedAnchor; + _name [11] = _anchor.ToString (); + + Debug.Log (string.Concat (_name)); + } + } + } + } + + LineRenderer makeLineRenderer (string name, Color color, float wd) + { + var gameObject = new GameObject (name); + + gameObject.transform.parent = part.transform; + + gameObject.transform.localPosition = Vector3.zero; + gameObject.transform.localRotation = Quaternion.identity; + + LineRenderer lineRenderer = gameObject.AddComponent(); + + lineRenderer.useWorldSpace = true; + lineRenderer.material = new Material (Shader.Find ("Particles/Additive")); + + lineRenderer.SetColors (color, color); + lineRenderer.SetWidth (wd, wd); + lineRenderer.SetVertexCount (0); + + return lineRenderer; + } + + public void OnDestroy () + { + viewJoints = false; + + ClearJointLines (); + + GameEvents.onGameSceneLoadRequested.Remove (new EventData.OnEvent (OnGameSceneLoadRequested)); + GameEvents.onVesselWasModified.Remove (new EventData.OnEvent (OnVesselModified)); + GameEvents.onPartJointBreak.Remove (new EventData.OnEvent (OnPartJointBreak)); + GameEvents.onVesselGoOffRails.Remove (new EventData.OnEvent (OnVesselGoOffRails)); + } + + void OnGameSceneLoadRequested (GameScenes scene) + { + if ((scene == GameScenes.FLIGHT ? false : viewJoints)) + { + viewJoints = false; + + ClearJointLines (); + } + } + + void OnPartJointBreak (PartJoint pj, float value) + { + Part host; + int i; + bool flag; + + if (pj.Host != part) + { + if (pj.Target == part) + { + host = pj.Host; + + if (nodeParts.Contains(host)) + { + if (bottomNodePart != null) + { + RemoveJoints (host, bottomNodePart); + } + + RemoveJoints (host, part); + + flag = nodeParts.Remove (host); + } + else if (host == bottomNodePart) + { + for (i = 0; i < nodeParts.Count; i++) + { + RemoveJoints (nodeParts [i], bottomNodePart); + } + + RemoveJoints (bottomNodePart, part); + } + + return; + } + + return; + } + + host = pj.Target; + + if (nodeParts.Contains(host)) + { + if (bottomNodePart != null) + { + RemoveJoints (host, bottomNodePart); + } + + RemoveJoints (host, part); + + flag = nodeParts.Remove (host); + } + else if (host == bottomNodePart) + { + for (i = 0; i < nodeParts.Count; i++) + { + RemoveJoints (nodeParts [i], bottomNodePart); + } + + RemoveJoints (bottomNodePart, part); + } + } + + public override void OnStart (PartModule.StartState state) + { + base.OnStart (state); + + var _uiControlFlight = (UI_Toggle) Fields["viewJoints"].uiControlFlight; + + _uiControlFlight.onFieldChanged = (Callback)Delegate.Combine (_uiControlFlight.onFieldChanged, new Callback(UIviewJoints_changed)); + + GameEvents.onGameSceneLoadRequested.Add (new EventData.OnEvent (OnGameSceneLoadRequested)); + GameEvents.onVesselWasModified.Add (new EventData.OnEvent (OnVesselModified)); + GameEvents.onPartJointBreak.Add (new EventData.OnEvent (OnPartJointBreak)); + GameEvents.onVesselGoOffRails.Add (new EventData.OnEvent (OnVesselGoOffRails)); + } + + void OnVesselGoOffRails (Vessel v) + { + if ((v == null ? true : this == null)) + { + } + } + + void OnVesselModified (Vessel v) + { + if (v == vessel) + { + if (viewJoints) + { + ViewJoints (); + } + } + } + + void RemoveJoints (Part p, Part pp) + { + if ((p == null || p.Rigidbody == null || pp == null ? false : !(pp.Rigidbody == null))) + { + ConfigurableJoint [] components = p.gameObject.GetComponents(); + + for (int i = 0; i < (int)components.Length; i++) + { + ConfigurableJoint configurableJoint = components [i]; + + if (configurableJoint.connectedBody == pp.Rigidbody) + { + try + { + UnityEngine.Object.Destroy (configurableJoint); + } + catch (Exception exception1) + { + Exception exception = exception1; + + string [] str = { "[PF]: RemoveJoint Anomaly (", p.ToString(), ", ", pp.ToString(), "): ", exception.Message }; + + Debug.Log (string.Concat (str)); + } + } + } + } + } + + void UIviewJoints_changed (BaseField bf, object obj) + { + if (viewJoints) + { + ListJoints (); + + ViewJoints (); + } + else + { + ClearJointLines(); + } + } + + public void ViewJoints () + { + ClearJointLines (); + + List activeVessel = FlightGlobals.ActiveVessel.parts; + + for (int i = 0; i < activeVessel.Count; i++) + { + ConfigurableJoint [] components = activeVessel [i].gameObject.GetComponents(); + + if (components != null) + { + for (int j = 0; j < (int)components.Length; j++) + { + ConfigurableJoint configurableJoint = components [j]; + + if (configurableJoint.connectedBody != null) + { + Vector3 vector3 = new Vector3 (0f, 5f, 0f); + Vector3 vector31 = new Vector3 (0.25f, 0f, 0f); + + Vector3 _position = activeVessel [i].transform.position + vector3; + Vector3 _position1 = configurableJoint.connectedBody.position + vector3; + Vector3 _position2 = (activeVessel [i].transform.position + (activeVessel [i].transform.rotation * configurableJoint.anchor)) + vector3; + + Vector3 vector32 = (configurableJoint.connectedBody.position + (configurableJoint.connectedBody.rotation * configurableJoint.connectedAnchor)) + vector3; + + jointLines.Add (JointLine (_position, _position1, Color.blue, w1)); + jointLines.Add (JointLine (_position2, vector32 + vector31, Color.yellow, w2)); + jointLines.Add (JointLine (_position, _position2, Color.gray, 0.03f)); + jointLines.Add (JointLine (_position1, vector32, Color.gray, 0.03f)); + } + } + } + } + } + } +} diff --git a/Source/ProceduralFairings/PayloadScan.cs b/Source/ProceduralFairings/PayloadScan.cs new file mode 100644 index 0000000..c0bd250 --- /dev/null +++ b/Source/ProceduralFairings/PayloadScan.cs @@ -0,0 +1,211 @@ +// ================================================== +// Procedural Fairings plug-in by Alexey Volynskov. + +// Licensed under CC-BY-4.0 terms: https://creativecommons.org/licenses/by/4.0/legalcode +// ================================================== + +using System; +using System.Collections.Generic; +using UnityEngine; + +namespace Keramzit +{ + struct PayloadScan + { + public List profile; + public List payload; + public HashSet hash; + + public List targets; + + public Matrix4x4 w2l; + + public float ofs, verticalStep, extraRadius; + + public int nestedBases; + + public PayloadScan (Part p, float vs, float er) + { + profile = new List(); + payload = new List(); + targets = new List(); + hash = new HashSet(); + + hash.Add (p); + + w2l = p.transform.worldToLocalMatrix; + ofs = 0; + verticalStep = vs; + extraRadius = er; + nestedBases = 0; + } + + public void addPart (Part p, Part prevPart) + { + if (p == null || hash.Contains (p)) + { + return; + } + + hash.Add (p); + + if (p.GetComponent() != null) + { + return; + } + + if (p == prevPart.parent && prevPart.srfAttachNode.attachedPart == p) + { + return; + } + + // Check for another fairing base. + + if (p.GetComponent() != null && p.GetComponent() == null) + { + AttachNode node = p.FindAttachNode ("top"); + + if (node != null && node.attachedPart == prevPart) + { + // Reversed base - potential inline fairing target. + + if (nestedBases <= 0) + { + targets.Add (p); + + return; + } + + --nestedBases; + } + else + { + ++nestedBases; + } + } + + payload.Add (p); + } + + public void addPayloadEdge (Vector3 v0, Vector3 v1) + { + float r0 = Mathf.Sqrt (v0.x * v0.x + v0.z * v0.z) + extraRadius; + float r1 = Mathf.Sqrt (v1.x * v1.x + v1.z * v1.z) + extraRadius; + + float y0 = (v0.y - ofs) / verticalStep; + float y1 = (v1.y - ofs) / verticalStep; + + if (y0 > y1) + { + float tmp; + + tmp = y0; y0 = y1; y1 = tmp; + tmp = r0; r0 = r1; r1 = tmp; + } + + int h0 = Mathf.FloorToInt (y0); + int h1 = Mathf.FloorToInt (y1); + + if (h1 < 0) + { + return; + } + + if (h1 >= profile.Count) + { + var farray = new float [h1 - profile.Count + 1]; + + for (int i = 0; i < farray.Length; i++) + { + farray [i] = 0; + } + + profile.AddRange (farray); + } + + if (h0 >= 0) + { + profile [h0] = Mathf.Max (profile [h0], r0); + } + + profile [h1] = Mathf.Max (profile [h1], r1); + + if (h0 != h1) + { + float k = (r1 - r0) / (y1 - y0); + float b = r0 + k * (h0 + 1 - y0); + float maxR = Mathf.Max (r0, r1); + + for (int h = Math.Max (h0, 0); h < h1; ++h) + { + float r = Mathf.Min (k * (h - h0) + b, maxR); + + profile [h] = Mathf.Max (profile [h], r); + profile [h + 1] = Mathf.Max (profile [h + 1], r); + } + } + } + + public void addPayload (Bounds box, Matrix4x4 boxTm) + { + Matrix4x4 m = w2l * boxTm; + + Vector3 p0 = box.min, p1 = box.max; + + var verts = new Vector3 [8]; + + for (int i = 0; i < 8; ++i) + { + verts [i] = m.MultiplyPoint3x4 (new Vector3 ((i & 1) != 0 ? p1.x : p0.x, (i & 2) != 0 ? p1.y : p0.y, (i & 4) != 0 ? p1.z : p0.z)); + } + + addPayloadEdge (verts [0], verts [1]); + addPayloadEdge (verts [2], verts [3]); + addPayloadEdge (verts [4], verts [5]); + addPayloadEdge (verts [6], verts [7]); + + addPayloadEdge (verts [0], verts [2]); + addPayloadEdge (verts [1], verts [3]); + addPayloadEdge (verts [4], verts [6]); + addPayloadEdge (verts [5], verts [7]); + + addPayloadEdge (verts [0], verts [4]); + addPayloadEdge (verts [1], verts [5]); + addPayloadEdge (verts [2], verts [6]); + addPayloadEdge (verts [3], verts [7]); + } + + public void addPayload (Collider c) + { + var mc = c as MeshCollider; + var bc = c as BoxCollider; + + if (mc) + { + var m = w2l * mc.transform.localToWorldMatrix; + + var verts = mc.sharedMesh.vertices; + var faces = mc.sharedMesh.triangles; + + for (int i = 0; i < faces.Length; i += 3) + { + var v0 = m.MultiplyPoint3x4 (verts [faces [i]]); + var v1 = m.MultiplyPoint3x4 (verts [faces [i + 1]]); + var v2 = m.MultiplyPoint3x4 (verts [faces [i + 2]]); + + addPayloadEdge (v0, v1); + addPayloadEdge (v1, v2); + addPayloadEdge (v2, v0); + } + } + else if (bc) + { + addPayload (new Bounds (bc.center, bc.size), bc.transform.localToWorldMatrix); + } + else + { + addPayload (c.bounds, Matrix4x4.identity); + } + } + } +} diff --git a/Source/ProceduralFairings/ProcAdapter.cs b/Source/ProceduralFairings/ProcAdapter.cs new file mode 100644 index 0000000..3b2b157 --- /dev/null +++ b/Source/ProceduralFairings/ProcAdapter.cs @@ -0,0 +1,728 @@ +// ================================================== +// Procedural Fairings plug-in by Alexey Volynskov. + +// Licensed under CC-BY-4.0 terms: https://creativecommons.org/licenses/by/4.0/legalcode +// ================================================== + +using KSP.UI.Screens; +using System; +using System.Collections.Generic; +using UnityEngine; + +namespace Keramzit +{ + abstract class ProceduralAdapterBase : PartModule + { + [KSPField (isPersistant = true, guiActiveEditor = true, guiName = "Base", guiFormat = "S4", guiUnits = "m")] + [UI_FloatEdit (sigFigs = 3, unit = "m", minValue = 0.1f, maxValue = 5, incrementLarge = 1.25f, incrementSmall = 0.125f, incrementSlide = 0.001f)] + public float baseSize = 1.25f; + + [KSPField (isPersistant = true, guiActiveEditor = true, guiName = "Top", guiFormat = "S4", guiUnits = "m")] + [UI_FloatEdit (sigFigs = 3, unit = "m", minValue = 0.1f, maxValue = 5, incrementLarge = 1.25f, incrementSmall = 0.125f, incrementSlide = 0.001f)] + public float topSize = 1.25f; + + [KSPField (isPersistant = true, guiActiveEditor = true, guiName = "Height", guiFormat = "S4", guiUnits = "m")] + [UI_FloatEdit (sigFigs = 3, unit = "m", minValue = 0.1f, maxValue = 50, incrementLarge = 1.0f, incrementSmall = 0.1f, incrementSlide = 0.001f)] + public float height = 1; + + [KSPField] public string topNodeName = "top1"; + + [KSPField] public float diameterStepLarge = 1.25f; + [KSPField] public float diameterStepSmall = 0.125f; + + [KSPField] public float heightStepLarge = 1.0f; + [KSPField] public float heightStepSmall = 0.1f; + + public bool changed = true; + + abstract public float minHeight { get; } + + float lastBaseSize = -1000; + float lastTopSize = -1000; + float lastHeight = -1000; + + protected bool justLoaded; + + public virtual void checkTweakables () + { + if (!baseSize.Equals (lastBaseSize)) + { + lastBaseSize = baseSize; + + changed = true; + } + + if (!topSize.Equals (lastTopSize)) + { + lastTopSize = topSize; + + changed = true; + } + + if (!height.Equals (lastHeight)) + { + lastHeight = height; + + changed = true; + } + } + + public virtual void FixedUpdate () + { + checkTweakables (); + + if (changed) + { + updateShape(); + } + + justLoaded = false; + } + + public virtual void updateShape () + { + changed = false; + + float topheight = 0; + float topnodeheight = 0; + + var node = part.FindAttachNode ("bottom"); + + if (node != null) + { + node.size = Mathf.RoundToInt(baseSize / diameterStepLarge); + } + + node = part.FindAttachNode ("top"); + + if (node != null) + { + node.size = Mathf.RoundToInt (baseSize / diameterStepLarge); + + topheight = node.position.y; + } + + node = part.FindAttachNode (topNodeName); + + if (node != null) + { + node.position = new Vector3 (0, height, 0); + + node.size = Mathf.RoundToInt (topSize / diameterStepLarge); + + if (!justLoaded) + { + PFUtils.updateAttachedPartPos (node, part); + } + + topnodeheight = height; + } + else + { + Debug.LogError ("[PF]: No '" + topNodeName + "' node in part!", this); + } + + var internodes = part.FindAttachNodes ("interstage"); + + if (internodes != null) + { + var inc = (topnodeheight - topheight) / (internodes.Length / 2 + 1); + + for (int i = 0, j = 0; i < internodes.Length; i = i + 2) + { + var height = topheight + (j + 1) * inc; + + j++; + + node = internodes [i]; + + node.position.y = height; + node.size = node.size = Mathf.RoundToInt (topSize / diameterStepLarge) - 1; + + if (!justLoaded) + { + PFUtils.updateAttachedPartPos (node, part); + } + + node = internodes [i + 1]; + + node.position.y = height; + node.size = node.size = Mathf.RoundToInt (topSize / diameterStepLarge) - 1; + + if (!justLoaded) + { + PFUtils.updateAttachedPartPos (node, part); + } + } + } + } + + public override void OnStart(StartState state) + { + base.OnStart (state); + + if (state == StartState.None) + { + return; + } + + StartCoroutine (FireFirstChanged ()); + + } + + public IEnumerator FireFirstChanged () + { + while(!(part.editorStarted || part.started)) + { + yield return new WaitForFixedUpdate (); + } + + // Wait a little more... + + yield return new WaitForSeconds (.01f); + + changed = true; + } + + public override void OnLoad (ConfigNode cfg) + { + base.OnLoad (cfg); + + justLoaded = true; + changed = true; + } + } + + class ProceduralFairingAdapter : ProceduralAdapterBase, IPartCostModifier, IPartMassModifier + { + [KSPField] public float sideThickness = 0.05f / 1.25f; + [KSPField] public Vector4 specificMass = new Vector4 (0.005f, 0.011f, 0.009f, 0f); + [KSPField] public float specificBreakingForce = 6050; + [KSPField] public float specificBreakingTorque = 6050; + [KSPField] public float costPerTonne = 2000; + + [KSPField] public float dragAreaScale = 1; + + [KSPField (isPersistant = true)] + public bool topNodeDecouplesWhenFairingsGone; + + public bool isTopNodePartPresent = true; + public bool isFairingPresent = true; + + [KSPEvent (name = "decNoFairings", active = true, guiActive = true, guiActiveEditor = true, guiActiveUnfocused = true, guiName = "text")] + public void UIToggleTopNodeDecouple () + { + topNodeDecouplesWhenFairingsGone = !topNodeDecouplesWhenFairingsGone; + + UpdateUIdecNoFairingsText (topNodeDecouplesWhenFairingsGone); + } + + void UpdateUIdecNoFairingsText (bool flag) + { + if (flag) + { + Events["UIToggleTopNodeDecouple"].guiName = "Decouple when Fairing gone: Yes"; + } + else + { + Events["UIToggleTopNodeDecouple"].guiName = "Decouple when Fairing gone: No"; + } + } + + public override float minHeight + { + get + { + return baseSize * 0.2f; + } + } + + public ModifierChangeWhen GetModuleCostChangeWhen() { return ModifierChangeWhen.FIXED; } + public ModifierChangeWhen GetModuleMassChangeWhen() { return ModifierChangeWhen.FIXED; } + + public float GetModuleCost (float defcost, ModifierStagingSituation sit) + { + return totalMass * costPerTonne - defcost; + } + + public float GetModuleMass (float defmass, ModifierStagingSituation sit) + { + return totalMass - defmass; + } + + public float calcSideThickness () + { + return Mathf.Min (sideThickness * Mathf.Max (baseSize, topSize), Mathf.Min (baseSize, topSize) * 0.25f); + } + + public float topRadius + { + get + { + return topSize * 0.5f - calcSideThickness (); + } + } + + [KSPField (isPersistant = true, guiActiveEditor = true, guiName = "Extra height", guiFormat = "S4", guiUnits = "m")] + [UI_FloatEdit (sigFigs = 3, unit = "m", minValue = 0, maxValue = 50, incrementLarge = 1.0f, incrementSmall = 0.1f, incrementSlide = 0.001f)] + public float extraHeight = 0; + + public bool engineFairingRemoved; + + [KSPField (isPersistant = false, guiActive = false, guiActiveEditor = true, guiName = "Mass")] + public string massDisplay; + + [KSPField (isPersistant = false, guiActive = false, guiActiveEditor = true, guiName = "Cost")] + public string costDisplay; + + bool limitsSet; + + float lastExtraHt = -1000; + + public override void checkTweakables () + { + base.checkTweakables (); + + if (!extraHeight.Equals (lastExtraHt)) + { + lastExtraHt = extraHeight; + + changed = true; + } + } + + void RemoveTopPartJoints () + { + Part topPart = getTopPart (); + + Part bottomPart = getBottomPart (); + + if ((topPart == null ? false : (bottomPart != null))) + { + ConfigurableJoint [] components = topPart.gameObject.GetComponents(); + + for (int i = 0; i < (int) components.Length; i++) + { + ConfigurableJoint configurableJoint = components [i]; + + if (configurableJoint.connectedBody == bottomPart.Rigidbody) + { + UnityEngine.Object.Destroy (configurableJoint); + } + } + } + } + + public void Start () + { + part.mass = totalMass; + } + + public override void OnStart (StartState state) + { + base.OnStart (state); + + limitsSet = false; + + part.mass = totalMass; + + isFairingPresent = CheckForFairingPresent (); + + isTopNodePartPresent = (getTopPart () != null); + + UpdateUIdecNoFairingsText (topNodeDecouplesWhenFairingsGone); + + GameEvents.onEditorShipModified.Add (OnEditorShipModified); + GameEvents.onVesselWasModified.Add (OnVesselWasModified); + GameEvents.onVesselCreate.Add (OnVesselCreate); + GameEvents.onVesselGoOffRails.Add (OnVesselGoOffRails); + GameEvents.onVesselLoaded.Add (OnVesselLoaded); + GameEvents.onStageActivate.Add (OnStageActivate); + } + + public void OnDestroy () + { + GameEvents.onEditorShipModified.Remove (OnEditorShipModified); + GameEvents.onVesselWasModified.Remove (OnVesselWasModified); + GameEvents.onVesselCreate.Remove (OnVesselCreate); + GameEvents.onVesselGoOffRails.Remove (OnVesselGoOffRails); + GameEvents.onVesselLoaded.Remove (OnVesselLoaded); + GameEvents.onStageActivate.Remove (OnStageActivate); + } + + bool isShipModified = true; + bool isStaged; + + int stageNum; + + // Lets catch some events... + + void OnEditorShipModified (ShipConstruct sc) + { + isShipModified = true; + } + + void OnVesselWasModified (Vessel ves) + { + isShipModified = true; + } + + void OnVesselCreate (Vessel ves) + { + isShipModified = true; + } + + void OnVesselGoOffRails (Vessel ves) + { + isShipModified = true; + } + + void OnVesselLoaded (Vessel ves) + { + isShipModified = true; + } + + void OnStageActivate (int stage) + { + isStaged = true; + + stageNum = stage; + } + + public float totalMass; + + public override void updateShape () + { + base.updateShape (); + + float sth = calcSideThickness (); + + float br = baseSize * 0.5f - sth; + float scale = br * 2; + + part.mass = totalMass = ((specificMass.x * scale + specificMass.y) * scale + specificMass.z) * scale + specificMass.w; + + massDisplay = PFUtils.formatMass (totalMass); + costDisplay = PFUtils.formatCost (part.partInfo.cost + GetModuleCost (part.partInfo.cost, ModifierStagingSituation.CURRENT)); + + part.breakingForce = specificBreakingForce * Mathf.Pow (br, 2); + part.breakingTorque = specificBreakingTorque * Mathf.Pow (br, 2); + + var model = part.FindModelTransform ("model"); + + if (model != null) + { + model.localScale = Vector3.one * scale; + } + else + { + Debug.LogError("[PF]: No 'model' transform found in part!", this); + } + + part.rescaleFactor = scale; + + var node = part.FindAttachNode ("top"); + + node.position = node.originalPosition * scale; + + if (!justLoaded) + { + PFUtils.updateAttachedPartPos (node, part); + } + + var topNode = part.FindAttachNode ("top"); + var bottomNode = part.FindAttachNode ("bottom"); + + float y = (topNode.position.y + bottomNode.position.y) * 0.5f; + + int sideNodeSize = Mathf.RoundToInt(scale / diameterStepLarge) - 1; + + if (sideNodeSize < 0) + { + sideNodeSize = 0; + } + + var nodes = part.FindAttachNodes ("connect"); + + if (nodes != null) + { + for (int i = 0; i < nodes.Length; i++) + { + var n = nodes [i]; + + n.position.y = y; + n.size = sideNodeSize; + + if (!justLoaded) + { + PFUtils.updateAttachedPartPos (n, part); + } + } + } + + var topnode2 = part.FindAttachNode (topNodeName); + var internodes = part.FindAttachNodes ("interstage"); + + if (internodes != null && topnode2 != null) + { + var topheight = topNode.position.y; + var topnode2height = topnode2.position.y; + + var inc = (topnode2height - topheight) / (internodes.Length / 2 + 1); + + for (int i = 0, j = 0; i < internodes.Length; i = i + 2) + { + var height = topheight + (j + 1) * inc; + + j++; + + node = internodes [i]; + + node.position.y = height; + node.size = topNode.size; + + if (!justLoaded) + { + PFUtils.updateAttachedPartPos (node, part); + } + + node = internodes[i + 1]; + + node.position.y = height; + node.size = sideNodeSize; + + if (!justLoaded) + { + PFUtils.updateAttachedPartPos (node, part); + } + } + } + + var nnt = part.GetComponent(); + + if (nnt) + { + nnt.radius = baseSize * 0.5f; + } + + var fbase = part.GetComponent(); + + if (fbase) + { + fbase.baseSize = br * 2; + fbase.sideThickness = sth; + + fbase.needShapeUpdate = true; + } + + StartCoroutine (PFUtils.updateDragCubeCoroutine(part, dragAreaScale)); + } + + public override void FixedUpdate () + { + base.FixedUpdate (); + + if (!limitsSet && PFUtils.canCheckTech ()) + { + limitsSet = true; + + float minSize = PFUtils.getTechMinValue ("PROCFAIRINGS_MINDIAMETER", 0.25f); + float maxSize = PFUtils.getTechMaxValue ("PROCFAIRINGS_MAXDIAMETER", 30); + + PFUtils.setFieldRange (Fields["baseSize"], minSize, maxSize); + PFUtils.setFieldRange (Fields["topSize"], minSize, maxSize); + + ((UI_FloatEdit) Fields["baseSize"].uiControlEditor).incrementLarge = diameterStepLarge; + ((UI_FloatEdit) Fields["baseSize"].uiControlEditor).incrementSmall = diameterStepSmall; + ((UI_FloatEdit) Fields["topSize"].uiControlEditor).incrementLarge = diameterStepLarge; + ((UI_FloatEdit) Fields["topSize"].uiControlEditor).incrementSmall = diameterStepSmall; + + ((UI_FloatEdit) Fields["height"].uiControlEditor).incrementLarge = heightStepLarge; + ((UI_FloatEdit) Fields["height"].uiControlEditor).incrementSmall = heightStepSmall; + ((UI_FloatEdit) Fields["extraHeight"].uiControlEditor).incrementLarge = heightStepLarge; + ((UI_FloatEdit) Fields["extraHeight"].uiControlEditor).incrementSmall = heightStepSmall; + } + + if (isShipModified) + { + isShipModified = false; + + // Remove the engine fairing (if there is any) from topmost node. + + if (!engineFairingRemoved) + { + var node = part.FindAttachNode (topNodeName); + + if (node != null && node.attachedPart != null) + { + var tp = node.attachedPart; + + if (HighLogic.LoadedSceneIsEditor || !tp.packed) + { + var comps = tp.GetComponents(); + + for (int i = 0; i < comps.Length; i++) + { + var mj = comps [i]; + + var jt = tp.FindModelTransform (mj.jettisonName); + + if (jt == null) + { + jt = mj.jettisonTransform; + } + + if (jt != null) + { + jt.gameObject.SetActive (false); + } + + mj.jettisonName = null; + mj.jettisonTransform = null; + } + + if (!HighLogic.LoadedSceneIsEditor) + { + engineFairingRemoved = true; + } + } + } + } + + if (!HighLogic.LoadedSceneIsEditor) + { + if (isTopNodePartPresent) + { + var tp = getTopPart (); + + if (tp == null) + { + isTopNodePartPresent = false; + + Events["UIToggleTopNodeDecouple"].guiActive = false; + } + else + { + if (topNodeDecouplesWhenFairingsGone && !CheckForFairingPresent ()) + { + PartModule item = part.Modules["ModuleDecouple"]; + + if (item == null) + { + Debug.LogError ("[PF]: Cannot decouple from top part!", this); + } + else + { + RemoveTopPartJoints (); + + ((ModuleDecouple)item).Decouple (); + + part.stackIcon.RemoveIcon (); + + StageManager.Instance.SortIcons (true); + + isFairingPresent = false; + isTopNodePartPresent = false; + + Events["UIToggleTopNodeDecouple"].guiActive = false; + } + } + } + } + + if (isStaged) + { + isStaged = false; + + if (part != null) + { + if (stageNum == part.inverseStage) + { + part.stackIcon.RemoveIcon (); + + StageManager.Instance.SortIcons (true); + + Events["UIToggleTopNodeDecouple"].guiActive = false; + } + } + } + } + } + } + + public Part getBottomPart () + { + Part part; + + AttachNode attachNode = base.part.FindAttachNode ("bottom"); + + if (attachNode != null) + { + part = attachNode.attachedPart; + } + else + { + part = null; + } + + return part; + } + + public bool CheckForFairingPresent() + { + if (!isFairingPresent) + { + return false; + } + + var nodes = part.FindAttachNodes ("connect"); + + if (nodes == null) + { + return false; + } + + for (int i = 0; i < nodes.Length; i++) + { + var n = nodes [i]; + + if (n.attachedPart != null) + { + return true; + } + } + + return false; + } + + public Part getTopPart () + { + var node = part.FindAttachNode (topNodeName); + + if (node == null) + { + return null; + } + + return node.attachedPart; + } + + public override void OnLoad (ConfigNode cfg) + { + base.OnLoad (cfg); + + if (cfg.HasValue ("baseRadius") && cfg.HasValue ("topRadius")) + { + // Load legacy settings. + + float br = float.Parse (cfg.GetValue ("baseRadius")); + float tr = float.Parse (cfg.GetValue ("topRadius")); + + baseSize = (br + sideThickness * br) * 2; + topSize = (tr + sideThickness * br) * 2; + + sideThickness *= 1.15f / 1.25f; + } + } + } +} diff --git a/Source/ProceduralFairings/ProceduralFairings.csproj b/Source/ProceduralFairings/ProceduralFairings.csproj new file mode 100644 index 0000000..a21477d --- /dev/null +++ b/Source/ProceduralFairings/ProceduralFairings.csproj @@ -0,0 +1,82 @@ + + + + Debug + AnyCPU + {51CE67F2-5981-4AD2-97E8-0E2B44792AB2} + Library + ProceduralFairings + ProceduralFairings + v3.5 + False + OnBuildSuccess + False + False + False + ..\Builds\$(Configuration)\ + + + true + PdbOnly + False + ..\..\GameData\ProceduralFairings\Plugins\ + prompt + 4 + false + + + None + True + ..\..\GameData\ProceduralFairings\Plugins\ + prompt + 4 + false + + + False + ..\Builds\ + DEBUG; + Project + + + 4194304 + AnyCPU + False + Auto + 4096 + + + False + ..\Builds\ + Project + + + + + + + + + + + + + + + + + C:\Program Files %28x86%29\Steam\SteamApps\common\Kerbal Space Program\KSP_Data\Managed\Assembly-CSharp.dll + False + + + + C:\Program Files %28x86%29\Steam\SteamApps\common\Kerbal Space Program\KSP_Data\Managed\UnityEngine.dll + False + + + C:\Program Files %28x86%29\Steam\SteamApps\common\Kerbal Space Program\KSP_Data\Managed\UnityEngine.UI.dll + False + + + + \ No newline at end of file diff --git a/Source/ProceduralFairings/Properties/AssemblyInfo.cs b/Source/ProceduralFairings/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..369b1a6 --- /dev/null +++ b/Source/ProceduralFairings/Properties/AssemblyInfo.cs @@ -0,0 +1,37 @@ +using System; +using System.Reflection; +using System.Runtime.InteropServices; + +// General Information about the specified assembly. + +[assembly: AssemblyTitle ("ProceduralFairings")] +[assembly: AssemblyDescription ("Procedural Fairings for KSP")] +[assembly: AssemblyCopyright ("Copyright © 2016 - 2018, e-dog, rsparkyc")] +[assembly: AssemblyConfiguration ("Release")] +[assembly: AssemblyProduct ("ProceduralFairings")] +[assembly: AssemblyTrademark ("")] +[assembly: AssemblyCulture ("")] + +// Hide the specified assembly from any COM components. + +[assembly: ComVisible (false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM. + +[assembly: Guid ("71c1f577-92c0-4ad6-9d1c-58a6eb02a0bc")] + +// Assembly version information. Consists of the following four values: +// +// • Major Version +// • Minor Version +// • Build Number +// • Revision + +[assembly: AssemblyVersion ("1.3.1.1891")] +[assembly: AssemblyFileVersion ("1.3.1.1")] + +// The KSPAssembly attribute can be used to ensure that the plugin assemblies +// are loaded in the correct order. +// This attribute is not currently used but it is included here for completeness. + +[assembly: KSPAssembly ("ProceduralFairings", 1, 0)] diff --git a/Source/ProceduralFairings/Resizers.cs b/Source/ProceduralFairings/Resizers.cs new file mode 100644 index 0000000..5541cc8 --- /dev/null +++ b/Source/ProceduralFairings/Resizers.cs @@ -0,0 +1,332 @@ +// ================================================== +// Procedural Fairings plug-in by Alexey Volynskov. + +// Licensed under CC-BY-4.0 terms: https://creativecommons.org/licenses/by/4.0/legalcode +// ================================================== + +using System; +using UnityEngine; + +namespace Keramzit +{ + public abstract class KzPartResizer : PartModule, IPartCostModifier, IPartMassModifier + { + [KSPField (isPersistant = true, guiActiveEditor = true, guiName = "Size", guiFormat = "S4", guiUnits = "m")] + [UI_FloatEdit (sigFigs = 3, unit = "m", minValue = 0.1f, maxValue = 5, incrementLarge = 1.25f, incrementSmall = 0.125f, incrementSlide = 0.001f)] + public float size = 1.25f; + + [KSPField] public float diameterStepLarge = 1.25f; + [KSPField] public float diameterStepSmall = 0.125f; + + [KSPField] public Vector4 specificMass = new Vector4 (0.005f, 0.011f, 0.009f, 0f); + [KSPField] public float specificBreakingForce = 1536; + [KSPField] public float specificBreakingTorque = 1536; + [KSPField] public float costPerTonne = 2000; + + [KSPField] public string minSizeName = "PROCFAIRINGS_MINDIAMETER"; + [KSPField] public string maxSizeName = "PROCFAIRINGS_MAXDIAMETER"; + + [KSPField] public float dragAreaScale = 1; + + [KSPField(isPersistant = false, guiActive = false, guiActiveEditor = true, guiName = "Mass")] + public string massDisplay; + + [KSPField(isPersistant = false, guiActive = false, guiActiveEditor = true, guiName = "Cost")] + public string costDisplay; + + protected float oldSize = -1000; + protected bool justLoaded, limitsSet; + + public ModifierChangeWhen GetModuleCostChangeWhen () { return ModifierChangeWhen.FIXED; } + public ModifierChangeWhen GetModuleMassChangeWhen () { return ModifierChangeWhen.FIXED; } + + public float GetModuleCost (float defcost, ModifierStagingSituation sit) + { + return totalMass * costPerTonne - defcost; + } + + public float GetModuleMass (float defmass, ModifierStagingSituation sit) + { + return totalMass - defmass; + } + + public void Start () + { + part.mass = totalMass; + } + + public override void OnStart (StartState state) + { + base.OnStart (state); + + limitsSet = false; + + updateNodeSize (size); + + part.mass = totalMass; + } + + public override void OnLoad (ConfigNode cfg) + { + base.OnLoad (cfg); + + justLoaded = true; + + if (HighLogic.LoadedSceneIsEditor || HighLogic.LoadedSceneIsFlight) + { + updateNodeSize (size); + } + } + + public virtual void FixedUpdate () + { + if (!limitsSet && PFUtils.canCheckTech ()) + { + limitsSet = true; + + float minSize = PFUtils.getTechMinValue (minSizeName, 0.25f); + float maxSize = PFUtils.getTechMaxValue (maxSizeName, 30); + + PFUtils.setFieldRange (Fields["size"], minSize, maxSize); + + ((UI_FloatEdit) Fields["size"].uiControlEditor).incrementLarge = diameterStepLarge; + ((UI_FloatEdit) Fields["size"].uiControlEditor).incrementSmall = diameterStepSmall; + } + + if (!size.Equals (oldSize)) + { + resizePart (size); + + StartCoroutine (PFUtils.updateDragCubeCoroutine (part, dragAreaScale)); + } + + justLoaded = false; + } + + public void scaleNode (AttachNode node, float scale, bool setSize) + { + if (node == null) + { + return; + } + + node.position = node.originalPosition * scale; + + if (!justLoaded) + { + PFUtils.updateAttachedPartPos (node, part); + } + + if (setSize) + { + node.size = Mathf.RoundToInt (scale / diameterStepLarge); + } + + if (node.attachedPart != null) + { + var baseEventDatum = new BaseEventDetails (0); + + baseEventDatum.Set("location", node.position); + baseEventDatum.Set("orientation", node.orientation); + baseEventDatum.Set("secondaryAxis", node.secondaryAxis); + baseEventDatum.Set("node", node); + + node.attachedPart.SendEvent ("OnPartAttachNodePositionChanged", baseEventDatum); + } + } + + public void setNodeSize (AttachNode node, float scale) + { + if (node == null) + { + return; + } + + node.size = Mathf.RoundToInt (scale / diameterStepLarge); + } + + public virtual void updateNodeSize (float scale) + { + setNodeSize (part.FindAttachNode ("top"), scale); + setNodeSize (part.FindAttachNode ("bottom"), scale); + + var nodes = part.FindAttachNodes ("interstage"); + + if (nodes != null) + { + for (int i = 0; i < nodes.Length; i++) + { + setNodeSize (nodes [i], scale); + } + } + } + + public float totalMass; + + public virtual void resizePart (float scale) + { + oldSize = size; + + part.mass = totalMass = ((specificMass.x * scale + specificMass.y) * scale + specificMass.z) * scale + specificMass.w; + + massDisplay = PFUtils.formatMass (totalMass); + costDisplay = PFUtils.formatCost (part.partInfo.cost + GetModuleCost(part.partInfo.cost, ModifierStagingSituation.CURRENT) + part.partInfo.cost); + + part.breakingForce = specificBreakingForce * Mathf.Pow (scale, 2); + part.breakingTorque = specificBreakingTorque * Mathf.Pow (scale, 2); + + var model = part.FindModelTransform ("model"); + + if (model != null) + { + model.localScale = Vector3.one * scale; + } + else + { + Debug.LogError ("[PF]: No 'model' transform found in part!", this); + } + + part.rescaleFactor = scale; + + scaleNode(part.FindAttachNode ("top"), scale, true); + scaleNode(part.FindAttachNode ("bottom"), scale, true); + + var nodes = part.FindAttachNodes ("interstage"); + + if (nodes != null) + { + for (int i = 0; i < nodes.Length; i++) + { + scaleNode (nodes [i], scale, true); + } + } + } + } + + public class KzFairingBaseResizer : KzPartResizer + { + [KSPField] public float sideThickness = 0.05f / 1.25f; + + public float calcSideThickness () + { + return Mathf.Min (sideThickness * size, size * 0.25f); + } + + public override void updateNodeSize (float scale) + { + float sth = calcSideThickness (); + + float br = size * 0.5f - sth; + scale = br * 2; + + base.updateNodeSize (scale); + + int sideNodeSize = Mathf.RoundToInt (scale / diameterStepLarge) - 1; + + if (sideNodeSize < 0) + { + sideNodeSize = 0; + } + + var nodes = part.FindAttachNodes ("connect"); + + for (int i = 0; i < nodes.Length; i++) + { + var n = nodes [i]; + + n.size = sideNodeSize; + } + } + + public override void resizePart (float scale) + { + float sth = calcSideThickness (); + + float br = size * 0.5f - sth; + scale = br * 2; + + base.resizePart (scale); + + var topNode = part.FindAttachNode ("top"); + var bottomNode = part.FindAttachNode ("bottom"); + + float y = (topNode.position.y + bottomNode.position.y) * 0.5f; + + int sideNodeSize = Mathf.RoundToInt(scale / diameterStepLarge) - 1; + + if (sideNodeSize < 0) + { + sideNodeSize = 0; + } + + var nodes = part.FindAttachNodes ("connect"); + + for (int i = 0; i < nodes.Length; i++) + { + var n = nodes [i]; + + n.position.y = y; + n.size = sideNodeSize; + + if (!justLoaded) + { + PFUtils.updateAttachedPartPos (n, part); + } + } + + var nnt = part.GetComponent(); + + if (nnt) + { + nnt.radius = size * 0.5f; + } + + var fbase = part.GetComponent(); + + if (fbase) + { + fbase.baseSize = br * 2; + fbase.sideThickness = sth; + fbase.needShapeUpdate = true; + } + } + } + + public class KzThrustPlateResizer : KzPartResizer + { + public override void resizePart (float scale) + { + base.resizePart (scale); + + var node = part.FindAttachNode ("bottom"); + + var nodes = part.FindAttachNodes ("bottom"); + + for (int i = 0; i < nodes.Length; i++) + { + var n = nodes [i]; + + n.position.y = node.position.y; + + if (!justLoaded) + { + PFUtils.updateAttachedPartPos (n, part); + } + } + + var nnt = part.GetComponent(); + + if (nnt) + { + float mr = size * 0.5f; + + if (nnt.radius > mr) + { + nnt.radius = mr; + } + + ((UI_FloatEdit) nnt.Fields["radius"].uiControlEditor).maxValue = mr; + } + } + } +} diff --git a/Source/ProceduralFairings/Utilities.cs b/Source/ProceduralFairings/Utilities.cs new file mode 100644 index 0000000..d5342bc --- /dev/null +++ b/Source/ProceduralFairings/Utilities.cs @@ -0,0 +1,437 @@ +// ================================================== +// Procedural Fairings plug-in by Alexey Volynskov. + +// Licensed under CC-BY-4.0 terms: https://creativecommons.org/licenses/by/4.0/legalcode +// ================================================== + +using System; +using System.Collections.Generic; +using UnityEngine; + +namespace Keramzit +{ + struct BezierSlope + { + Vector2 p1, p2; + + public BezierSlope (Vector4 v) + { + p1 = new Vector2 (v.x, v.y); + p2 = new Vector2 (v.z, v.w); + } + + public Vector2 interp (float t) + { + Vector2 a = Vector2.Lerp (Vector2.zero, p1, t); + Vector2 b = Vector2.Lerp (p1, p2, t); + Vector2 c = Vector2.Lerp (p2, Vector2.one, t); + Vector2 d = Vector2.Lerp (a, b, t); + Vector2 e = Vector2.Lerp (b, c, t); + + return Vector2.Lerp (d, e, t); + } + } + + class Tuple + { + internal T1 Item1 { get; set; } + internal T2 Item2 { get; set; } + + public Tuple (T1 item1, T2 item2) + { + this.Item1 = item1; + this.Item2 = item2; + } + } + + public static class PFUtils + { + public static bool canCheckTech () + { + return HighLogic.LoadedSceneIsEditor && (ResearchAndDevelopment.Instance != null || (HighLogic.CurrentGame.Mode != Game.Modes.CAREER && HighLogic.CurrentGame.Mode != Game.Modes.SCIENCE_SANDBOX)); + } + + public static bool haveTech (string name) + { + if (HighLogic.CurrentGame.Mode != Game.Modes.CAREER && HighLogic.CurrentGame.Mode != Game.Modes.SCIENCE_SANDBOX) + { + return name == "sandbox"; + } + + return ResearchAndDevelopment.GetTechnologyState (name) == RDTech.State.Available; + } + + public static float getTechMinValue (string cfgname, float defVal) + { + bool hasValue = false; + float minVal = 0; + + var confnodes = GameDatabase.Instance.GetConfigNodes (cfgname); + + for (int j = 0; j < confnodes.Length; j++) + { + var tech = confnodes [j]; + + for (int i = 0; i < tech.values.Count; ++i) + { + var value = tech.values [i]; + + if (!haveTech(value.name)) + { + continue; + } + + float v = float.Parse (value.value); + + if (!hasValue || v < minVal) + { + minVal = v; hasValue = true; + } + } + } + + if (!hasValue) + { + return defVal; + } + + return minVal; + } + + public static float getTechMaxValue (string cfgname, float defVal) + { + bool hasValue = false; + float maxVal = 0; + + var confnodes = GameDatabase.Instance.GetConfigNodes (cfgname); + + for (int j = 0; j < confnodes.Length; j++) + { + var tech = confnodes [j]; + + for (int i = 0; i < tech.values.Count; ++i) + { + var value = tech.values [i]; + + if (!haveTech(value.name)) + { + continue; + } + + float v = float.Parse (value.value); + + if (!hasValue || v > maxVal) + { + maxVal = v; hasValue = true; + } + } + } + + if (!hasValue) + { + return defVal; + } + + return maxVal; + } + + public static void setFieldRange (BaseField field, float minval, float maxval) + { + var fr = field.uiControlEditor as UI_FloatRange; + + if (fr != null) + { + fr.minValue = minval; + fr.maxValue = maxval; + } + + var fe = field.uiControlEditor as UI_FloatEdit; + + if (fe != null) + { + fe.minValue = minval; + fe.maxValue = maxval; + } + } + + public static void updateAttachedPartPos (AttachNode node, Part part) + { + if (node == null || part == null) + { + return; + } + + var ap = node.attachedPart; + + if (!ap) + { + return; + } + + var an = ap.FindAttachNodeByPart (part); + + if (an == null) + { + return; + } + + var dp = part.transform.TransformPoint (node.position) - ap.transform.TransformPoint (an.position); + + if (ap == part.parent) + { + while (ap.parent) ap = ap.parent; + ap.transform.position += dp; + part.transform.position -= dp; + } + else + { + ap.transform.position += dp; + } + } + + public static string formatMass (float mass) + { + if (mass < 0.01f) + { + return (mass * 1e3f).ToString ("n3") + "kg"; + } + + return mass.ToString("n3") + "t"; + } + + public static string formatCost (float cost) + { + return cost.ToString ("n0"); + } + + public static void enableRenderer (Transform t, bool e) + { + if (!t) + { + return; + } + + var r = t.GetComponent(); + + if (r) + { + r.enabled = e; + } + } + + public static void hideDragStuff (Part part) + { + enableRenderer (part.FindModelTransform ("dragOnly"), false); + } + + public static bool FARinstalled, FARchecked; + + public static bool isFarInstalled () + { + if (!FARchecked) + { + var asmlist = AssemblyLoader.loadedAssemblies; + + if (asmlist != null) + { + for (int i = 0; i < asmlist.Count; i++) + { + if (asmlist[i].name == "FerramAerospaceResearch") + { + FARinstalled = true; + + break; + } + } + } + + FARchecked = true; + } + + return FARinstalled; + } + + public static void updateDragCube (Part part, float areaScale) + { + if (isFarInstalled ()) + { + Debug.Log ("[PF]: Calling FAR to update voxels..."); + + part.SendMessage ("GeometryPartModuleRebuildMeshData"); + } + + if (!HighLogic.LoadedSceneIsFlight) + { + return; + } + + enableRenderer (part.FindModelTransform ("dragOnly"), true); + + var dragCube = DragCubeSystem.Instance.RenderProceduralDragCube (part); + + enableRenderer (part.FindModelTransform ("dragOnly"), false); + + for (int i = 0; i < 6; ++i) + { + dragCube.Area [i] *= areaScale; + } + + part.DragCubes.ClearCubes (); + part.DragCubes.Cubes.Add (dragCube); + part.DragCubes.ResetCubeWeights (); + } + + public static IEnumerator updateDragCubeCoroutine (Part part, float areaScale) + { + while (true) + { + if (part == null || part.Equals(null)) + { + yield break; + } + + if (HighLogic.LoadedSceneIsFlight) + { + if (part.vessel == null || part.vessel.Equals(null)) + { + yield break; + } + + if (!FlightGlobals.ready || part.packed || !part.vessel.loaded) + { + yield return new WaitForFixedUpdate (); + + continue; + } + + break; + } + + if (HighLogic.LoadedSceneIsEditor) + { + yield return new WaitForFixedUpdate (); + + break; + } + + yield break; + } + + PFUtils.updateDragCube (part, areaScale); + } + + public static void refreshPartWindow () + { + var objs = UnityEngine.Object.FindObjectsOfType(); + + for (int i = 0; i < objs.Length; i++) + { + var w = objs [i]; + + w.displayDirty = true; + } + } + + public static Part partFromHit (this RaycastHit hit) + { + if (hit.collider == null || hit.collider.gameObject == null) + { + return null; + } + + var go = hit.collider.gameObject; + + var p = Part.FromGO (go); + + while (p == null) + { + if (go.transform != null && go.transform.parent != null && go.transform.parent.gameObject != null) + { + go = go.transform.parent.gameObject; + } + else + { + break; + } + + p = Part.FromGO (go); + } + + return p; + } + + public static List getAllChildrenRecursive (this Part rootPart, bool root) + { + var children = new List(); + + if (!root) + { + children.Add (rootPart); + } + + for (int i = 0; i < rootPart.children.Count; i++) + { + var child = rootPart.children [i]; + + children.AddRange (child.getAllChildrenRecursive (false)); + } + + return children; + } + + public static float GetMaxValueFromList (List list) + { + float max = 0; + + for (int i = 0; i < list.Count; i++) + { + if (max < list [i]) + { + max = list [i]; + } + } + + return max; + } + } + + [KSPAddon (KSPAddon.Startup.EditorAny, false)] + + public class EditorScreenMessager : MonoBehaviour + { + static float osdMessageTime; + static string osdMessageText; + + public static void showMessage (string msg, float delay) + { + osdMessageText = msg; + osdMessageTime = Time.time + delay; + } + + void OnGUI () + { + if (!HighLogic.LoadedSceneIsEditor) + { + return; + } + + if (Time.time < osdMessageTime) + { + GUI.skin = HighLogic.Skin; + + var style = new GUIStyle ("Label"); + + style.alignment = TextAnchor.MiddleCenter; + style.fontSize = 20; + style.normal.textColor = Color.black; + + GUI.Label (new Rect (2, 2 + (Screen.height / 9), Screen.width, 50), osdMessageText, style); + + style.normal.textColor = Color.yellow; + + GUI.Label (new Rect (0, Screen.height / 9, Screen.width, 50), osdMessageText, style); + } + } + } + +} diff --git a/Source/Properties/AssemblyInfo.cs b/Source/Properties/AssemblyInfo.cs deleted file mode 100644 index 3ad1ed9..0000000 --- a/Source/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// Allgemeine Informationen über eine Assembly werden über die folgenden -// Attribute gesteuert. Ändern Sie diese Attributwerte, um die Informationen zu ändern, -// die mit einer Assembly verknüpft sind. -[assembly: AssemblyTitle("ProceduralFairings")] -[assembly: AssemblyDescription("Procedural Fairings 4.0 for KSP 1.3")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("ProceduralFairings.Properties")] -[assembly: AssemblyCopyright("Copyright © 2017")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Durch Festlegen von ComVisible auf "false" werden die Typen in dieser Assembly unsichtbar -// für COM-Komponenten. Wenn Sie auf einen Typ in dieser Assembly von -// COM zugreifen müssen, legen Sie das ComVisible-Attribut für diesen Typ auf "true" fest. -[assembly: ComVisible(false)] - -// Die folgende GUID bestimmt die ID der Typbibliothek, wenn dieses Projekt für COM verfügbar gemacht wird -[assembly: Guid("71c1f577-92c0-4ad6-9d1c-58a6eb02a0bc")] - -// Versionsinformationen für eine Assembly bestehen aus den folgenden vier Werten: -// -// Hauptversion -// Nebenversion -// Buildnummer -// Revision -// -[assembly: AssemblyVersion("0.0.4.0")] -[assembly: AssemblyFileVersion("0.0.4.0")] diff --git a/Source/Resizers.cs b/Source/Resizers.cs deleted file mode 100644 index d29e5a7..0000000 --- a/Source/Resizers.cs +++ /dev/null @@ -1,281 +0,0 @@ -// Procedural Fairings plug-in by Alexey Volynskov -// Licensed under CC BY 3.0 terms: http://creativecommons.org/licenses/by/3.0/ -using System; -using System.Collections.Generic; -using System.Text; -using UnityEngine; - - -namespace Keramzit -{ - - - public abstract class KzPartResizer : PartModule, IPartCostModifier, IPartMassModifier - { - [KSPField(isPersistant = true, guiActiveEditor = true, guiName = "Size", guiFormat = "S4", guiUnits = "m")] - [UI_FloatEdit(sigFigs = 3, unit = "m", minValue = 0.1f, maxValue = 5, incrementLarge = 1.25f, incrementSmall = 0.125f, incrementSlide = 0.001f)] - public float size = 1.25f; - - [KSPField] public float diameterStepLarge = 1.25f; - [KSPField] public float diameterStepSmall = 0.125f; - - [KSPField] public Vector4 specificMass = new Vector4(0.005f, 0.011f, 0.009f, 0f); - [KSPField] public float specificBreakingForce = 1536; - [KSPField] public float specificBreakingTorque = 1536; - [KSPField] public float costPerTonne = 2000; - - [KSPField] public string minSizeName = "PROCFAIRINGS_MINDIAMETER"; - [KSPField] public string maxSizeName = "PROCFAIRINGS_MAXDIAMETER"; - - [KSPField] public float dragAreaScale = 1; - - [KSPField(isPersistant = false, guiActive = false, guiActiveEditor = true, guiName = "Mass")] - public string massDisplay; - - [KSPField(isPersistant = false, guiActive = false, guiActiveEditor = true, guiName = "Cost")] - public string costDisplay; - - - protected float oldSize = -1000; - protected bool justLoaded = false, limitsSet = false; - - // ProceduralParts needs this - // [KSPAPIExtensions.PartMessage.PartMessageEvent(false)] - // public event KSPAPIExtensions.PartMessage.PartAttachNodePositionChanged AttachNodeChanged; - - public ModifierChangeWhen GetModuleCostChangeWhen() { return ModifierChangeWhen.FIXED; } - public ModifierChangeWhen GetModuleMassChangeWhen() { return ModifierChangeWhen.FIXED; } - - public float GetModuleCost(float defcost, ModifierStagingSituation sit) - { - return totalMass * costPerTonne - defcost; - } - - public float GetModuleMass(float defmass, ModifierStagingSituation sit) - { - return totalMass - defmass; - } - - - public void Start() - { - part.mass = totalMass; - } - public override void OnStart(StartState state) - { - base.OnStart(state); - // KSPAPIExtensions.PartMessage.PartMessageService.Register(this); - limitsSet = false; - updateNodeSize(size); - part.mass = totalMass; - } - - - public override void OnLoad(ConfigNode cfg) - { - base.OnLoad(cfg); - justLoaded = true; - if (HighLogic.LoadedSceneIsEditor || HighLogic.LoadedSceneIsFlight) updateNodeSize(size); - } - - - public virtual void FixedUpdate() - { - if (!limitsSet && PFUtils.canCheckTech()) { - limitsSet = true; - float minSize = PFUtils.getTechMinValue(minSizeName, 0.25f); - float maxSize = PFUtils.getTechMaxValue(maxSizeName, 30); - - PFUtils.setFieldRange(Fields["size"], minSize, maxSize); - - ((UI_FloatEdit)Fields["size"].uiControlEditor).incrementLarge = diameterStepLarge; - ((UI_FloatEdit)Fields["size"].uiControlEditor).incrementSmall = diameterStepSmall; - } - - if (size != oldSize) { - resizePart(size); - StartCoroutine(PFUtils.updateDragCubeCoroutine(part, dragAreaScale)); - } - - justLoaded = false; - } - - - public void scaleNode(AttachNode node, float scale, bool setSize) - { - if (node == null) return; - node.position = node.originalPosition * scale; - if (!justLoaded) PFUtils.updateAttachedPartPos(node, part); - if (setSize) node.size = Mathf.RoundToInt(scale / diameterStepLarge); - - if (node.attachedPart != null) { - BaseEventDetails baseEventDatum = new BaseEventDetails(0); - baseEventDatum.Set("location", node.position); - baseEventDatum.Set("orientation", node.orientation); - baseEventDatum.Set("secondaryAxis", node.secondaryAxis); - baseEventDatum.Set("node", node); - node.attachedPart.SendEvent("OnPartAttachNodePositionChanged", baseEventDatum); - } - - - // Tell ProceduralParts, so it can update its node stuff... - // AttachNodeChanged(node, node.position, node.orientation, node.secondaryAxis); - } - - - public void setNodeSize(AttachNode node, float scale) - { - if (node == null) return; - node.size = Mathf.RoundToInt(scale / diameterStepLarge); - } - - - public virtual void updateNodeSize(float scale) - { - setNodeSize(part.FindAttachNode("top"), scale); - setNodeSize(part.FindAttachNode("bottom"), scale); - - var nodes = part.FindAttachNodes("interstage"); - if (nodes != null) { - for (int i = 0; i < nodes.Length; i++) { - setNodeSize(nodes[i], scale); - } - } - } - - public float totalMass; - public virtual void resizePart(float scale) - { - oldSize = size; - - part.mass = totalMass = ((specificMass.x * scale + specificMass.y) * scale + specificMass.z) * scale + specificMass.w; - massDisplay = PFUtils.formatMass(totalMass); - costDisplay = PFUtils.formatCost(part.partInfo.cost + GetModuleCost(part.partInfo.cost, ModifierStagingSituation.CURRENT) + part.partInfo.cost); - part.breakingForce = specificBreakingForce * Mathf.Pow(scale, 2); - part.breakingTorque = specificBreakingTorque * Mathf.Pow(scale, 2); - - var model = part.FindModelTransform("model"); - if (model != null) model.localScale = Vector3.one * scale; - else Debug.LogError("[KzPartResizer] No 'model' transform in the part", this); - part.rescaleFactor = scale; - - scaleNode(part.FindAttachNode("top"), scale, true); - scaleNode(part.FindAttachNode("bottom"), scale, true); - - var nodes = part.FindAttachNodes("interstage"); - if (nodes != null) { - for (int i = 0; i < nodes.Length; i++) { - scaleNode(nodes[i], scale, true); - } - } - - - } - } - - - //ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ// - - - public class KzFairingBaseResizer : KzPartResizer - { - [KSPField] public float sideThickness = 0.05f / 1.25f; - - - public float calcSideThickness() - { - return Mathf.Min(sideThickness * size, size * 0.25f); - } - - - public override void updateNodeSize(float scale) - { - float sth = calcSideThickness(); - float br = size * 0.5f - sth; - scale = br * 2; - - base.updateNodeSize(scale); - - int sideNodeSize = Mathf.RoundToInt(scale / diameterStepLarge) - 1; - if (sideNodeSize < 0) sideNodeSize = 0; - - var nodes = part.FindAttachNodes("connect"); - for (int i = 0; i < nodes.Length; i++) { - var n = nodes[i]; - n.size = sideNodeSize; - } - - } - - - public override void resizePart(float scale) - { - float sth = calcSideThickness(); - float br = size * 0.5f - sth; - scale = br * 2; - - base.resizePart(scale); - - var topNode = part.FindAttachNode("top"); - var bottomNode = part.FindAttachNode("bottom"); - - float y = (topNode.position.y + bottomNode.position.y) * 0.5f; - int sideNodeSize = Mathf.RoundToInt(scale / diameterStepLarge) - 1; - if (sideNodeSize < 0) sideNodeSize = 0; - - var nodes = part.FindAttachNodes("connect"); - for (int i = 0; i < nodes.Length; i++) { - var n = nodes[i]; - n.position.y = y; - n.size = sideNodeSize; - if (!justLoaded) PFUtils.updateAttachedPartPos(n, part); - } - - var nnt = part.GetComponent(); - if (nnt) { - nnt.radius = size * 0.5f; - } - - var fbase = part.GetComponent(); - if (fbase) { - fbase.baseSize = br * 2; - fbase.sideThickness = sth; - fbase.needShapeUpdate = true; - } - } - } - - - //ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ// - - - public class KzThrustPlateResizer : KzPartResizer - { - public override void resizePart(float scale) - { - base.resizePart(scale); - - var node = part.FindAttachNode("bottom"); - - var nodes = part.FindAttachNodes("bottom"); - for (int i = 0; i < nodes.Length; i++) { - var n = nodes[i]; - n.position.y = node.position.y; - if (!justLoaded) PFUtils.updateAttachedPartPos(n, part); - } - - var nnt = part.GetComponent(); - if (nnt) { - float mr = size * 0.5f; - if (nnt.radius > mr) nnt.radius = mr; - ((UI_FloatEdit)nnt.Fields["radius"].uiControlEditor).maxValue = mr; - } - } - } - - - //ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ// - - -} // namespace - diff --git a/Source/build.bat b/Source/build.bat deleted file mode 100644 index e1bac14..0000000 --- a/Source/build.bat +++ /dev/null @@ -1,13 +0,0 @@ -@echo off -set KSP=C:\games\KSPtest -%WINDIR%\Microsoft.NET\Framework64\v3.5\csc /nologo /t:library^ - /out:"%KSP%\GameData\ProceduralFairings\ProceduralFairings.dll"^ - /r:"%KSP%\KSP_Data\Managed\Assembly-CSharp.dll"^ - /r:"%KSP%\KSP_Data\Managed\UnityEngine.dll"^ - /r:"%KSP%\KSP_Data\Managed\UnityEngine.UI.dll"^ - NodeNumberTweaker.cs Resizers.cs FairingShielding.cs ^ - FairingBase.cs FairingDecoupler.cs FairingSide.cs PayloadScan.cs ProcAdapter.cs utils.cs -if errorlevel 1 goto exit -rem copy /y %KSP%\GameData\ProceduralFairings\ProceduralFairings.dll "C:\Steam\SteamApps\common\Kerbal Space Program\GameData\ProceduralFairings\" -echo ====================================== -:exit diff --git a/Source/s.bat b/Source/s.bat deleted file mode 100644 index 61d39c2..0000000 --- a/Source/s.bat +++ /dev/null @@ -1,4 +0,0 @@ -@echo off -c: -cd c:\games\KSPtest -start KSP_x64 diff --git a/Source/utils.cs b/Source/utils.cs deleted file mode 100644 index 1961e45..0000000 --- a/Source/utils.cs +++ /dev/null @@ -1,340 +0,0 @@ -// Procedural Fairings plug-in by Alexey Volynskov -// Licensed under CC BY 3.0 terms: http://creativecommons.org/licenses/by/3.0/ -using System; -using System.Collections.Generic; -using System.Text; -using UnityEngine; - - -namespace Keramzit -{ - - - //ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ// - - - struct BezierSlope - { - Vector2 p1, p2; - - public BezierSlope(Vector4 v) - { - p1 = new Vector2(v.x, v.y); - p2 = new Vector2(v.z, v.w); - } - - public Vector2 interp(float t) - { - Vector2 a = Vector2.Lerp(Vector2.zero, p1, t); - Vector2 b = Vector2.Lerp(p1, p2, t); - Vector2 c = Vector2.Lerp(p2, Vector2.one, t); - - Vector2 d = Vector2.Lerp(a, b, t); - Vector2 e = Vector2.Lerp(b, c, t); - - return Vector2.Lerp(d, e, t); - } - } - - - //ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ// - - internal class Tuple - { - internal T1 Item1 { get; set; } - internal T2 Item2 { get; set; } - - public Tuple(T1 item1, T2 item2) - { - this.Item1 = item1; - this.Item2 = item2; - } - } - - //ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ// - - public static class PFUtils - { - public static bool canCheckTech() - { - return HighLogic.LoadedSceneIsEditor && - (ResearchAndDevelopment.Instance != null || (HighLogic.CurrentGame.Mode != Game.Modes.CAREER && HighLogic.CurrentGame.Mode != Game.Modes.SCIENCE_SANDBOX)); - } - - - public static bool haveTech(string name) - { - if (HighLogic.CurrentGame.Mode != Game.Modes.CAREER && HighLogic.CurrentGame.Mode != Game.Modes.SCIENCE_SANDBOX) return name == "sandbox"; - return ResearchAndDevelopment.GetTechnologyState(name) == RDTech.State.Available; - } - - - public static float getTechMinValue(string cfgname, float defVal) - { - bool hasValue = false; - float minVal = 0; - - var confnodes = GameDatabase.Instance.GetConfigNodes(cfgname); - for (int j = 0; j < confnodes.Length; j++) { - var tech = confnodes[j]; - for (int i = 0; i < tech.values.Count; ++i) { - var value = tech.values[i]; - if (!haveTech(value.name)) continue; - float v = float.Parse(value.value); - if (!hasValue || v < minVal) { minVal = v; hasValue = true; } - } - } - - if (!hasValue) return defVal; - return minVal; - } - - - public static float getTechMaxValue(string cfgname, float defVal) - { - bool hasValue = false; - float maxVal = 0; - - var confnodes = GameDatabase.Instance.GetConfigNodes(cfgname); - for (int j = 0; j < confnodes.Length; j++) { - var tech = confnodes[j]; - for (int i = 0; i < tech.values.Count; ++i) { - var value = tech.values[i]; - if (!haveTech(value.name)) continue; - float v = float.Parse(value.value); - if (!hasValue || v > maxVal) { maxVal = v; hasValue = true; } - } - } - - if (!hasValue) return defVal; - return maxVal; - } - - - public static void setFieldRange(BaseField field, float minval, float maxval) - { - var fr = field.uiControlEditor as UI_FloatRange; - if (fr != null) { - fr.minValue = minval; - fr.maxValue = maxval; - } - - var fe = field.uiControlEditor as UI_FloatEdit; - if (fe != null) { - fe.minValue = minval; - fe.maxValue = maxval; - } - } - - - public static void updateAttachedPartPos(AttachNode node, Part part) - { - if (node == null || part == null) return; - - var ap = node.attachedPart; - if (!ap) return; - - var an = ap.FindAttachNodeByPart(part); - if (an == null) return; - - var dp = - part.transform.TransformPoint(node.position) - - ap.transform.TransformPoint(an.position); - - if (ap == part.parent) { - while (ap.parent) ap = ap.parent; - ap.transform.position += dp; - part.transform.position -= dp; - } - else - ap.transform.position += dp; - } - - - public static string formatMass(float mass) - { - if (mass < 0.01f) return (mass * 1e3f).ToString("n3") + "kg"; - else return mass.ToString("n3") + "t"; - } - - public static string formatCost(float cost) - { - return cost.ToString("n0"); - } - - public static void enableRenderer(Transform t, bool e) - { - if (!t) return; - var r = t.GetComponent(); - if (r) r.enabled = e; - } - - public static void hideDragStuff(Part part) - { - enableRenderer(part.FindModelTransform("dragOnly"), false); - } - - public static bool FARinstalled = false, FARchecked = false; - - public static bool isFarInstalled() - { - if (!FARchecked) { - var asmlist = AssemblyLoader.loadedAssemblies; - - if (asmlist != null) { - for (int i = 0; i < asmlist.Count; i++) { - if (asmlist[i].name == "FerramAerospaceResearch") { - FARinstalled = true; - break; - } - } - } - //FARinstalled = AssemblyLoader.loadedAssemblies.Any(a => a.assembly.GetName().Name == "FerramAerospaceResearch"); - FARchecked = true; - } - - return FARinstalled; - } - - public static void updateDragCube(Part part, float areaScale) - { - if (isFarInstalled()) { - Debug.Log("[PF] calling FAR to update voxels"); - part.SendMessage("GeometryPartModuleRebuildMeshData"); - } - - if (!HighLogic.LoadedSceneIsFlight) return; - - enableRenderer(part.FindModelTransform("dragOnly"), true); - - var dragCube = DragCubeSystem.Instance.RenderProceduralDragCube(part); - - enableRenderer(part.FindModelTransform("dragOnly"), false); - - for (int i = 0; i < 6; ++i) dragCube.Area[i] *= areaScale; - // dragCube.Area[(int)DragCube.DragFace.YP]*=areaScale; - // dragCube.Area[(int)DragCube.DragFace.YN]*=areaScale; - - // Debug.Log(part.name+" dragCube area="+dragCube.Area[(int)DragCube.DragFace.YP]+" size="+dragCube.Size+" ms="+model.localScale); - part.DragCubes.ClearCubes(); - part.DragCubes.Cubes.Add(dragCube); - part.DragCubes.ResetCubeWeights(); - } - - public static IEnumerator updateDragCubeCoroutine(Part part, float areaScale) - { - while (true) { - if (part == null || part.Equals(null)) yield break; - - if (HighLogic.LoadedSceneIsFlight) { - if (part.vessel == null || part.vessel.Equals(null)) yield break; - - if (!FlightGlobals.ready || part.packed || !part.vessel.loaded) { - yield return new WaitForFixedUpdate(); - continue; - } - break; - } - else if (HighLogic.LoadedSceneIsEditor) { - yield return new WaitForFixedUpdate(); - break; - } - else - yield break; - } - - PFUtils.updateDragCube(part, areaScale); - } - - public static void refreshPartWindow() - { - var objs = UnityEngine.Object.FindObjectsOfType(); - for (int i = 0; i < objs.Length; i++) { - var w = objs[i]; - w.displayDirty = true; - } - } - - public static Part partFromHit(this RaycastHit hit) - { - if (hit.collider == null || hit.collider.gameObject == null) { - return null; - } - var go = hit.collider.gameObject; - var p = Part.FromGO(go); - while (p == null) { - if (go.transform != null && go.transform.parent != null && go.transform.parent.gameObject != null) { - go = go.transform.parent.gameObject; - } - else { - break; - } - p = Part.FromGO(go); - } - return p; - } - - public static List getAllChildrenRecursive(this Part rootPart, bool root) - { - var children = new List(); - if (!root) { - children.Add(rootPart); - } - - for (int i = 0; i < rootPart.children.Count; i++) { - var child = rootPart.children[i]; - children.AddRange(child.getAllChildrenRecursive(false)); - } - return children; - } - - public static float GetMaxValueFromList(List list) - { - float max = 0; - for (int i = 0; i < list.Count; i++) - if (max < list[i]) - max = list[i]; - - return max; - } - } - - - //ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ// - - - [KSPAddon(KSPAddon.Startup.EditorAny, false)] - public class EditorScreenMessager : MonoBehaviour - { - static float osdMessageTime = 0; - static string osdMessageText = null; - - public static void showMessage(string msg, float delay) - { - osdMessageText = msg; - osdMessageTime = Time.time + delay; - } - - public void OnGUI() - { - if (!HighLogic.LoadedSceneIsEditor) return; - - if (Time.time < osdMessageTime) { - GUI.skin = HighLogic.Skin; - GUIStyle style = new GUIStyle("Label"); - style.alignment = TextAnchor.MiddleCenter; - style.fontSize = 20; - style.normal.textColor = Color.black; - GUI.Label(new Rect(2, 2 + (Screen.height / 9), Screen.width, 50), osdMessageText, style); - style.normal.textColor = Color.yellow; - GUI.Label(new Rect(0, Screen.height / 9, Screen.width, 50), osdMessageText, style); - } - } - } - - - //ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ// - - -} // namespace - From 2773a616abe6e1c1d221afa18593b4f1cb2914e4 Mon Sep 17 00:00:00 2001 From: PhineasFreak Date: Sun, 18 Feb 2018 11:57:40 +0200 Subject: [PATCH 02/34] Add the rest of the curve stuff * The base curve can now be edited like the nose curve. * Added an option to change the number of segments that the fairing is comprised (both base and nose). * Reshuffled the GUI elements a bit to make handling a bit easier. * Reverted the fairing side part name for compatibility with various mods that use MM to clone and edit the baseline fairing part. --- .../ProceduralFairings/Parts/fairing_side.cfg | 2 +- .../Plugins/ProceduralFairings.dll | Bin 86016 -> 86016 bytes Source/ProceduralFairings/FairingBase.cs | 4 +- Source/ProceduralFairings/FairingShielding.cs | 4 +- Source/ProceduralFairings/FairingSide.cs | 75 +++++++++++++----- 5 files changed, 59 insertions(+), 26 deletions(-) diff --git a/GameData/ProceduralFairings/Parts/fairing_side.cfg b/GameData/ProceduralFairings/Parts/fairing_side.cfg index b3bf584..e7cc975 100644 --- a/GameData/ProceduralFairings/Parts/fairing_side.cfg +++ b/GameData/ProceduralFairings/Parts/fairing_side.cfg @@ -1,6 +1,6 @@ PART { - name = KzProcFairingSideGlobal + name = KzProcFairingSide1 module = Part author = e-dog diff --git a/GameData/ProceduralFairings/Plugins/ProceduralFairings.dll b/GameData/ProceduralFairings/Plugins/ProceduralFairings.dll index a6f6c526451a8936f49180f9a69cbe37f453072a..d26e4b6c3c8191d5b5b288e924cbeb24c2880410 100644 GIT binary patch delta 17940 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 Date: Sun, 18 Feb 2018 12:01:33 +0200 Subject: [PATCH 03/34] Small Github readme edits --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 9acce60..7d45528 100644 --- a/README.md +++ b/README.md @@ -9,21 +9,21 @@ ## Installation -Extract the contents of the ProceduralFairings.zip and then drop the ProceduralFairings folder into the GameData folder. **Note:** if you are upgrading then make sure that any previous Procedural Fairings installation has been completely removed beforehand. +Extract the contents of the ProceduralFairings .zip file and then drop the ProceduralFairings folder into the GameData folder. **Note:** if you are upgrading then make sure that any previous Procedural Fairings installation has been completely removed beforehand. ### Usage -1. Put a fairing base under your payload (all Procedural Fairings parts are in the Payload tab) and a decoupler if necessary. +1. Put a fairing base under your payload (all Procedural Fairings parts are placed under the **Payload** tab) and a decoupler if necessary. 2. Attached fairings automatically reshape for your payload. 3. Enabling symmetry on fairings will encapsulate your payload -4. Rearrange stages to jettison fairings at the proper stage. +4. Rearrange the stages to jettison fairings at the proper stage. ### Notes -* All part tweakables are accessible via the right-click part action windows (diameter, length, shape etc). +* All part tweakables are accessible via the right-click part action windows (diameter, length, shape, staging etc). * Flipping another fairing base over and adding it above the payload will cause any side fairings to stick to it instead of creating a nose cone, thereby creating inline fairings between two bases. * Procedural Fairings includes low-profile base rings intended for inline fairings. Separate interstage bases are also included. -* Procedural Fairings are fully integrated with the career mode. The minimum and maximum part sizes are limited by the available tech (see "ProceduralFairings_Settings.cfg" for more details). +* Procedural Fairings are fully integrated with the career mode. The minimum and maximum part sizes are limited by the available tech (see **ProceduralFairings_Settings.cfg** for more details). ## License From 15beaba7489414b245d6210619512d15d840d288 Mon Sep 17 00:00:00 2001 From: PhineasFreak Date: Sun, 18 Feb 2018 15:15:17 +0200 Subject: [PATCH 04/34] Part updates * Save-breaking update! * Re-export all part models and textures to allow easier texture switching (KSP 1.4+). * Add support for normal maps to the fairing side model. * Add/fix some missing decoupler FX and sound definitions. * Add decoupler modules to the fairing bases and interstages (can be toggled via the PAW). * Make the fairing side invisible when hovering the cursor above it. * Add a MM patch to convert the stock fairing bases to PF ones. --- ...ons.cfg => PF_FilterExtensions_Config.cfg} | 4 +- ...gs_Settings.cfg => PF_Settings_Config.cfg} | 50 ++++----- .../Config/PF_Squad_Config.cfg | 106 ++++++++++++++++++ .../ProceduralFairings/Parts/baseModel.mu | Bin 83161 -> 0 bytes .../ProceduralFairings/Parts/baseRingModel.mu | Bin 63857 -> 0 bytes .../ProceduralFairings/Parts/base_ring.cfg | 25 ++++- .../Parts/{baseRingTex.dds => base_ring.dds} | Bin .../ProceduralFairings/Parts/base_ring.mu | Bin 0 -> 65825 bytes .../{blackRingTex.dds => base_ring_black.dds} | Bin .../Parts/base_standard.cfg | 27 +++-- .../Parts/{baseTex.dds => base_standard.dds} | Bin .../ProceduralFairings/Parts/base_standard.mu | Bin 0 -> 94721 bytes .../Parts/{fairing1.dds => fairing.dds} | Bin .../ProceduralFairings/Parts/fairing_NRM.dds | Bin 0 -> 1398256 bytes .../ProceduralFairings/Parts/fairing_side.cfg | 11 +- .../ProceduralFairings/Parts/fairing_side.mu | Bin 0 -> 3534 bytes .../Parts/interstage_adapter.cfg | 12 +- .../ProceduralFairings/Parts/sideModel.mu | Bin 3045 -> 0 bytes .../ProceduralFairings/Parts/thrustPlate.mu | Bin 20169 -> 0 bytes .../ProceduralFairings/Parts/thrust_plate.cfg | 4 +- .../{thrustPlate1.dds => thrust_plate.dds} | Bin .../ProceduralFairings/Parts/thrust_plate.mu | Bin 0 -> 22966 bytes ...mp_Normal_NRM.dds => thrust_plate_NRM.dds} | Bin .../Plugins/ProceduralFairings.dll | Bin 86016 -> 86016 bytes .../fuselage1.dds => Textures/fuselage.dds} | Bin .../Textures/fuselage_NRM.dds | Bin 0 -> 349680 bytes Source/ProceduralFairings/FairingBase.cs | 10 +- Source/ProceduralFairings/FairingSide.cs | 27 ++--- .../ProceduralFairings/NodeNumberTweaker.cs | 2 +- 29 files changed, 212 insertions(+), 66 deletions(-) rename GameData/ProceduralFairings/Config/{ProceduralFairings_FilterExtensions.cfg => PF_FilterExtensions_Config.cfg} (62%) rename GameData/ProceduralFairings/Config/{ProceduralFairings_Settings.cfg => PF_Settings_Config.cfg} (71%) create mode 100644 GameData/ProceduralFairings/Config/PF_Squad_Config.cfg delete mode 100644 GameData/ProceduralFairings/Parts/baseModel.mu delete mode 100644 GameData/ProceduralFairings/Parts/baseRingModel.mu rename GameData/ProceduralFairings/Parts/{baseRingTex.dds => base_ring.dds} (100%) create mode 100644 GameData/ProceduralFairings/Parts/base_ring.mu rename GameData/ProceduralFairings/Parts/{blackRingTex.dds => base_ring_black.dds} (100%) rename GameData/ProceduralFairings/Parts/{baseTex.dds => base_standard.dds} (100%) create mode 100644 GameData/ProceduralFairings/Parts/base_standard.mu rename GameData/ProceduralFairings/Parts/{fairing1.dds => fairing.dds} (100%) create mode 100644 GameData/ProceduralFairings/Parts/fairing_NRM.dds create mode 100644 GameData/ProceduralFairings/Parts/fairing_side.mu delete mode 100644 GameData/ProceduralFairings/Parts/sideModel.mu delete mode 100644 GameData/ProceduralFairings/Parts/thrustPlate.mu rename GameData/ProceduralFairings/Parts/{thrustPlate1.dds => thrust_plate.dds} (100%) create mode 100644 GameData/ProceduralFairings/Parts/thrust_plate.mu rename GameData/ProceduralFairings/Parts/{thrustPlate1bump_Normal_NRM.dds => thrust_plate_NRM.dds} (100%) rename GameData/ProceduralFairings/{Parts/fuselage1.dds => Textures/fuselage.dds} (100%) create mode 100644 GameData/ProceduralFairings/Textures/fuselage_NRM.dds diff --git a/GameData/ProceduralFairings/Config/ProceduralFairings_FilterExtensions.cfg b/GameData/ProceduralFairings/Config/PF_FilterExtensions_Config.cfg similarity index 62% rename from GameData/ProceduralFairings/Config/ProceduralFairings_FilterExtensions.cfg rename to GameData/ProceduralFairings/Config/PF_FilterExtensions_Config.cfg index 39e42cb..9dedbc2 100644 --- a/GameData/ProceduralFairings/Config/ProceduralFairings_FilterExtensions.cfg +++ b/GameData/ProceduralFairings/Config/PF_FilterExtensions_Config.cfg @@ -3,12 +3,12 @@ // compatibility. // ================================================== -@PART[*]:HAS[@MODULE[ProceduralFairing*]]:NEEDS[FilterExtension] +@PART[*]:HAS[@MODULE[ProceduralFairing*]]:FOR[ProceduralFairings]:NEEDS[FilterExtension] { @bulkheadProfiles ^= :$:,proc: } -@PART[KzThrustPlate]:NEEDS[FilterExtension] +@PART[KzThrustPlate]:FOR[ProceduralFairings]:NEEDS[FilterExtension] { @bulkheadProfiles ^= :$:,proc: } diff --git a/GameData/ProceduralFairings/Config/ProceduralFairings_Settings.cfg b/GameData/ProceduralFairings/Config/PF_Settings_Config.cfg similarity index 71% rename from GameData/ProceduralFairings/Config/ProceduralFairings_Settings.cfg rename to GameData/ProceduralFairings/Config/PF_Settings_Config.cfg index 83473c6..995bf53 100644 --- a/GameData/ProceduralFairings/Config/ProceduralFairings_Settings.cfg +++ b/GameData/ProceduralFairings/Config/PF_Settings_Config.cfg @@ -4,34 +4,34 @@ PROCFAIRINGS_MINDIAMETER { - start = 1.00 - miniaturization = 0.400 + start = 1.0 + miniaturization = 0.4 sandbox = 0.1 } PROCFAIRINGS_MAXDIAMETER { start = 1.50 - advAerodynamics = 4 - heavyAerodynamics = 12 - experimentalAerodynamics = 30 - sandbox = 50 + advAerodynamics = 4.0 + heavyAerodynamics = 12.0 + experimentalAerodynamics = 30.0 + sandbox = 50.0 } PROCROCKET_MINDIAMETER { - start = 1.00 - miniaturization = 0.400 + start = 1.0 + miniaturization = 0.4 sandbox = 0.1 } PROCROCKET_MAXDIAMETER { - start = 1.50 - advConstruction = 4 - metaMaterials = 12 - aerospaceTech = 30 - sandbox = 50 + start = 1.5 + advConstruction = 4.0 + metaMaterials = 12.0 + aerospaceTech = 30.0 + sandbox = 50.0 } // ================================================== @@ -47,7 +47,7 @@ PART MODEL { - model = ProceduralFairings/Parts/baseModel + model = ProceduralFairings/Parts/base_standard } TechRequired = miniaturization @@ -56,7 +56,7 @@ PART category = none title = Procedural Fairings Upgrade manufacturer = Keramzit Engineering - description = Allows fairings and plates to be made as small as 0.4m. + description = Allows fairings and plates to be made as small as 0.4 meters. } PART @@ -67,7 +67,7 @@ PART MODEL { - model = ProceduralFairings/Parts/baseModel + model = ProceduralFairings/Parts/base_standard } TechRequired = advAerodynamics @@ -76,7 +76,7 @@ PART category = none title = Procedural Fairings Upgrade manufacturer = Keramzit Engineering - description = Allows fairing bases up to 4m size. + description = Allows fairing bases up to 4 meters size. } PART @@ -89,7 +89,7 @@ PART MODEL { - model = ProceduralFairings/Parts/baseModel + model = ProceduralFairings/Parts/base_standard } TechRequired = heavyAerodynamics @@ -98,7 +98,7 @@ PART category = none title = Procedural Fairings Upgrade manufacturer = Keramzit Engineering - description = Allows fairing bases up to 12m size. + description = Allows fairing bases up to 12 meters size. } PART @@ -109,7 +109,7 @@ PART MODEL { - model = ProceduralFairings/Parts/baseModel + model = ProceduralFairings/Parts/base_standard } TechRequired = experimentalAerodynamics @@ -118,7 +118,7 @@ PART category = none title = Procedural Fairings Upgrade manufacturer = Keramzit Engineering - description = Allows fairing bases up to 30m size. + description = Allows fairing bases up to 30 meters size. } PART @@ -129,7 +129,7 @@ PART MODEL { - model = ProceduralFairings/Parts/thrustPlate + model = ProceduralFairings/Parts/thrust_plate } TechRequired = metaMaterials @@ -138,7 +138,7 @@ PART category = none title = Procedural Fairings Upgrade manufacturer = Keramzit Engineering - description = Allows thrust plates up to 12m size. + description = Allows thrust plates up to 12 meters size. } PART @@ -149,7 +149,7 @@ PART MODEL { - model = ProceduralFairings/Parts/thrustPlate + model = ProceduralFairings/Parts/thrust_plate } TechRequired = aerospaceTech @@ -158,5 +158,5 @@ PART category = none title = Procedural Fairings Upgrade manufacturer = Keramzit Engineering - description = Allows thrust plates up to 30m size. + description = Allows thrust plates up to 30 meters size. } diff --git a/GameData/ProceduralFairings/Config/PF_Squad_Config.cfg b/GameData/ProceduralFairings/Config/PF_Squad_Config.cfg new file mode 100644 index 0000000..def16b1 --- /dev/null +++ b/GameData/ProceduralFairings/Config/PF_Squad_Config.cfg @@ -0,0 +1,106 @@ +// ================================================== +// Replace the stock procedural fairings with ours. +// ================================================== + +@PART[fairingSize1]:FOR[ProceduralFairings] +{ + @MODEL,0 + { + %scale = 0.8275, 0.8275, 0.8275 + %position = 0.0, -0.115, 0.0 + } + + !MODEL,1{} + + @node_stack_top = 0.0, 0.0725, 0.0, 0.0, 1.0, 0.0, 0 + @node_stack_bottom = 0.0, -0.265, 0.0, 0.0, -1.0, 0.0, 1 + + %node_stack_connect01 = 0.5, 0.1, 0.0, 0.0, 1.0, 0.0, 0 + %node_stack_connect02 = 0.5, 0.1, 0.0, 0.0, 1.0, 0.0, 0 + %node_stack_connect03 = 0.5, 0.1, 0.0, 0.0, 1.0, 0.0, 0 + %node_stack_connect04 = 0.5, 0.1, 0.0, 0.0, 1.0, 0.0, 0 + %node_stack_connect05 = 0.5, 0.1, 0.0, 0.0, 1.0, 0.0, 0 + %node_stack_connect06 = 0.5, 0.1, 0.0, 0.0, 1.0, 0.0, 0 + %node_stack_connect07 = 0.5, 0.1, 0.0, 0.0, 1.0, 0.0, 0 + %node_stack_connect08 = 0.5, 0.1, 0.0, 0.0, 1.0, 0.0, 0 + + @title = AE-FF-PF Airstream Protective Shell + + !MODULE[ModuleProceduralFairing],*{} + + !MODULE[ModuleCargoBay],*{} + + !MODULE[ModuleStructuralNode*],*{} + + MODULE + { + name = ProceduralFairingBase + baseSize = 1.15 + sideThickness = 0.05 + verticalStep = 0.1 + } + + MODULE + { + name = KzNodeNumberTweaker + nodePrefix = connect + maxNumber = 8 + numNodes = 2 + radius = 0.625 + shouldResizeNodes = False + } + + MODULE + { + name = KzFairingBaseResizer + size = 1.25 + costPerTonne = 1000 + specificMass = 0.007, 0.026, 0.010, 0 + specificBreakingForce = 1280 + specificBreakingTorque = 1280 + dragAreaScale = 1.5 + } + + MODULE + { + name = KzFairingBaseShielding + } + + MODULE + { + name = ModuleDecouple + isOmniDecoupler = False + ejectionForce = 250 + ejectionForcePercent = 100 + menuName = Decoupler Staging + stagingEnabled = False + stagingEnableText = Decoupler: Not Staged + stagingDisableText = Decoupler: Staged + } + + MODULE + { + name = ModuleToggleCrossfeed + crossfeedStatus = False + toggleEditor = True + toggleFlight = True + enableText = Enable Crossfeed + disableText = Disable Crossfeed + } +} + +// ================================================== +// Hide the rest of the bases. +// ================================================== + +@PART[fairingSize2]:FOR[ProceduralFairings] +{ + @TechRequired = none + @category = none +} + +@PART[fairingSize3]:FOR[ProceduralFairings] +{ + @TechRequired = none + @category = none +} diff --git a/GameData/ProceduralFairings/Parts/baseModel.mu b/GameData/ProceduralFairings/Parts/baseModel.mu deleted file mode 100644 index cfb5618e060101d9735e59ec1bcaa799eee1ffff..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 83161 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 RGkDf9IUTZ<6coWP7mYt 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 diff --git a/GameData/ProceduralFairings/Parts/baseRingModel.mu b/GameData/ProceduralFairings/Parts/baseRingModel.mu deleted file mode 100644 index 7e7e63dc81f0f6e11466af738f6d10a6da6e4f5b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 63857 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 F{{fayb{zl! diff --git a/GameData/ProceduralFairings/Parts/base_ring.cfg b/GameData/ProceduralFairings/Parts/base_ring.cfg index bbc3b87..2d79b41 100644 --- a/GameData/ProceduralFairings/Parts/base_ring.cfg +++ b/GameData/ProceduralFairings/Parts/base_ring.cfg @@ -6,7 +6,7 @@ PART MODEL { - model = ProceduralFairings/Parts/baseRingModel + model = ProceduralFairings/Parts/base_ring scale = 1.0, 1.0, 1.0 } @@ -27,14 +27,18 @@ PART attachRules = 1,0,1,1,0 + fx_gasBurst_white = 0.0, 0.3, 0.0, 0.0, 1.0, 0.0, decouple + + sound_decoupler_fire = decouple + TechRequired = aerodynamicSystems cost = 100 entryCost = 4600 category = Payload subcategory = 0 - title = Fairing Base Ring + title = Procedural Fairing Base Ring manufacturer = Keramzit Engineering - description = Structural base for mounting side fairings and your payload. Decoupler sold separately. + description = Structural base for mounting side fairings and your payload. Ring style. mass = 0 dragModelType = default @@ -80,7 +84,7 @@ PART name = KzFairingBaseResizer size = 1.25 costPerTonne = 1000 - specificMass = 0.0064, 0.0130, 0.0098, 0 + specificMass = 0.006, 0.013, 0.010, 0 specificBreakingForce = 1280 specificBreakingTorque = 1280 dragAreaScale = 1.5 @@ -91,6 +95,19 @@ PART name = KzFairingBaseShielding } + MODULE + { + name = ModuleDecouple + explosiveNodeID = top + isOmniDecoupler = False + ejectionForce = 250 + ejectionForcePercent = 100 + menuName = Decoupler Staging + stagingEnabled = False + stagingEnableText = Decoupler: Not Staged + stagingDisableText = Decoupler: Staged + } + MODULE { name = ModuleToggleCrossfeed diff --git a/GameData/ProceduralFairings/Parts/baseRingTex.dds b/GameData/ProceduralFairings/Parts/base_ring.dds similarity index 100% rename from GameData/ProceduralFairings/Parts/baseRingTex.dds rename to GameData/ProceduralFairings/Parts/base_ring.dds diff --git a/GameData/ProceduralFairings/Parts/base_ring.mu b/GameData/ProceduralFairings/Parts/base_ring.mu new file mode 100644 index 0000000000000000000000000000000000000000..f69b3badc8d1f494b4387f64052ed45ecb41e326 GIT binary patch literal 65825 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%0ANebu=%9P-Gv&1(-S40Otd;IuX%?3cApAAq*%qIA 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

@CJ}fK9=f55K$C0=ZaC^WCfBiz#A8&>H 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ⅆQzGt%lB7; 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