Пример #1
0
        /// <summary>
        /// A coroutine that updates the scaled space in the background
        /// </summary>
        private IEnumerator UpdateScaledSpace()
        {
            // Path to the cache
            String CacheDirectory = "saves/" + HighLogic.SaveFolder + "/diversity/";

            for (Int32 a = 0; a < scaledSpaceUpdate.Count; a++)
            {
                // Get the body
                CelestialBody body = scaledSpaceUpdate[a];
                current = body.bodyDisplayName;
                index   = a + 1;

                // Mesh
                Directory.CreateDirectory(CacheDirectory + "mesh");
                Utility.UpdateScaledMesh(body.scaledBody, body.pqsController, body, CacheDirectory + "mesh/");

                // Textures
                Directory.CreateDirectory(CacheDirectory + "textures/" + body.bodyName);
                String TextureDirectory = CacheDirectory + "textures/" + body.bodyName + "/";
                if (File.Exists(TextureDirectory + "color.png") && File.Exists(TextureDirectory + "normal.png"))
                {
                    Texture2D colorMap = Utility.LoadTexture(TextureDirectory + "color.png", false, true, true);
                    yield return(null);

                    Texture2D normalMap = Utility.LoadTexture(TextureDirectory + "normal.png", false, true, true);
                    yield return(null);

                    // Apply them to the ScaledVersion
                    body.scaledBody.GetComponent <MeshRenderer>().material.SetTexture("_MainTex", colorMap);
                    body.scaledBody.GetComponent <MeshRenderer>().material.SetTexture("_BumpMap", normalMap);
                    yield return(null);
                }
                else
                {
                    // Get PQS
                    PQS pqs = body.pqsController;
                    pqs.SetupExternalRender();

                    // Get the mods
                    Action <PQS.VertexBuildData> modOnVertexBuildHeight = (Action <PQS.VertexBuildData>)Delegate.CreateDelegate(
                        typeof(Action <PQS.VertexBuildData>),
                        pqs,
                        typeof(PQS).GetMethod("Mod_OnVertexBuildHeight", BindingFlags.Instance | BindingFlags.NonPublic));
                    Action <PQS.VertexBuildData> modOnVertexBuild = (Action <PQS.VertexBuildData>)Delegate.CreateDelegate(
                        typeof(Action <PQS.VertexBuildData>),
                        pqs,
                        typeof(PQS).GetMethod("Mod_OnVertexBuild", BindingFlags.Instance | BindingFlags.NonPublic));
                    PQSMod[] mods = pqs.GetComponentsInChildren <PQSMod>().Where(m => m.sphere == pqs && m.modEnabled).ToArray();

                    // Create the Textures
                    Texture2D colorMap  = new Texture2D(pqs.mapFilesize, pqs.mapFilesize / 2, TextureFormat.ARGB32, true);
                    Texture2D heightMap = new Texture2D(pqs.mapFilesize, pqs.mapFilesize / 2, TextureFormat.RGB24, true);

                    // Arrays
                    Color[] colorMapValues  = new Color[pqs.mapFilesize * (pqs.mapFilesize / 2)];
                    Color[] heightMapValues = new Color[pqs.mapFilesize * (pqs.mapFilesize / 2)];

                    // Wait a some time
                    yield return(null);

                    // Loop through the pixels
                    for (Int32 y = 0; y < (pqs.mapFilesize / 2); y++)
                    {
                        for (Int32 x = 0; x < pqs.mapFilesize; x++)
                        {
                            // Update Message
                            percent = ((Double)((y * pqs.mapFilesize) + x) / ((pqs.mapFilesize / 2) * pqs.mapFilesize)) * 100;

                            // Create a VertexBuildData
                            PQS.VertexBuildData data = new PQS.VertexBuildData
                            {
                                directionFromCenter = (QuaternionD.AngleAxis((360d / pqs.mapFilesize) * x, Vector3d.up) * QuaternionD.AngleAxis(90d - (180d / (pqs.mapFilesize / 2)) * y, Vector3d.right)) * Vector3d.forward,
                                vertHeight          = pqs.radius
                            };

                            // Build from the Mods
                            modOnVertexBuildHeight(data);
                            modOnVertexBuild(data);

                            // Adjust the height
                            Double height = (data.vertHeight - pqs.radius) * (1d / pqs.mapMaxHeight);
                            if (height < 0)
                            {
                                height = 0;
                            }
                            else if (height > 1)
                            {
                                height = 1;
                            }

                            // Adjust the Color
                            Color color = data.vertColor;
                            if (!pqs.mapOcean)
                            {
                                color.a = 1f;
                            }
                            else if (height > pqs.mapOceanHeight)
                            {
                                color.a = 0f;
                            }
                            else
                            {
                                color = pqs.mapOceanColor.A(1f);
                            }

                            // Set the Pixels
                            colorMapValues[(y * pqs.mapFilesize) + x]  = color;
                            heightMapValues[(y * pqs.mapFilesize) + x] = new Color((Single)height, (Single)height, (Single)height);
                        }
                        yield return(null);
                    }

                    // Apply the maps
                    colorMap.SetPixels(colorMapValues);
                    colorMap.Apply();
                    heightMap.SetPixels(heightMapValues);
                    yield return(null);

                    // Close the Renderer
                    pqs.CloseExternalRender();

                    // Bump to Normal Map
                    Texture2D normalMap = Utility.BumpToNormalMap(heightMap, 7);

                    // Serialize them to disk
                    File.WriteAllBytes(TextureDirectory + "color.png", colorMap.EncodeToPNG());
                    File.WriteAllBytes(TextureDirectory + "normal.png", normalMap.EncodeToPNG());
                    yield return(null);

                    // Apply them to the ScaledVersion
                    body.scaledBody.GetComponent <MeshRenderer>().material.SetTexture("_MainTex", colorMap);
                    body.scaledBody.GetComponent <MeshRenderer>().material.SetTexture("_BumpMap", normalMap);
                    yield return(null);
                }

                // OnDemand
                if (Templates.IsKopernicusInstalled)
                {
                    Type      onDemandType = Templates.Types.FirstOrDefault(t => t.Name == "ScaledSpaceOnDemand");
                    Component onDemand     = body.scaledBody.GetComponent(onDemandType);
                    if (onDemand != null)
                    {
                        FieldInfo texture           = onDemandType.GetField("texture");
                        FieldInfo normals           = onDemandType.GetField("normals");
                        String    RelativeDirectory = TextureDirectory.Replace(KSPUtil.ApplicationRootPath, "../");
                        texture.SetValue(onDemand, RelativeDirectory + "color.png");
                        normals.SetValue(onDemand, RelativeDirectory + "normal.png");
                    }
                }
                percent = 0;
                yield return(null);
            }
            guiEnabled = false;
            scaledSpaceUpdate.Clear();

            FlightDriver.SetPause(false, false);
            InputLockManager.RemoveControlLock("planetaryDiversityCache");
        }
