From 04b7880abf3ae91c95a41a16ae9a8bc4cebcbaae Mon Sep 17 00:00:00 2001 From: PhineasFreak Date: Sun, 18 Feb 2018 01:20:01 +0200 Subject: [PATCH] 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 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 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 -