-
Notifications
You must be signed in to change notification settings - Fork 114
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Removed the ability to delete various parameters, like the ScaledSpace Material * Moved the Utility / Exporting code from Kittopia to Kopernicus * Added KittopiaActions to export the planet config / textures
- release-223
- release-222
- release-221
- release-220
- release-219
- release-218
- release-217
- release-216
- release-215
- release-214
- release-213
- release-212
- release-211
- release-210
- release-209
- release-209_RC1
- release-208
- release-207
- release-206
- release-205
- release-204
- release-203
- release-202
- release-201
- release-200
- release-199
- release-198
- release-197
- release-196
- release-196_RC1
- release-195
- release-194
- release-193
- release-192
- release-191
- release-190
- release-190_RC6
- release-190_RC5
- release-190_RC4
- release-190_RC3
- release-190_RC2
- release-190_RC1
- release-189
- release-188
- release-187
- release-186
- release-185
- release-184
- release-183
- release-182
- release-181
- release-180
- release-179
- release-178
- release-177
- release-176
- release-176_RC2
- release-176_RC1
- release-175
- release-174
- release-173
- release-172
- release-171
- release-170
- release-170_RC2
- release-170_RC1
- release-169
- release-168
- release-167
- release-166
- release-166_RC2
- release-166_RC1
- release-165
- release-164
- release-163
- release-162
- release-161
- release-160
- release-159
- release-158
- release-157
- release-156
- release-155
- release-154
- release-153
- release-152
- release-151
- release-150
- release-149
- release-148
- release-147
- release-146
- release-145
- release-144
- release-143
- release-142
- release-141
- release-140
- release-139
- release-138
- release-137
- release-136
- release-135
- release-134
- release-133
- release-132
- release-131
- release-130
- release-129
- release-128
- release-127
- release-126
- release-125
- release-124
- release-123
- release-122
- release-121
- release-120
- release-119
- release-118
- release-117
- release-116
- release-115
- release-114
- release-113
- release-112
- release-111
- release-110
- release-109
- release-108
- release-107
- release-106
- release-105
- release-104
- release-103
- release-102
- release-101
- release-100
- release-99
- release-98
- release-97
- release-96
- release-95
- release-94
- release-93
- release-92
- release-91
- release-90
- release-89
- release-88
- release-87
- release-86
- release-85
- release-84
- release-83
- release-82
- release-81
- release-80
- release-79
- release-78
- release-77
- release-76
- release-75
- release-74
- release-73
- release-72
- release-71
- release-70
- release-69
- release-68
- release-67
- release-66
- release-65
- release-64
- release-63
- release-62
- release-61
- release-60
- release-59
- release-58
- release-57
- release-56
- release-55
- release-54
- release-53
- release-52
- release-51
- release-50
- release-49
- release-48
- release-47
- release-46
- release-45
- release-44
- release-43
- release-42
- release-41
- release-40
- release-39
- release-38
- release-37
- release-36
- release-35
- release-34
- release-33
- release-32
- release-31
- release-30
- release-29
- release-28
- release-27
- release-26
- release-25
- release-1.9.1-24
- release-1.9.1-23
- release-1.9.1-22
- release-1.9.1-21
- release-1.9.1-20
- release-1.9.1-19
- release-1.9.1-18
- release-1.9.1-17
- release-1.9.1-16
- release-1.9.1-15
- release-1.9.1-14
- release-1.9.1-13
- release-1.9.1-12
- release-1.9.1-11
- release-1.9.1-10
- release-1.9.1-9
- release-1.9.1-8
- release-1.9.1-7
- release-1.9.1-6r1
- release-1.9.1-5
- release-1.9.1-4
- release-1.9.1-3
- release-1.9.1-2
- release-1.9.1-1
- release-1.8.1-24
- release-1.8.1-23
- release-1.8.1-1
- release-1.7.3-2
- release-1.7.3-1
- release-1.7.1-5
- release-1.7.1-4
- release-1.7.1-3
- release-1.7.1-2
- release-1.7.1-1
- release-1.7.0-1
- release-1.6.1-2
- release-1.6.1-1
- release-1.6.0-1
- release-1.5.1-1
- release-1.5.0-1
- release-1.4.5-4
- release-1.4.5-3
- release-1.4.5-2
- release-1.4.5-1
- 1.9.1-1
Showing
12 changed files
with
1,193 additions
and
40 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,292 @@ | ||
/** | ||
* Kopernicus Planetary System Modifier | ||
* ------------------------------------------------------------- | ||
* This library is free software; you can redistribute it and/or | ||
* modify it under the terms of the GNU Lesser General Public | ||
* License as published by the Free Software Foundation; either | ||
* version 3 of the License, or (at your option) any later version. | ||
* | ||
* This library is distributed in the hope that it will be useful, | ||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
* Lesser General Public License for more details. | ||
* | ||
* You should have received a copy of the GNU Lesser General Public | ||
* License along with this library; if not, write to the Free Software | ||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, | ||
* MA 02110-1301 USA | ||
* | ||
* This library is intended to be used as a plugin for Kerbal Space Program | ||
* which is copyright 2011-2017 Squad. Your usage of Kerbal Space Program | ||
* itself is governed by the terms of its EULA, not the license above. | ||
* | ||
* https://kerbalspaceprogram.com | ||
*/ | ||
|
||
using System; | ||
using System.Collections; | ||
using System.Collections.Generic; | ||
using System.Reflection; | ||
using Kopernicus.Configuration; | ||
using KSP.Localization; | ||
using Object = System.Object; | ||
|
||
namespace Kopernicus | ||
{ | ||
namespace UI | ||
{ | ||
/// <summary> | ||
/// Converts a celestial body into a Kopernicus config | ||
/// </summary> | ||
public static class PlanetConfigExporter | ||
{ | ||
public static ConfigNode CreateConfig(CelestialBody celestialBody) | ||
{ | ||
return CreateConfig(new Body(celestialBody)); | ||
} | ||
|
||
public static ConfigNode CreateConfig(Body body) | ||
{ | ||
// Create the ConfigNode | ||
ConfigNode bodyNode = new ConfigNode("Body"); | ||
|
||
// Export the planet to the config | ||
WriteToConfig(body, ref bodyNode); | ||
|
||
return bodyNode; | ||
} | ||
|
||
private static void WriteToConfig(Object value, ref ConfigNode node) | ||
{ | ||
// If the value can export to config node directly | ||
if (value is IConfigNodeWritable) | ||
{ | ||
ConfigNode values = ((IConfigNodeWritable) value).ValueToNode(); | ||
if (values != null) | ||
{ | ||
node.AddData(values); | ||
} | ||
|
||
return; | ||
} | ||
|
||
// Get all ParserTargets from the object | ||
Dictionary<ParserTarget, MemberInfo> parserTargets = Tools.GetParserTargets(value.GetType()); | ||
|
||
// Export all found targets | ||
foreach (KeyValuePair<ParserTarget, MemberInfo> keyValuePair in parserTargets) | ||
{ | ||
ParserTarget parserTarget = keyValuePair.Key; | ||
MemberInfo memberInfo = keyValuePair.Value; | ||
|
||
// Is this a collection or a single value? | ||
if (Tools.IsCollection(parserTarget)) | ||
{ | ||
ProcessCollection(parserTarget, memberInfo, value, ref node); | ||
} | ||
else | ||
{ | ||
ProcessSingleValue(parserTarget, memberInfo, value, ref node); | ||
} | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// Adds a single value ParserTarget to the ConfigNode tree | ||
/// </summary> | ||
private static void ProcessSingleValue(ParserTarget parserTarget, MemberInfo memberInfo, Object reference, | ||
ref ConfigNode node) | ||
{ | ||
// Get the value of the MemberInfo | ||
Object value = Tools.GetValue(memberInfo, reference); | ||
if (value == null) | ||
{ | ||
return; | ||
} | ||
|
||
// Is this a value or a node? | ||
ConfigType configType = Tools.GetConfigType(value.GetType()); | ||
if (configType == ConfigType.Value) | ||
{ | ||
SetValue(parserTarget, memberInfo, reference, ref node); | ||
} | ||
else | ||
{ | ||
// Create the new node | ||
String name = parserTarget.FieldName; | ||
if (parserTarget.NameSignificance == NameSignificance.Type) | ||
{ | ||
name += ":" + value.GetType().Name; | ||
} | ||
|
||
ConfigNode valueNode = node.AddNode(name); | ||
WriteToConfig(value, ref valueNode); | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// Adds a multi-value ParserTarget to the ConfigNode tree | ||
/// </summary> | ||
private static void ProcessCollection(ParserTarget parserTarget, MemberInfo memberInfo, Object reference, | ||
ref ConfigNode node) | ||
{ | ||
// Get the type of the collection | ||
Type memberType = Tools.MemberType(memberInfo); | ||
|
||
// Is the collection a dictionary? | ||
if (typeof(IDictionary).IsAssignableFrom(memberType)) | ||
{ | ||
IDictionary dictionary = Tools.GetValue(memberInfo, reference) as IDictionary; | ||
|
||
// Is the dictionary null? | ||
if (dictionary == null) | ||
{ | ||
return; | ||
} | ||
|
||
// Create the new ConfigNode | ||
ConfigNode targetNode = null; | ||
|
||
// Iterate over the elements of the dictionary | ||
foreach (DictionaryEntry value in dictionary) | ||
{ | ||
// Null-Check | ||
if (value.Key == null || value.Value == null) | ||
{ | ||
continue; | ||
} | ||
|
||
// Create the node if neccessary | ||
if (targetNode == null) | ||
{ | ||
targetNode = node; | ||
if (parserTarget.FieldName != "self") | ||
{ | ||
targetNode = node.AddNode(parserTarget.FieldName); | ||
} | ||
} | ||
|
||
// The first generic type has to be ConfigType.Value, figure out the type of the second one | ||
ConfigType type = Tools.GetConfigType(value.Value.GetType()); | ||
|
||
// If it is a node, add it to the node | ||
if (type == ConfigType.Node) | ||
{ | ||
ConfigNode valueNode = targetNode.AddNode(Tools.FormatParsable(value.Key)); | ||
WriteToConfig(value.Value, ref valueNode); | ||
} | ||
else | ||
{ | ||
targetNode.AddValue(Tools.FormatParsable(value.Key), Tools.FormatParsable(value.Value)); | ||
} | ||
} | ||
} | ||
else if (typeof(IList).IsAssignableFrom(memberType)) | ||
{ | ||
IList list = Tools.GetValue(memberInfo, reference) as IList; | ||
|
||
// Is the dictionary null? | ||
if (list == null) | ||
{ | ||
return; | ||
} | ||
|
||
// Create the new ConfigNode | ||
ConfigNode targetNode = null; | ||
|
||
// Iterate over the elements of the list | ||
foreach (Object value in list) | ||
{ | ||
// Null-Check | ||
if (value == null) | ||
{ | ||
continue; | ||
} | ||
|
||
// Create the node if neccessary | ||
if (targetNode == null) | ||
{ | ||
targetNode = node; | ||
if (parserTarget.FieldName != "self") | ||
{ | ||
targetNode = node.AddNode(parserTarget.FieldName); | ||
} | ||
} | ||
|
||
// Figure out the config type of type | ||
ConfigType type = Tools.GetConfigType(value.GetType()); | ||
|
||
// If it is a node, add it to the node | ||
if (type == ConfigType.Node) | ||
{ | ||
String name = "Value"; | ||
if (parserTarget.NameSignificance == NameSignificance.Key) | ||
{ | ||
name = parserTarget.Key; | ||
} | ||
|
||
if (parserTarget.NameSignificance == NameSignificance.Type) | ||
{ | ||
name = value.GetType().Name; | ||
} | ||
|
||
ConfigNode valueNode = targetNode.AddNode(name); | ||
WriteToConfig(value, ref valueNode); | ||
} | ||
else | ||
{ | ||
String name = "value"; | ||
if (parserTarget.NameSignificance == NameSignificance.Key) | ||
{ | ||
name = parserTarget.Key; | ||
} | ||
|
||
if (parserTarget.NameSignificance == NameSignificance.Type) | ||
{ | ||
name = value.GetType().Name; | ||
} | ||
|
||
targetNode.AddValue(name, Tools.FormatParsable(value)); | ||
} | ||
} | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// Formats and adds a ParserTarget to a ConfigNode | ||
/// </summary> | ||
private static void SetValue(ParserTarget parserTarget, MemberInfo memberInfo, Object reference, | ||
ref ConfigNode node) | ||
{ | ||
// Get the value behind the MemberInfo | ||
Object value = Tools.GetValue(memberInfo, reference); | ||
if (value == null) | ||
{ | ||
return; | ||
} | ||
|
||
// Format the value | ||
String formattedValue = Tools.FormatParsable(value); | ||
if (formattedValue == null) | ||
{ | ||
return; | ||
} | ||
|
||
formattedValue = Localizer.Format(formattedValue); | ||
|
||
// Get a description | ||
String description = Tools.GetDescription(memberInfo); | ||
|
||
// Add it to the config | ||
if (String.IsNullOrEmpty(description)) | ||
{ | ||
node.AddValue(parserTarget.FieldName, formattedValue); | ||
} | ||
else | ||
{ | ||
node.AddValue(parserTarget.FieldName, formattedValue, description); | ||
} | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,256 @@ | ||
using System; | ||
using System.Collections; | ||
using System.IO; | ||
using System.Linq; | ||
using System.Reflection; | ||
using System.Threading; | ||
using UnityEngine; | ||
using UnityEngine.UI; | ||
|
||
namespace Kopernicus | ||
{ | ||
namespace UI | ||
{ | ||
/// <summary> | ||
/// Exports the celestial body maps of a body | ||
/// </summary> | ||
public class PlanetTextureExporter | ||
{ | ||
[RequireConfigType(ConfigType.Node)] | ||
public class TextureOptions | ||
{ | ||
[ParserTarget("exportColor")] | ||
public NumericParser<Boolean> ExportColor; | ||
|
||
[ParserTarget("exportHeight")] | ||
public NumericParser<Boolean> ExportHeight; | ||
|
||
[ParserTarget("exportNormal")] | ||
public NumericParser<Boolean> ExportNormal; | ||
|
||
[ParserTarget("transparentMaps")] | ||
public NumericParser<Boolean> TransparentMaps; | ||
|
||
[ParserTarget("saveToDisk")] | ||
public NumericParser<Boolean> SaveToDisk; | ||
|
||
[ParserTarget("applyToScaled")] | ||
public NumericParser<Boolean> ApplyToScaled; | ||
|
||
[ParserTarget("normalStrength")] | ||
public NumericParser<Single> NormalStrength; | ||
|
||
public TextureOptions() | ||
{ | ||
ExportColor = true; | ||
ExportHeight = true; | ||
ExportNormal = true; | ||
TransparentMaps = true; | ||
SaveToDisk = true; | ||
ApplyToScaled = true; | ||
NormalStrength = 7; | ||
} | ||
} | ||
|
||
public static IEnumerator UpdateTextures(CelestialBody celestialBody, TextureOptions options) | ||
{ | ||
// Get time | ||
DateTime now = DateTime.Now; | ||
|
||
// If the user wants to export normals, we need height too | ||
if (options.ExportNormal) | ||
{ | ||
options.ExportHeight = true; | ||
} | ||
|
||
// Prepare the PQS | ||
PQS pqsVersion = celestialBody.pqsController; | ||
|
||
// If the PQS is null, abort | ||
if (pqsVersion == null) | ||
{ | ||
throw new InvalidOperationException(); | ||
} | ||
|
||
// Tell the PQS that we are going to build maps | ||
pqsVersion.isBuildingMaps = true; | ||
pqsVersion.isFakeBuild = true; | ||
|
||
// Get the mod building methods from the PQS | ||
Action<PQS.VertexBuildData> modOnVertexBuildHeight = | ||
(Action<PQS.VertexBuildData>) Delegate.CreateDelegate( | ||
typeof(Action<PQS.VertexBuildData>), | ||
pqsVersion, | ||
typeof(PQS).GetMethod("Mod_OnVertexBuildHeight", | ||
BindingFlags.Instance | BindingFlags.NonPublic)); | ||
Action<PQS.VertexBuildData> modOnVertexBuild = (Action<PQS.VertexBuildData>) Delegate.CreateDelegate( | ||
typeof(Action<PQS.VertexBuildData>), | ||
pqsVersion, | ||
typeof(PQS).GetMethod("Mod_OnVertexBuild", BindingFlags.Instance | BindingFlags.NonPublic)); | ||
|
||
// Disable the PQS | ||
pqsVersion.gameObject.SetActive(false); | ||
|
||
// Get all mods the PQS is connected to | ||
PQSMod[] mods = pqsVersion.GetComponentsInChildren<PQSMod>() | ||
.Where(m => m.sphere == pqsVersion && m.modEnabled).ToArray(); | ||
|
||
// Create the Textures | ||
Texture2D colorMap = new Texture2D(pqsVersion.mapFilesize, pqsVersion.mapFilesize / 2, | ||
TextureFormat.ARGB32, | ||
true); | ||
Texture2D heightMap = new Texture2D(pqsVersion.mapFilesize, pqsVersion.mapFilesize / 2, | ||
TextureFormat.RGB24, | ||
true); | ||
|
||
// Arrays | ||
Color[] colorMapValues = new Color[pqsVersion.mapFilesize * (pqsVersion.mapFilesize / 2)]; | ||
Color[] heightMapValues = new Color[pqsVersion.mapFilesize * (pqsVersion.mapFilesize / 2)]; | ||
|
||
|
||
// Create a VertexBuildData | ||
PQS.VertexBuildData data = new PQS.VertexBuildData(); | ||
|
||
// Display | ||
ScreenMessage message = ScreenMessages.PostScreenMessage("Generating Planet-Maps", Single.MaxValue, ScreenMessageStyle.UPPER_CENTER); | ||
yield return null; | ||
|
||
// Loop through the pixels | ||
for (Int32 y = 0; y < pqsVersion.mapFilesize / 2; y++) | ||
{ | ||
for (Int32 x = 0; x < pqsVersion.mapFilesize; x++) | ||
{ | ||
// Update Message | ||
Double percent = (Double) (y * pqsVersion.mapFilesize + x) / | ||
(pqsVersion.mapFilesize / 2 * pqsVersion.mapFilesize) * 100; | ||
while (CanvasUpdateRegistry.IsRebuildingLayout()) Thread.Sleep(10); | ||
message.textInstance.text.text = "Generating Planet-Maps: " + percent.ToString("0.00") + "%"; | ||
|
||
// Update the VertexBuildData | ||
data.directionFromCenter = | ||
QuaternionD.AngleAxis(360d / pqsVersion.mapFilesize * x, Vector3d.up) * | ||
QuaternionD.AngleAxis(90d - 180d / (pqsVersion.mapFilesize / 2f) * y, Vector3d.right) | ||
* Vector3d.forward; | ||
data.vertHeight = pqsVersion.radius; | ||
|
||
// Build from the Mods | ||
Double height = Double.MinValue; | ||
if (options.ExportHeight) | ||
{ | ||
modOnVertexBuildHeight(data); | ||
|
||
// Adjust the height | ||
height = (data.vertHeight - pqsVersion.radius) * (1d / pqsVersion.mapMaxHeight); | ||
if (height < 0) | ||
{ | ||
height = 0; | ||
} | ||
else if (height > 1) | ||
{ | ||
height = 1; | ||
} | ||
|
||
// Set the Pixels | ||
heightMapValues[y * pqsVersion.mapFilesize + x] = | ||
new Color((Single) height, (Single) height, (Single) height); | ||
} | ||
|
||
if (options.ExportColor) | ||
{ | ||
modOnVertexBuild(data); | ||
|
||
// Adjust the Color | ||
Color color = data.vertColor; | ||
if (!pqsVersion.mapOcean) | ||
{ | ||
color.a = 1f; | ||
} | ||
else if (height > pqsVersion.mapOceanHeight) | ||
{ | ||
color.a = options.TransparentMaps ? 0f : 1f; | ||
} | ||
else | ||
{ | ||
color = pqsVersion.mapOceanColor.A(1f); | ||
} | ||
|
||
// Set the Pixels | ||
colorMapValues[y * pqsVersion.mapFilesize + x] = color; | ||
} | ||
} | ||
|
||
yield return null; | ||
} | ||
|
||
// Serialize the maps to disk | ||
String path = KSPUtil.ApplicationRootPath + "/GameData/KittopiaTech/PluginData/" + | ||
celestialBody.transform.name + "/"; | ||
Directory.CreateDirectory(path); | ||
|
||
// Colormap | ||
if (options.ExportColor) | ||
{ | ||
// Save it | ||
colorMap.SetPixels(colorMapValues); | ||
yield return null; | ||
|
||
if (options.SaveToDisk) | ||
{ | ||
File.WriteAllBytes(path + celestialBody.transform.name + "_Color.png", colorMap.EncodeToPNG()); | ||
yield return null; | ||
} | ||
|
||
// Apply it | ||
if (options.ApplyToScaled) | ||
{ | ||
colorMap.Apply(); | ||
celestialBody.scaledBody.GetComponent<MeshRenderer>().material.SetTexture("_MainTex", colorMap); | ||
} | ||
} | ||
|
||
if (options.ExportHeight) | ||
{ | ||
heightMap.SetPixels(heightMapValues); | ||
yield return null; | ||
|
||
if (options.SaveToDisk) | ||
{ | ||
File.WriteAllBytes(path + celestialBody.transform.name + "_Height.png", | ||
heightMap.EncodeToPNG()); | ||
yield return null; | ||
} | ||
|
||
if (options.ExportNormal) | ||
{ | ||
// Bump to Normal Map | ||
Texture2D normalMap = Utility.BumpToNormalMap(heightMap, options.NormalStrength); | ||
yield return null; | ||
|
||
if (options.SaveToDisk) | ||
{ | ||
File.WriteAllBytes(path + celestialBody.transform.name + "_Normal.png", | ||
normalMap.EncodeToPNG()); | ||
yield return null; | ||
} | ||
|
||
// Apply it | ||
if (options.ApplyToScaled) | ||
{ | ||
normalMap.Apply(); | ||
celestialBody.scaledBody.GetComponent<MeshRenderer>().material | ||
.SetTexture("_BumpMap", normalMap); | ||
} | ||
} | ||
} | ||
|
||
// Close the Renderer | ||
pqsVersion.isBuildingMaps = false; | ||
pqsVersion.isFakeBuild = false; | ||
|
||
// Declare that we're done | ||
ScreenMessages.RemoveMessage(message); | ||
ScreenMessages.PostScreenMessage("Operation completed in: " + (DateTime.Now - now).TotalMilliseconds + " ms", 2f, ScreenMessageStyle.UPPER_CENTER); | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,381 @@ | ||
using System; | ||
using System.Collections; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using System.Reflection; | ||
using Kopernicus; | ||
using Kopernicus.UI; | ||
using UnityEngine; | ||
using Object = System.Object; | ||
|
||
namespace Kopernicus | ||
{ | ||
namespace UI | ||
{ | ||
public static class Tools | ||
{ | ||
/// <summary> | ||
/// Returns all ParserTargets in a specific type | ||
/// </summary> | ||
public static Dictionary<ParserTarget, MemberInfo> GetParserTargets(Type parserType) | ||
{ | ||
// Create the dictionary | ||
Dictionary<ParserTarget, MemberInfo> targets = new Dictionary<ParserTarget, MemberInfo>(); | ||
|
||
// Get all fields and properties from the type | ||
MemberInfo[] members = parserType.GetMembers() | ||
.Where(m => m.MemberType == MemberTypes.Field || m.MemberType == MemberTypes.Property).ToArray(); | ||
|
||
// Iterate over the members to check if they are ParserTargets | ||
foreach (MemberInfo memberInfo in members) | ||
{ | ||
// Get all ParserTargets from the current member | ||
ParserTarget[] parserTargets = GetAttributes<ParserTarget>(memberInfo); | ||
|
||
if (parserTargets == null || !parserTargets.Any()) | ||
{ | ||
continue; | ||
} | ||
|
||
// And add them to the dictionary | ||
targets.Add(parserTargets[parserTargets.Length - 1], memberInfo); | ||
} | ||
|
||
return targets; | ||
} | ||
|
||
/// <summary> | ||
/// Returns all ParserTargets in a specific type | ||
/// </summary> | ||
public static Dictionary<ParserTarget, MemberInfo> GetParserTargets<T>() | ||
{ | ||
return GetParserTargets(typeof(T)); | ||
} | ||
|
||
/// <summary> | ||
/// Returns whether the ParserTarget describes a single value or a range of values | ||
/// </summary> | ||
public static Boolean IsCollection(ParserTarget parserTarget) | ||
{ | ||
return parserTarget is ParserTargetCollection; | ||
} | ||
|
||
/// <summary> | ||
/// Returns the real type of a MemberInfo | ||
/// </summary> | ||
public static Type MemberType(MemberInfo member) | ||
{ | ||
if (member.MemberType == MemberTypes.Field) | ||
{ | ||
return ((FieldInfo) member).FieldType; | ||
} | ||
|
||
if (member.MemberType == MemberTypes.Property) | ||
{ | ||
return ((PropertyInfo) member).PropertyType; | ||
} | ||
|
||
return null; | ||
} | ||
|
||
/// <summary> | ||
/// If a ParserTarget has a description, return it | ||
/// </summary> | ||
public static String GetDescription(MemberInfo memberInfo) | ||
{ | ||
KittopiaDescription[] descriptions = GetAttributes<KittopiaDescription>(memberInfo); | ||
if (descriptions == null || !descriptions.Any()) | ||
{ | ||
return null; | ||
} | ||
|
||
return descriptions[0].description; | ||
} | ||
|
||
/// <summary> | ||
/// Gets the config type that is required by the member | ||
/// </summary> | ||
public static ConfigType GetConfigType(Type memberType) | ||
{ | ||
// Add exceptions for String and ConfigNode | ||
if (memberType == typeof(String)) | ||
{ | ||
return ConfigType.Value; | ||
} | ||
|
||
if (memberType == typeof(ConfigNode)) | ||
{ | ||
return ConfigType.Node; | ||
} | ||
|
||
// Get the RequireConfigType Attribute | ||
RequireConfigType[] configTypes = | ||
memberType.GetCustomAttributes(typeof(RequireConfigType), false) as RequireConfigType[]; | ||
|
||
if (configTypes == null || !configTypes.Any()) | ||
{ | ||
Debug.Log(memberType); | ||
throw new InvalidOperationException("Member needs to have a parsable type."); | ||
} | ||
|
||
return configTypes[0].Type; | ||
} | ||
|
||
/// <summary> | ||
/// Returns the value that is assigned to a MemberInfo | ||
/// </summary> | ||
public static Object GetValue(MemberInfo member, Object reference) | ||
{ | ||
if (member.MemberType == MemberTypes.Field) | ||
{ | ||
return ((FieldInfo) member).GetValue(reference); | ||
} | ||
|
||
if (member.MemberType == MemberTypes.Property) | ||
{ | ||
PropertyInfo info = (PropertyInfo) member; | ||
if (info.CanRead) | ||
{ | ||
return info.GetValue(reference, null); | ||
} | ||
} | ||
|
||
return null; | ||
} | ||
|
||
/// <summary> | ||
/// Sets the value that is assigned to a MemberInfo | ||
/// </summary> | ||
public static void SetValue(MemberInfo member, Object reference, Object value) | ||
{ | ||
if (member.MemberType == MemberTypes.Field) | ||
{ | ||
((FieldInfo) member).SetValue(reference, value); | ||
} | ||
|
||
if (member.MemberType == MemberTypes.Property) | ||
{ | ||
PropertyInfo info = (PropertyInfo) member; | ||
if (info.CanWrite) | ||
{ | ||
info.SetValue(reference, value, null); | ||
} | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// Return whether the member has an ignore marker attached | ||
/// </summary> | ||
public static Boolean HasAttribute<T>(MemberInfo memberInfo) where T : Attribute | ||
{ | ||
Object[] values = | ||
memberInfo.GetCustomAttributes(typeof(T), false); | ||
return values.Any(); | ||
} | ||
|
||
/// <summary> | ||
/// Return whether the member has an ignore marker attached | ||
/// </summary> | ||
public static T[] GetAttributes<T>(MemberInfo memberInfo) where T : Attribute | ||
{ | ||
return memberInfo.GetCustomAttributes(typeof(T), false) as T[]; | ||
} | ||
|
||
/// <summary> | ||
/// Converts a value to a parsable string representation | ||
/// </summary> | ||
public static String FormatParsable(Object value) | ||
{ | ||
if (value == null) | ||
{ | ||
return null; | ||
} | ||
|
||
if (value is String) | ||
{ | ||
return (String) value; | ||
} | ||
|
||
if (value is IParsable) | ||
{ | ||
return ((IParsable) value).ValueToString(); | ||
} | ||
|
||
return value.ToString(); | ||
} | ||
|
||
/// <summary> | ||
/// Sets the value of a parsertarget using a string | ||
/// </summary> | ||
public static void SetValueFromString(MemberInfo member, Object reference, String value) | ||
{ | ||
// Get the current value | ||
Object current = GetValue(member, reference); | ||
Object backup = current; | ||
|
||
try | ||
{ | ||
// Get the type of the member | ||
Type memberType = MemberType(member); | ||
|
||
// Is the member a string member? | ||
if (memberType == typeof(String)) | ||
{ | ||
// Simply assign the new value | ||
SetValue(member, reference, value); | ||
return; | ||
} | ||
|
||
// Is the member a parsable type? | ||
if (typeof(IParsable).IsAssignableFrom(memberType)) | ||
{ | ||
// Is the member null? | ||
if (current == null) | ||
{ | ||
SetValue(member, reference, current = Activator.CreateInstance(memberType)); | ||
} | ||
|
||
// Now we can parse the value | ||
IParsable parser = (IParsable) current; | ||
parser.SetFromString(value); | ||
|
||
// Reapply | ||
SetValue(member, reference, parser); | ||
return; | ||
} | ||
} | ||
catch (Exception e) | ||
{ | ||
Debug.LogException(e); | ||
SetValue(member, reference, backup); | ||
return; | ||
} | ||
|
||
// The member wasn't parsable | ||
throw new InvalidOperationException("The member wasn't parsable"); | ||
} | ||
|
||
/// <summary> | ||
/// Parses and applies the user input to a ParserTarget | ||
/// </summary> | ||
public static String ApplyInput(MemberInfo member, String input, Object reference) | ||
{ | ||
SetValueFromString(member, reference, input); | ||
return FormatParsable(GetValue(member, reference)) ?? ""; | ||
} | ||
|
||
/// <summary> | ||
/// Calls the methods declared as KittopiaDestructor | ||
/// </summary> | ||
public static void Destruct(Object value) | ||
{ | ||
// Get all methods of the object | ||
MethodInfo[] methods = value.GetType() | ||
.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); | ||
|
||
for (Int32 i = 0; i < methods.Length; i++) | ||
{ | ||
// Is the method a KittopiaDestructor? | ||
if (HasAttribute<KittopiaDestructor>(methods[i])) | ||
{ | ||
methods[i].Invoke(value, null); | ||
} | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// Calls the methods declared as KittopiaDestructor | ||
/// </summary> | ||
public static Object Construct(Type type, CelestialBody body) | ||
{ | ||
// Get all methods of the object | ||
ConstructorInfo[] methods = | ||
type.GetConstructors(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); | ||
|
||
for (Int32 i = 0; i < methods.Length; i++) | ||
{ | ||
// Is the method a KittopiaDestructor? | ||
if (HasAttribute<KittopiaConstructor>(methods[i])) | ||
{ | ||
KittopiaConstructor attr = GetAttributes<KittopiaConstructor>(methods[i])[0]; | ||
if (attr.parameter == KittopiaConstructor.Parameter.Empty) | ||
{ | ||
return methods[i].Invoke(null); | ||
} | ||
|
||
if (attr.parameter == KittopiaConstructor.Parameter.CelestialBody) | ||
{ | ||
return methods[i].Invoke(new Object[] {body}); | ||
} | ||
} | ||
} | ||
|
||
return null; | ||
} | ||
|
||
/// <summary> | ||
/// Returns all KittopiaActions in a specific type | ||
/// </summary> | ||
public static Dictionary<KittopiaAction, MethodInfo> GetKittopiaActions(Type parserType) | ||
{ | ||
// Create the dictionary | ||
Dictionary<KittopiaAction, MethodInfo> targets = new Dictionary<KittopiaAction, MethodInfo>(); | ||
|
||
// Get all methods from the type | ||
MethodInfo[] members = | ||
parserType.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); | ||
|
||
// Iterate over the members to check if they are KittopiaActions | ||
foreach (MethodInfo memberInfo in members) | ||
{ | ||
// Get all KittopiaActions from the current member | ||
KittopiaAction[] actions = GetAttributes<KittopiaAction>(memberInfo); | ||
|
||
if (actions == null || !actions.Any()) | ||
{ | ||
continue; | ||
} | ||
|
||
// And add them to the dictionary | ||
targets.Add(actions[0], memberInfo); | ||
} | ||
|
||
return targets; | ||
} | ||
|
||
/// <summary> | ||
/// Calls a KittopiaAction function | ||
/// </summary> | ||
public static void InvokeKittopiaAction(MethodInfo method, Object reference, Action callback) | ||
{ | ||
// Is the method an enumerator? | ||
if (typeof(IEnumerator).IsAssignableFrom(method.ReturnType)) | ||
{ | ||
IEnumerator coroutine = (IEnumerator) method.Invoke(reference, null); | ||
HighLogic.fetch.StartCoroutine(CoroutineCallback(coroutine, callback)); | ||
} | ||
else | ||
{ | ||
// Simply invoke the method and call the callback | ||
method.Invoke(reference, null); | ||
callback(); | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// Executes a coroutine and executes code after it finished | ||
/// </summary> | ||
private static IEnumerator CoroutineCallback(IEnumerator coroutine, Action callback) | ||
{ | ||
while(coroutine.MoveNext()) | ||
{ | ||
yield return coroutine.Current; | ||
} | ||
|
||
yield return null; | ||
|
||
callback(); | ||
} | ||
} | ||
} | ||
} |