Пример #2
0
            /// <summary>
            /// Renders the Window
            /// </summary>
            protected override void Render(Int32 id)
            {
                // Call base
                base.Render(id);

                // Check for PQS
                if (Current.pqsController == null)
                {
                    Button(Localization.LOC_KITTOPIATECH_PQSEDITOR_ADD, () =>
                    {
                        // Create a new PQS
                        GameObject controllerRoot       = new GameObject(Current.name);
                        controllerRoot.transform.parent = Current.transform;
                        PQS pqsVersion = controllerRoot.AddComponent <PQS>();

                        // I am at this time unable to determine some of the magic parameters which cause the PQS to work... (Or just lazy but who cares :P)
                        PSystemBody Laythe = Utility.FindBody(Injector.StockSystemPrefab.rootBody, "Laythe");
                        Utility.CopyObjectFields(Laythe.pqsVersion, pqsVersion);
                        pqsVersion.surfaceMaterial = Laythe.pqsVersion.surfaceMaterial;

                        // Create the fallback material (always the same shader)
                        pqsVersion.fallbackMaterial      = new PQSProjectionFallbackLoader();
                        pqsVersion.fallbackMaterial.name = Guid.NewGuid().ToString();

                        // Create the celestial body transform
                        GameObject mod       = new GameObject("_CelestialBody");
                        mod.transform.parent = controllerRoot.transform;
                        PQSMod_CelestialBodyTransform transform = mod.AddComponent <PQSMod_CelestialBodyTransform>();
                        transform.sphere                        = pqsVersion;
                        transform.forceActivate                 = false;
                        transform.deactivateAltitude            = 115000;
                        transform.forceRebuildOnTargetChange    = false;
                        transform.planetFade                    = new PQSMod_CelestialBodyTransform.AltitudeFade();
                        transform.planetFade.fadeFloatName      = "_PlanetOpacity";
                        transform.planetFade.fadeStart          = 100000.0f;
                        transform.planetFade.fadeEnd            = 110000.0f;
                        transform.planetFade.valueStart         = 0.0f;
                        transform.planetFade.valueEnd           = 1.0f;
                        transform.planetFade.secondaryRenderers = new List <GameObject>();
                        transform.secondaryFades                = new PQSMod_CelestialBodyTransform.AltitudeFade[0];
                        transform.requirements                  = PQS.ModiferRequirements.Default;
                        transform.modEnabled                    = true;
                        transform.order = 10;

                        // Create the material direction
                        mod = new GameObject("_Material_SunLight");
                        mod.transform.parent = controllerRoot.gameObject.transform;
                        PQSMod_MaterialSetDirection lightDirection = mod.AddComponent <PQSMod_MaterialSetDirection>();
                        lightDirection.sphere       = pqsVersion;
                        lightDirection.valueName    = "_sunLightDirection";
                        lightDirection.requirements = PQS.ModiferRequirements.Default;
                        lightDirection.modEnabled   = true;
                        lightDirection.order        = 100;

                        // Create the UV planet relative position
                        mod = new GameObject("_Material_SurfaceQuads");
                        mod.transform.parent = controllerRoot.transform;
                        PQSMod_UVPlanetRelativePosition uvs = mod.AddComponent <PQSMod_UVPlanetRelativePosition>();
                        uvs.sphere       = pqsVersion;
                        uvs.requirements = PQS.ModiferRequirements.Default;
                        uvs.modEnabled   = true;
                        uvs.order        = 999999;

                        // Crete the quad mesh colliders
                        mod = new GameObject("QuadMeshColliders");
                        mod.transform.parent = controllerRoot.gameObject.transform;
                        PQSMod_QuadMeshColliders collider = mod.AddComponent <PQSMod_QuadMeshColliders>();
                        collider.sphere                          = pqsVersion;
                        collider.maxLevelOffset                  = 0;
                        collider.physicsMaterial                 = new PhysicMaterial();
                        collider.physicsMaterial.name            = "Ground";
                        collider.physicsMaterial.dynamicFriction = 0.6f;
                        collider.physicsMaterial.staticFriction  = 0.8f;
                        collider.physicsMaterial.bounciness      = 0.0f;
                        collider.physicsMaterial.frictionCombine = PhysicMaterialCombine.Maximum;
                        collider.physicsMaterial.bounceCombine   = PhysicMaterialCombine.Average;
                        collider.requirements                    = PQS.ModiferRequirements.Default;
                        collider.modEnabled                      = true;
                        collider.order = 100;

                        // Assing the new PQS
                        Current.pqsController              = pqsVersion;
                        pqsVersion.transform.position      = Current.transform.position;
                        pqsVersion.transform.localPosition = Vector3.zero;

                        // Set mode
                        _mode = Modes.List;
                    }, new Rect(20, index * distance + 10, 350, 20));
                    return;
                }

                // Mode List
                if (_mode == Modes.List)
                {
                    // Get the PQS-Spheres and their mods
                    IEnumerable <PQS>    pqsList    = Current.GetComponentsInChildren <PQS>(true);
                    IEnumerable <PQSMod> pqsModList = Current.GetComponentsInChildren <PQSMod>(true);

                    // Scroll
                    BeginScrollView(250, (pqsList.Count() + pqsModList.Count()) * distance + distance * 4, 20);

                    // Index
                    index = 0;

                    // Render
                    foreach (PQS pqs in pqsList)
                    {
                        Button(pqs.ToString(), () =>
                        {
                            _mode   = Modes.PQS;
                            _sphere = pqs;
                        }, new Rect(20, index * distance + 10, 350, 20));
                    }
                    foreach (PQSMod mod in pqsModList)
                    {
                        Button(mod.ToString(), () =>
                        {
                            _mode   = Modes.PQSMod;
                            _sphere = mod.sphere;
                            _mod    = mod;
                        }, new Rect(20, index * distance + 10, 350, 20));
                    }
                    index++;
                    Button(Localization.LOC_KITTOPIATECH_PQSEDITOR_ADD_MOD, () => _mode = Modes.AddMod, new Rect(20, index * distance + 10, 350, 20));
                    if (Current.pqsController.ChildSpheres.All(s => s.name != Current.pqsController.name + "Ocean"))
                    {
                        Button(Localization.LOC_KITTOPIATECH_PQSEDITOR_ADD_OCEAN, () =>
                        {
                            // Generate the PQS object
                            GameObject gameObject = new GameObject("Ocean");
                            gameObject.layer      = Constants.GameLayers.LocalSpace;
                            PQS ocean             = gameObject.AddComponent <PQS>();

                            // Setup materials
                            PSystemBody Body = Utility.FindBody(Injector.StockSystemPrefab.rootBody, "Laythe");
                            foreach (PQS oc in Body.pqsVersion.GetComponentsInChildren <PQS>(true))
                            {
                                if (oc.name != "LaytheOcean")
                                {
                                    continue;
                                }

                                // Copying Laythes Ocean-properties
                                Utility.CopyObjectFields <PQS>(oc, ocean);
                            }

                            // Load our new Material into the PQS
                            ocean.surfaceMaterial      = new PQSOceanSurfaceQuadLoader(ocean.surfaceMaterial);
                            ocean.surfaceMaterial.name = Guid.NewGuid().ToString();

                            // Load fallback material into the PQS
                            ocean.fallbackMaterial      = new PQSOceanSurfaceQuadFallbackLoader(ocean.fallbackMaterial);
                            ocean.fallbackMaterial.name = Guid.NewGuid().ToString();

                            // Create the UV planet relative position
                            GameObject mod       = new GameObject("_Material_SurfaceQuads");
                            mod.transform.parent = gameObject.transform;
                            PQSMod_UVPlanetRelativePosition uvs = mod.AddComponent <PQSMod_UVPlanetRelativePosition>();
                            uvs.sphere       = ocean;
                            uvs.requirements = PQS.ModiferRequirements.Default;
                            uvs.modEnabled   = true;
                            uvs.order        = 999999;

                            // Create the AerialPerspective Material
                            AerialPerspectiveMaterial mat = new AerialPerspectiveMaterial();
                            mat.Create(ocean);

                            // Create the OceanFX
                            OceanFX oceanFX = new OceanFX();
                            oceanFX.Create(ocean);

                            // Apply the Ocean
                            ocean.transform.parent = Current.pqsController.transform;

                            // Add the ocean PQS to the secondary renders of the CelestialBody Transform
                            PQSMod_CelestialBodyTransform transform = Current.pqsController.GetComponentsInChildren <PQSMod_CelestialBodyTransform>(true).FirstOrDefault(mod_ => mod_.transform.parent == Current.pqsController.transform);
                            transform.planetFade.secondaryRenderers.Add(ocean.gameObject);
                            typeof(PQS).GetField("_childSpheres", BindingFlags.Instance | BindingFlags.NonPublic)?.SetValue(Current.pqsController, null);

                            // Names!
                            ocean.name            = Current.pqsController.name + "Ocean";
                            ocean.gameObject.name = Current.pqsController.name + "Ocean";
                            ocean.transform.name  = Current.pqsController.name + "Ocean";

                            // Set up the ocean PQS
                            ocean.parentSphere            = Current.pqsController;
                            ocean.transform.position      = Current.pqsController.transform.position;
                            ocean.transform.localPosition = Vector3.zero;
                            ocean.radius = Current.Radius;
                        }, new Rect(20, index * distance + 10, 350, 20));
                    }
                    else
                    {
                        Button(Localization.LOC_KITTOPIATECH_PQSEDITOR_REMOVE_OCEAN, () =>
                        {
                            // Find atmosphere the ocean PQS
                            PQS ocean = Current.pqsController.GetComponentsInChildren <PQS>(true).First(pqs => pqs != Current.pqsController);
                            PQSMod_CelestialBodyTransform cbt = Current.pqsController.GetComponentsInChildren <PQSMod_CelestialBodyTransform>(true).First();

                            // Destroy the ocean PQS (this could be bad - destroying the secondary fades...)
                            cbt.planetFade.secondaryRenderers.Remove(ocean.gameObject);
                            typeof(PQS).GetField("_childSpheres", BindingFlags.Instance | BindingFlags.NonPublic)?.SetValue(Current.pqsController, null);
                            cbt.secondaryFades     = new PQSMod_CelestialBodyTransform.AltitudeFade[0];
                            ocean.transform.parent = null;
                            UnityEngine.Object.Destroy(ocean);
                        }, new Rect(20, index * distance + 10, 350, 20));
                    }

                    // End Scroll
                    EndScrollView();
                }

                // Mode PQS
                if (_mode == Modes.PQS)
                {
                    // Scroll
                    BeginScrollView(250, Utils.GetScrollSize <PQS>() + Utils.GetScrollSize <HazardousOcean>() + distance * 1, 20);

                    // Index
                    index = 0;

                    // Render the PQS
                    RenderObject(_sphere);

                    // If it is an ocean, create an Hazardous Ocean button
                    if (PQSOceanSurfaceQuad.UsesSameShader(_sphere.surfaceMaterial))
                    {
                        Label("hazardousOcean"); index--;
                        if (_sphere.GetComponent <HazardousOcean>() != null)
                        {
                            Button(Localization.LOC_KITTOPIATECH_EDIT, () =>
                            {
                                UIController.Instance.SetEditedObject(KittopiaWindows.Curve, _sphere.GetComponent <HazardousOcean>().heatCurve ?? new FloatCurve(), c => _sphere.GetComponent <HazardousOcean>().heatCurve = c);
                                UIController.Instance.EnableWindow(KittopiaWindows.Curve);
                            }, new Rect(200, index * distance + 10, 75, 20)); index--;
                            Button(Localization.LOC_KITTOPIATECH_REMOVE, () => UnityEngine.Object.DestroyImmediate(_sphere.GetComponent <HazardousOcean>()), new Rect(285, index * distance + 10, 75, 20));
                        }
                        else
                        {
                            Button(Localization.LOC_KITTOPIATECH_PQSEDITOR_ADD_HAZOCEAN, () => _sphere.gameObject.AddComponent <HazardousOcean>(), new Rect(200, index * distance + 10, 170, 20));
                        }
                    }
                    index++;

                    // Rebuild
                    Button(Localization.LOC_KITTOPIATECH_PQSEDITOR_REBUILD, () => _sphere.RebuildSphere());

                    // End Scroll
                    EndScrollView();
                }

                // Mode PQSMod
                if (_mode == Modes.PQSMod)
                {
                    // Scroll
                    BeginScrollView(250, Utils.GetScrollSize(_mod.GetType()) + distance * 5, 20);

                    // Index
                    index = 0;

                    // Render the PQS
                    RenderObject(_mod);
                    index++;

                    // Rebuild
                    Button(Localization.LOC_KITTOPIATECH_PQSEDITOR_REBUILD, () => _sphere.RebuildSphere());

                    // Remove
                    Button(Localization.LOC_KITTOPIATECH_PQSEDITOR_REMOVE_MOD, () =>
                    {
                        _mod.sphere = null;
                        UnityEngine.Object.Destroy(_mod);
                        _mod = null;

                        // Hack
                        _sphere.SetupExternalRender();
                        _sphere.CloseExternalRender();

                        _mode = Modes.List;
                    });

                    // End Scroll
                    EndScrollView();
                }

                // Mode AddPQSMod
                if (_mode == Modes.AddMod)
                {
                    // Get all PQSMod types
                    List <Type> types = Parser.ModTypes.Where(t => t.IsSubclassOf(typeof(PQSMod))).ToList();

                    // Begin Scroll
                    BeginScrollView(250, types.Count * distance + 10, 20);

                    // Index
                    index = 0;

                    // Render the possible types
                    foreach (Type t in types)
                    {
                        Button(t.FullName, () =>
                        {
                            // Hack^6
                            GameObject pqsModObject       = new GameObject(t.Name);
                            pqsModObject.transform.parent = Current.pqsController.transform;
                            PQSMod mod = pqsModObject.AddComponent(t) as PQSMod;
                            mod.sphere = Current.pqsController;

                            if (t == typeof(PQSMod_VoronoiCraters))
                            {
                                PQS mun = Utility.FindBody(Injector.StockSystemPrefab.rootBody, "Mun").pqsVersion;
                                PQSMod_VoronoiCraters craters = mun.GetComponentsInChildren <PQSMod_VoronoiCraters>()[0];
                                PQSMod_VoronoiCraters nc      = pqsModObject.GetComponentsInChildren <PQSMod_VoronoiCraters>()[0];
                                nc.craterColourRamp           = craters.craterColourRamp;
                                nc.craterCurve = craters.craterCurve;
                                nc.jitterCurve = craters.jitterCurve;
                            }
                            else if (t == typeof(PQSMod_VertexPlanet))
                            {
                                PQSMod_VertexPlanet vp     = mod as PQSMod_VertexPlanet;
                                vp.landClasses             = new [] { new PQSMod_VertexPlanet.LandClass("Class", 0, 1, Color.black, Color.white, 0) };
                                vp.continental             = new PQSMod_VertexPlanet.SimplexWrapper(0, 0, 0, 0);
                                vp.continentalRuggedness   = new PQSMod_VertexPlanet.SimplexWrapper(0, 0, 0, 0);
                                vp.continentalSharpness    = new PQSMod_VertexPlanet.NoiseModWrapper(0, 0, 0, 0);
                                vp.continentalSharpnessMap = new PQSMod_VertexPlanet.SimplexWrapper(0, 0, 0, 0);
                                vp.terrainType             = new PQSMod_VertexPlanet.SimplexWrapper(0, 0, 0, 0);
                            }
                            else if (t == typeof(PQSMod_HeightColorMap))
                            {
                                (mod as PQSMod_HeightColorMap).landClasses = new [] { new PQSMod_HeightColorMap.LandClass("Class", 0, 1, Color.black, Color.white, 0) };
                            }
                            else if (t == typeof(PQSMod_HeightColorMap2))
                            {
                                (mod as PQSMod_HeightColorMap2).landClasses = new[] { new PQSMod_HeightColorMap2.LandClass("Class", 0, 1, Color.black, Color.white, 0) };
                            }
                            else if (t == typeof(PQSMod_HeightColorMapNoise))
                            {
                                (mod as PQSMod_HeightColorMapNoise).landClasses = new[] { new PQSMod_HeightColorMapNoise.LandClass("Class", 0, 1, Color.black, Color.white, 0) };
                            }
                            else if (t == typeof(PQSLandControl))
                            {
                                PQSLandControl lc  = mod as PQSLandControl;
                                lc.altitudeSimplex = new Simplex();
                                lc.scatters        = new PQSLandControl.LandClassScatter[0];
                                lc.landClasses     = new [] { new PQSLandControl.LandClass()
                                                              {
                                                                  altitudeRange       = new PQSLandControl.LerpRange(),
                                                                  coverageSimplex     = new Simplex(),
                                                                  longitudeRange      = new PQSLandControl.LerpRange(),
                                                                  latitudeDoubleRange = new PQSLandControl.LerpRange(),
                                                                  latitudeRange       = new PQSLandControl.LerpRange(),
                                                                  scatter             = new PQSLandControl.LandClassScatterAmount[0]
                                                              } };
                                lc.latitudeSimplex  = new Simplex();
                                lc.longitudeSimplex = new Simplex();
                            }

                            // Edit the mod
                            _mod    = mod;
                            _sphere = mod.sphere;
                            _mode   = Modes.PQSMod;
                        }, new Rect(20, index * distance + 10, 350, 20));
                    }


                    // End Scroll
                    EndScrollView();
                }
            }
