/// <summary> /// Generates a thumbnail for the planet /// </summary> public static Texture2D GetPlanetThumbnail(CelestialBody body) { // Config RuntimePreviewGenerator.TransparentBackground = true; RuntimePreviewGenerator.BackgroundColor = Color.clear; RuntimePreviewGenerator.PreviewDirection = Vector3.forward; RuntimePreviewGenerator.Padding = -0.15f; ScaledSpaceOnDemand od = body.scaledBody.GetComponent <ScaledSpaceOnDemand>(); Boolean isLoaded = true; if (od != null) { isLoaded = od.isLoaded; if (!isLoaded) { od.LoadTextures(); } } GameObject sphere = GameObject.CreatePrimitive(PrimitiveType.Sphere); sphere.GetComponentInChildren <MeshFilter>().sharedMesh = body.scaledBody.GetComponent <MeshFilter>().sharedMesh; sphere.GetComponentInChildren <MeshRenderer>().sharedMaterial = body.scaledBody.GetComponent <MeshRenderer>().sharedMaterial; Texture2D finalTexture = RuntimePreviewGenerator.GenerateModelPreview(sphere.transform, 256, 256); Object.DestroyImmediate(sphere); if (!isLoaded) { od.UnloadTextures(); } return(finalTexture); }
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); }