Пример #3
0
            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.SetupExternalRender();

                // 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));

                // Get all mods the PQS is connected to
                PQSMod[] mods = pqsVersion.GetComponentsInChildren <PQSMod>()
                                .Where(m => m.sphere == pqsVersion && m.modEnabled).OrderBy(m => m.order).ToArray();

                // Prevent the PQS from updating
                pqsVersion.enabled = false;

                // 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 name = "KittopiaTech/PluginData/" + celestialBody.transform.name + "/";
                String path = KSPUtil.ApplicationRootPath + "/GameData/" + 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());
                        colorMap.name = name + celestialBody.transform.name + "_Color.png";
                        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, pqsVersion.radius, options.NormalStrength);
                        yield return(null);

                        if (options.SaveToDisk)
                        {
                            File.WriteAllBytes(path + celestialBody.transform.name + "_Normal.png",
                                               normalMap.EncodeToPNG());
                            normalMap.name = name + celestialBody.transform.name + "_Normal.png";
                            yield return(null);
                        }

                        // Apply it
                        if (options.ApplyToScaled)
                        {
                            normalMap.Apply();
                            celestialBody.scaledBody.GetComponent <MeshRenderer>().material
                            .SetTexture("_BumpMap", normalMap);
                        }
                    }
                }

                // Close the Renderer
                pqsVersion.enabled = true;
                pqsVersion.CloseExternalRender();

                // Declare that we're done
                ScreenMessages.RemoveMessage(message);
                ScreenMessages.PostScreenMessage("Operation completed in: " + (DateTime.Now - now).TotalMilliseconds + " ms", 2f, ScreenMessageStyle.UPPER_CENTER);
            }
            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)
                {
                    yield break;
                }

                // Tell the PQS that we are going to build maps
                pqsVersion.SetupExternalRender();

                // Get the mod building methods from the PQS
                #if !KSP131
                Action <PQS.VertexBuildData, Boolean> modOnVertexBuildHeight =
                    (Action <PQS.VertexBuildData, Boolean>)Delegate.CreateDelegate(
                        typeof(Action <PQS.VertexBuildData, Boolean>),
                        pqsVersion,
                        typeof(PQS).GetMethod("Mod_OnVertexBuildHeight",
                                              BindingFlags.Instance | BindingFlags.NonPublic));
                #else
                Action <PQS.VertexBuildData> modOnVertexBuildHeight =
                    (Action <PQS.VertexBuildData>)Delegate.CreateDelegate(
                        typeof(Action <PQS.VertexBuildData>),
                        pqsVersion,
                        typeof(PQS).GetMethod("Mod_OnVertexBuildHeight",
                                              BindingFlags.Instance | BindingFlags.NonPublic));
                #endif
                Action <PQS.VertexBuildData> modOnVertexBuild = (Action <PQS.VertexBuildData>)Delegate.CreateDelegate(
                    typeof(Action <PQS.VertexBuildData>),
                    pqsVersion,
                    typeof(PQS).GetMethod("Mod_OnVertexBuild", BindingFlags.Instance | BindingFlags.NonPublic));

                // Get all mods the PQS is connected to
                PQSMod[] mods = pqsVersion.GetComponentsInChildren <PQSMod>()
                                .Where(m => m.sphere == pqsVersion && m.modEnabled).OrderBy(m => m.order).ToArray();

                // Prevent the PQS from updating
                pqsVersion.enabled = false;

                // Create the Textures
                Texture2D colorMap = new Texture2D(options.Resolution, options.Resolution / 2,
                                                   TextureFormat.ARGB32,
                                                   true);
                Texture2D heightMap = new Texture2D(options.Resolution, options.Resolution / 2,
                                                    TextureFormat.RGB24,
                                                    true);

                // Arrays
                Color[] colorMapValues  = new Color[options.Resolution * (options.Resolution / 2)];
                Color[] heightMapValues = new Color[options.Resolution * (options.Resolution / 2)];

                // Create a VertexBuildData
                PQS.VertexBuildData data = new PQS.VertexBuildData();

                // Display
                ScreenMessage message = ScreenMessages.PostScreenMessage("Generating terrain data", Single.MaxValue, ScreenMessageStyle.UPPER_CENTER);
                yield return(null);

                Double[] heightValues = new Double[options.Resolution * (options.Resolution / 2)];

                // Loop through the pixels
                for (Int32 y = 0; y < options.Resolution / 2; y++)
                {
                    // Update Message
                    Double percent = y / (options.Resolution / 2d) * 100;
                    while (CanvasUpdateRegistry.IsRebuildingLayout())
                    {
                        Thread.Sleep(10);
                    }
                    message.textInstance.text.text = "Generating terrain data: " + percent.ToString("0.00") + "%";

                    for (Int32 x = 0; x < options.Resolution; x++)
                    {
                        // Update the VertexBuildData
                        data.directionFromCenter =
                            QuaternionD.AngleAxis(360d / options.Resolution * x, Vector3d.up) *
                            QuaternionD.AngleAxis(90d - 180d / (options.Resolution / 2f) * y, Vector3d.right)
                            * Vector3d.forward;
                        data.vertHeight = pqsVersion.radius;

                        #if !KSP131
                        modOnVertexBuildHeight(data, true);
                        #else
                        modOnVertexBuildHeight(data);
                        #endif
                        modOnVertexBuild(data);

                        // Cache the results
                        heightValues[y * options.Resolution + x]   = data.vertHeight;
                        colorMapValues[y * options.Resolution + x] = data.vertColor;
                    }
                    yield return(null);
                }

                // Update Message
                while (CanvasUpdateRegistry.IsRebuildingLayout())
                {
                    Thread.Sleep(10);
                }
                message.textInstance.text.text = "Calculating height difference";

                // Figure out the delta radius ourselves
                Double minHeight = Double.MaxValue;
                Double maxHeight = Double.MinValue;
                for (Int32 i = 0; i < heightValues.Length; i++)
                {
                    if (heightValues[i] > maxHeight)
                    {
                        maxHeight = heightValues[i];
                    }
                    else if (heightValues[i] < minHeight)
                    {
                        minHeight = heightValues[i];
                    }
                }
                Double deltaRadius = maxHeight - minHeight;
                yield return(null);

                // Update Message
                while (CanvasUpdateRegistry.IsRebuildingLayout())
                {
                    Thread.Sleep(10);
                }
                message.textInstance.text.text = "Calculating color data";

                // Apply the values
                for (Int32 y = 0; y < options.Resolution / 2; y++)
                {
                    // Update Message
                    Double percent = y / (options.Resolution / 2d) * 100;
                    while (CanvasUpdateRegistry.IsRebuildingLayout())
                    {
                        Thread.Sleep(10);
                    }
                    message.textInstance.text.text = "Calculating color data: " + percent.ToString("0.00") + "%";

                    for (Int32 x = 0; x < options.Resolution; x++)
                    {
                        // Build from the Mods
                        Double height = heightValues[y * options.Resolution + x] - pqsVersion.radius;
                        if (options.ExportColor)
                        {
                            // Adjust the Color
                            Color color = colorMapValues[y * options.Resolution + x];
                            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 * options.Resolution + x] = color;
                        }
                        if (options.ExportHeight)
                        {
                            // Adjust the height
                            height = height / deltaRadius;
                            if (height < 0)
                            {
                                height = 0;
                            }
                            else if (height > 1)
                            {
                                height = 1;
                            }

                            // Set the Pixels
                            heightMapValues[y * options.Resolution + x] =
                                new Color((Single)height, (Single)height, (Single)height);
                        }
                    }

                    yield return(null);
                }

                // Serialize the maps to disk
                String name = "KittopiaTech/PluginData/" + celestialBody.transform.name + "/" + DateTime.Now.ToString("yyyy-MM-dd_hh-mm-ss") + "/";
                String path = KSPUtil.ApplicationRootPath + "/GameData/" + name;
                Directory.CreateDirectory(path);

                // Colormap
                if (options.ExportColor)
                {
                    // Update Message
                    while (CanvasUpdateRegistry.IsRebuildingLayout())
                    {
                        Thread.Sleep(10);
                    }
                    message.textInstance.text.text = "Exporting planet maps: Color";

                    // Save it
                    colorMap.SetPixels(colorMapValues);
                    yield return(null);

                    if (options.SaveToDisk)
                    {
                        File.WriteAllBytes(path + celestialBody.transform.name + "_Color.png", colorMap.EncodeToPNG());
                        colorMap.name = name + celestialBody.transform.name + "_Color.png";
                        yield return(null);
                    }

                    // Apply it
                    if (options.ApplyToScaled)
                    {
                        ScaledSpaceOnDemand od = celestialBody.scaledBody.GetComponent <ScaledSpaceOnDemand>();
                        if (od != null)
                        {
                            od.texture = colorMap.name;
                            UnityEngine.Object.DestroyImmediate(colorMap);

                            if (od.isLoaded)
                            {
                                od.UnloadTextures();
                                od.LoadTextures();
                            }
                        }
                        else
                        {
                            colorMap.Apply();
                            celestialBody.scaledBody.GetComponent <MeshRenderer>().sharedMaterial.SetTexture("_MainTex", colorMap);
                        }
                    }
                    else
                    {
                        UnityEngine.Object.DestroyImmediate(colorMap);
                    }
                }

                if (options.ExportHeight)
                {
                    // Update Message
                    while (CanvasUpdateRegistry.IsRebuildingLayout())
                    {
                        Thread.Sleep(10);
                    }
                    message.textInstance.text.text = "Exporting planet maps: Height";

                    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)
                    {
                        // Update Message
                        while (CanvasUpdateRegistry.IsRebuildingLayout())
                        {
                            Thread.Sleep(10);
                        }
                        message.textInstance.text.text = "Exporting planet maps: Normal";

                        // Bump to Normal Map
                        Texture2D normalMap = Utility.BumpToNormalMap(heightMap, pqsVersion, options.NormalStrength / 10);
                        yield return(null);

                        if (options.SaveToDisk)
                        {
                            File.WriteAllBytes(path + celestialBody.transform.name + "_Normal.png",
                                               normalMap.EncodeToPNG());
                            normalMap.name = name + celestialBody.transform.name + "_Normal.png";
                            yield return(null);
                        }

                        // Apply it
                        if (options.ApplyToScaled)
                        {
                            ScaledSpaceOnDemand od = celestialBody.scaledBody.GetComponent <ScaledSpaceOnDemand>();
                            if (od != null)
                            {
                                od.normals = normalMap.name;
                                UnityEngine.Object.DestroyImmediate(normalMap);

                                if (od.isLoaded)
                                {
                                    od.UnloadTextures();
                                    od.LoadTextures();
                                }
                            }
                            else
                            {
                                normalMap.Apply();
                                celestialBody.scaledBody.GetComponent <MeshRenderer>().sharedMaterial
                                .SetTexture("_BumpMap", normalMap);
                            }
                        }
                        else
                        {
                            UnityEngine.Object.DestroyImmediate(normalMap);
                        }
                    }

                    UnityEngine.Object.DestroyImmediate(heightMap);
                }

                // Close the Renderer
                pqsVersion.enabled = true;
                pqsVersion.CloseExternalRender();

                // Declare that we're done
                ScreenMessages.RemoveMessage(message);
                ScreenMessages.PostScreenMessage("Operation completed in: " + (DateTime.Now - now).TotalMilliseconds + " ms", 2f, ScreenMessageStyle.UPPER_CENTER);
            }