void ApplyImportSettings(AlloyCustomImportObject settings, Texture2D texture, string path) { var importer = assetImporter as TextureImporter; var size = settings.GetOutputSize(); var def = settings.PackMode; var importSettings = def.ImportSettings; importer.textureType = TextureImporterType.Default; importer.sRGBTexture = !importSettings.IsLinear; importer.filterMode = importSettings.Filter; importer.mipmapEnabled = true; // They need the ability to set this themselves, but we should cap it. var nextPowerOfTwo = Mathf.NextPowerOfTwo((int)Mathf.Max(size.x, size.y)); if (importer.maxTextureSize > nextPowerOfTwo) { importer.maxTextureSize = nextPowerOfTwo; } // Allow setting to uncompressed, else use compressed. Disallows any other format! if (def.ImportSettings.DefaultCompressed && importer.textureCompression != TextureImporterCompression.Uncompressed && importer.textureCompression != TextureImporterCompression.CompressedLQ && importer.textureCompression != TextureImporterCompression.CompressedHQ) { importer.textureCompression = TextureImporterCompression.Compressed; } }
private void ApplyImportSettings(AlloyCustomImportObject settings, Texture2D texture, string path) { var importer = assetImporter as TextureImporter; var size = settings.GetOutputSize(); var def = settings.PackMode; if (def.ImportSettings.IsLinear) { importer.textureType = TextureImporterType.Advanced; importer.linearTexture = true; } importer.filterMode = def.ImportSettings.Filter; importer.mipmapEnabled = true; // They need the ability to set this themselves, but we should cap it. var nextPowerOfTwo = Mathf.NextPowerOfTwo((int)Mathf.Max(size.x, size.y)); if (importer.maxTextureSize > nextPowerOfTwo) { importer.maxTextureSize = nextPowerOfTwo; } // Allow setting to uncompressed, else use compressed. Disallows any other format! if (def.ImportSettings.DefaultCompressed && importer.textureFormat != TextureImporterFormat.AutomaticTruecolor) { importer.textureFormat = TextureImporterFormat.AutomaticCompressed; } }
private void OnGUI() { if (Target == null) { Target = CreateInstance<AlloyCustomImportObject>(); UpdateDefaults(); } ScrollPosition = EditorGUILayout.BeginScrollView(ScrollPosition, false, false, GUILayout.MinWidth(c_editorMinWidth), GUILayout.MaxWidth(position.width)); GUILayout.Space(10.0f); var def = Target.PackMode; // Pack mode tabs. using (new EditorGUILayout.HorizontalScope()) { foreach (var tabMode in GlobalDefinition.PackedMaps) { EditorGUI.BeginChangeCheck(); bool toggle = GUILayout.Toggle(def == tabMode, tabMode.Title, EditorStyles.toolbarButton); if (EditorGUI.EndChangeCheck()) { if (toggle && def != tabMode) { Target.PackMode = tabMode; //Update packed map definition UpdateDefaults(); } } } } string curPath; var suffix = Target.PackMode.Suffix; bool isValid = true; isValid = BaseGUI(); isValid = FileEntryGUI(suffix, ".png", ref SaveName, out curPath) && isValid; if (GenerateButtonGUI("Generate", isValid)) { var path = curPath + "/" + SaveName; path += suffix; var current = Target; Target = Instantiate(current); AlloyCustomImportAction.CreatePostProcessingInformation(path + ".asset", current); } EditorGUILayout.EndScrollView(); }
/// <summary> /// Generates the packed material map for an object /// </summary> public static void GeneratePackedMaterialMap(AlloyCustomImportObject settings, Texture2D target, string filePath) { var size = settings.GetOutputSize(); var normalMap = settings.NormalMapTexture; var useUnityGeneratedMipmaps = normalMap == null; int width = (int)size.x; int height = (int)size.y; int mipmapCount = 1; // When explicitly generating mip levels pick output count based on the largest input texture. if (!useUnityGeneratedMipmaps) { mipmapCount = GetMipmapCount(normalMap); for (int i = 0; i < 4; ++i) { if (settings.SelectedModes[i] != TextureValueChannelMode.Texture || settings.GetTexture(i) == null) { continue; } mipmapCount = Math.Max(mipmapCount, GetMipmapCount(settings.GetTexture(i))); } } // Adjust the dimensions of the output texture if necessary. if (target.width != width || target.height != height) { target.Resize(width, height); } if (!Mathf.IsPowerOfTwo(width) || !Mathf.IsPowerOfTwo(height)) { Debug.LogWarning( "Alloy: Texture resolution is not power of 2; will have issues generating correct mip maps if custom sizing is specified in generated texture platform settings."); } // Get readable input textures. var readableNormal = AlloyTextureReader.GetReadable(normalMap, true); var readableTextures = new Texture2D[settings.TexturesGUID.Length]; for (int i = 0; i < settings.TexturesGUID.Length; ++i) { if (settings.SelectedModes[i] != TextureValueChannelMode.Texture) { continue; } var settingsTex = settings.GetTexture(i); if (settingsTex == null) { readableTextures[i] = null; } else { readableTextures[i] = AlloyTextureReader.GetReadable(settingsTex, false); } } // Use renderer to sample mipmaps. try { var message = string.Format("Packing: \"{0}\"", settings.name); var progress = 1.0f; var bodyText = message; for (int mipLevel = 0; mipLevel < mipmapCount; mipLevel++) { if (mipmapCount > 1) { progress = (float)mipLevel / (mipmapCount - 1); bodyText = string.Format("{0} ({1})", message, progress.ToString("0%")); } EditorUtility.DisplayProgressBar("Building Packed maps...", bodyText, progress); // CPU Method - more reliable/consistent across GPUs, but slower. var normalCache = new AlloyTextureColorCache(readableNormal, target); UnityEngine.Profiling.Profiler.BeginSample("Read"); var texCache = readableTextures.Select(tex => new AlloyTextureColorCache(tex, target)).ToArray(); UnityEngine.Profiling.Profiler.EndSample(); AlloyPackerCompositor.CompositeMips(target, settings, texCache, normalCache, mipLevel); } } finally { EditorUtility.ClearProgressBar(); } // Clean up the readable textures. foreach (var texture in readableTextures) { Object.DestroyImmediate(texture); } Object.DestroyImmediate(readableNormal); // Update the texture's associated settings .asset object. settings.Width = width; settings.Height = height; settings.MaxResolution = 0; EditorUtility.SetDirty(settings); // Update the texture object. target.Apply(useUnityGeneratedMipmaps, false); }
public static void CreatePostProcessingInformation(string filePath, AlloyCustomImportObject settings) { settings.hideFlags = HideFlags.None; AssetDatabase.CreateAsset(settings, filePath); }
private void ApplyImportSettings(AlloyCustomImportObject settings, Texture2D texture, string path) { var importer = assetImporter as TextureImporter; var size = settings.GetOutputSize(); var def = settings.PackMode; if (def.ImportSettings.IsLinear) { importer.textureType = TextureImporterType.Advanced; importer.linearTexture = true; } importer.filterMode = def.ImportSettings.Filter; importer.mipmapEnabled = true; // They need the ability to set this themselves, but we should cap it. var nextPowerOfTwo = Mathf.NextPowerOfTwo((int) Mathf.Max(size.x, size.y)); if (importer.maxTextureSize > nextPowerOfTwo) { importer.maxTextureSize = nextPowerOfTwo; } // Allow setting to uncompressed, else use compressed. Disallows any other format! if (def.ImportSettings.DefaultCompressed && importer.textureFormat != TextureImporterFormat.AutomaticTruecolor) { importer.textureFormat = TextureImporterFormat.AutomaticCompressed; } }
/// <summary> /// Generates the packed material map for an object /// </summary> public static void GeneratePackedMaterialMap(AlloyCustomImportObject settings, Texture2D source, string filePath) { int mipmapCount = 1; bool isEditorInLinearSpace = PlayerSettings.colorSpace == ColorSpace.Linear; Shader.SetGlobalFloat("_EditorIsLinear", isEditorInLinearSpace ? 1.0f : 0.0f); Vector2 size = settings.GetOutputSize(); int width = (int) size.x; int height = (int) size.y; // Pick output texture dimensions based on the largest input texture. for (int i = 0; i < 4; ++i) { if (settings.SelectedModes[i] != TextureValueChannelMode.Texture || settings.GetTexture(i) == null) { continue; } mipmapCount = Math.Max(mipmapCount, GetMipmapCount(settings.GetTexture(i))); } bool doMips; if (settings.NormalMapTexture != null) { var tex = settings.NormalMapTexture; var count = GetMipmapCount(tex); mipmapCount = Math.Max(mipmapCount, count); doMips = true; } else { mipmapCount = 1; doMips = false; } if (source.width != width || source.height != height) { source.Resize(width, height); } if (!Mathf.IsPowerOfTwo(width) || !Mathf.IsPowerOfTwo(height)) { Debug.LogWarning( "Alloy: Texture resolution is not power of 2; will have issues generating correct mip maps if custom sizing is specified in generated texture platform settings."); } var readableTextures = new Texture2D[settings.TexturesGUID.Length]; var linear = new bool[settings.TexturesGUID.Length]; for (int i = 0; i < settings.TexturesGUID.Length; ++i) { if (settings.SelectedModes[i] != TextureValueChannelMode.Texture) { continue; } var settingsTex = settings.GetTexture(i); if (settingsTex == null) { readableTextures[i] = null; linear[i] = false; } else { var texImporter = AssetImporter.GetAtPath(AssetDatabase.GetAssetPath(settingsTex)) as TextureImporter; if (texImporter == null) { Debug.LogError("Couldn't find packed map texture in asset database!"); return; } readableTextures[i] = AlloyTextureReader.GetReadable(settingsTex, false); linear[i] = isEditorInLinearSpace && !texImporter.linearTexture; } } var normal = AlloyTextureReader.GetReadable(settings.NormalMapTexture, true); // Use renderer to sample mipmaps. for (int mipLevel = 0; mipLevel < mipmapCount; mipLevel++) { // CPU Method - more reliable/consistent across GPUs, but slower. EditorUtility.DisplayProgressBar("Calculating Mip Maps...", "MipLevel " + mipLevel, (float) mipLevel / mipmapCount); try { AlloyPackerCompositor.CompositeMips(source, settings, readableTextures, linear, normal, mipLevel); } finally { EditorUtility.ClearProgressBar(); } } foreach (var texture in readableTextures) { Object.DestroyImmediate(texture); } Object.DestroyImmediate(normal); int maxResolution = 0; settings.Width = width; settings.Height = height; settings.MaxResolution = maxResolution; EditorUtility.SetDirty(settings); // Tells Unity to save changes to the settings .asset object on disk source.Apply(!doMips, false); }
public static void CompositeMips(Texture2D target, AlloyCustomImportObject source, Texture2D[] maps, bool[] linear, Texture2D normalMap, int mipLevel) { // Basically a 1:1 port of the original shader // The only point of major difference is the filtering method used; which is a fraction simpler. // This was disabled, since it appears GetPixels results don't appear to be affected by Unity's messing with Linear inputs; the same way they do at runtime. Re-enable if you like. int w = Mathf.Max(1, target.width >> mipLevel); int h = Mathf.Max(1, target.height >> mipLevel); var colors = new Color[w * h]; float offX = 0.5f / w; // Half a pixel float offY = 0.5f / h; float rangeX = (1.0f / (mipLevel + 1)) / w; float rangeY = (1.0f / (mipLevel + 1)) / h; foreach (var channel in source.PackMode.Channels) { var inIndices = channel.InputIndices.ToArray(); var outIndices = channel.OutputIndices.ToArray(); for (int x = 0; x < w; x++) { for (int y = 0; y < h; y++) { float u = (float)x / w + offX; float v = (float)y / h + offY; Color col = colors[y * w + x]; float variance = 0.0f, avgNormalLength = 0.0f; if (channel.UseNormals && normalMap != null) { Vector4 normal = GetChannels(normalMap, u, v, mipLevel, w, h, rangeX, rangeY); normal.x = (normal.x * 2.0f) - 1.0f; normal.y = (normal.y * 2.0f) - 1.0f; normal.z = (normal.z * 2.0f) - 1.0f; avgNormalLength = ((Vector3)normal).magnitude; float avgNormLen2 = avgNormalLength * avgNormalLength; float kappa = (3.0f * avgNormalLength - avgNormalLength * avgNormLen2) / (1.0f - avgNormLen2); variance = Mathf.Clamp01((1.0f / (2.0f * kappa)) - source.VarianceBias); } for (int i = 0; i < outIndices.Length; ++i) { int storeIndex = outIndices[i]; var tex = maps[storeIndex]; var channelVal = source.ChannelValues[storeIndex]; float input = 0.0f; if (channel.OutputVariance) { input = variance; } else if (inIndices.Length > 0) { int read = inIndices[Mathf.Min(i, inIndices.Length - 1)]; input = GetChannel(tex, channelVal, linear[storeIndex], u, v, mipLevel, rangeX, rangeY, read); } if (channel.RoughnessCorrect) { // Specular AA for Beckmann roughness. // cf http://www.frostbite.com/wp-content/uploads/2014/11/course_notes_moving_frostbite_to_pbr.pdf pg92 if (avgNormalLength < 1.0f) { float a = input * input; a = Mathf.Sqrt(Mathf.Clamp01(a * a + variance)); input = Mathf.Sqrt(a); } } if (source.DoInvert[storeIndex]) { input = 1.0f - input; } col[storeIndex] = input; } colors[y * w + x] = col; } } } target.SetPixels(colors, mipLevel); }
public static void CompositeMips(Texture2D target, AlloyCustomImportObject source, AlloyTextureColorCache[] mapCache, AlloyTextureColorCache normalCache, int mipLevel) { // Basically a 1:1 port of the original shader // The only point of major difference is the filtering method used; which is a fraction simpler. // This was disabled, since it appears GetPixels results don't appear to be affected by Unity's messing with Linear inputs; the same way they do at runtime. Re-enable if you like. int w = Mathf.Max(2, target.width >> mipLevel); int h = Mathf.Max(2, target.height >> mipLevel); var colors = new Color[w * h]; float rangeX = (1.0f / (mipLevel + 1)) / target.width; float rangeY = (1.0f / (mipLevel + 1)) / target.height; UnityEngine.Profiling.Profiler.BeginSample("Composite mips"); for (int channelIndex = 0; channelIndex < source.PackMode.Channels.Count; channelIndex++) { var channel = source.PackMode.Channels[channelIndex]; var inIndices = channel.InputIndices.ToArray(); var outIndices = channel.OutputIndices.ToArray(); bool hasInputs = inIndices.Length > 0; for (int i = 0; i < outIndices.Length; ++i) { int storeIndex = outIndices[i]; var tex = mapCache[storeIndex]; var channelVal = source.ChannelValues[storeIndex]; if (hasInputs) { int readIndex = inIndices[Mathf.Min(i, inIndices.Length - 1)]; tex.SetActiveChannel(readIndex); } bool doInvert = source.DoInvert[storeIndex]; bool doNormal = channel.UseNormals && !normalCache.EmptyTexture; bool doNative = hasInputs && tex.NativeSize && mipLevel == 0; UnityEngine.Profiling.Profiler.BeginSample("Blit"); for (int x = 0; x < w; ++x) { for (int y = 0; y < h; ++y) { var pixelIndex = x + y * w; var input = 0.0f; if (!hasInputs || tex.EmptyTexture) { input = channelVal; } else if (doNative) { input = tex.ActiveChannel[pixelIndex]; } else { input = tex.GetChannelBilinear((float)x / (w - 1), (float)y / (h - 1), mipLevel, rangeX, rangeY); } if (doInvert) { input = 1.0f - input; } if (doNormal) { Vector3 normal; if (normalCache.NativeSize && mipLevel == 0) { normal = (Vector4)normalCache.Values[pixelIndex]; } else { normal = normalCache.GetPixelNormal((float)x / (w - 1), (float)y / (h - 1), mipLevel, rangeX, rangeY); } normal.x = (normal.x * 2.0f) - 1.0f; normal.y = (normal.y * 2.0f) - 1.0f; normal.z = (normal.z * 2.0f) - 1.0f; // Specular AA for Beckmann roughness. // cf http://www.frostbite.com/wp-content/uploads/2014/11/course_notes_moving_frostbite_to_pbr.pdf pg92 var variance = 0.0f; var avgNormalLength = normal.magnitude; var applyAA = avgNormalLength < 1.0f; if (applyAA) { float avgNormLen2 = avgNormalLength * avgNormalLength; float kappa = (3.0f * avgNormalLength - avgNormalLength * avgNormLen2) / (1.0f - avgNormLen2); variance = Mathf.Clamp01(1.0f / (2.0f * kappa));// - source.VarianceBias); } if (channel.OutputVariance) { input = variance; } else if (channel.RoughnessCorrect && applyAA) { float a = input * input; a = Mathf.Sqrt(Mathf.Clamp01(a * a + variance)); input = Mathf.Sqrt(a); } } switch (storeIndex) { case 0: colors[pixelIndex].r = input; break; case 1: colors[pixelIndex].g = input; break; case 2: colors[pixelIndex].b = input; break; case 3: colors[pixelIndex].a = input; break; } } } UnityEngine.Profiling.Profiler.EndSample(); } } UnityEngine.Profiling.Profiler.BeginSample("Set pixels"); target.SetPixels(colors, mipLevel); UnityEngine.Profiling.Profiler.EndSample(); UnityEngine.Profiling.Profiler.EndSample(); }
/// <summary> /// Generates the packed material map for an object /// </summary> public static void GeneratePackedMaterialMap(AlloyCustomImportObject settings, Texture2D target, string filePath) { int mipmapCount = 1; Vector2 size = settings.GetOutputSize(); int width = (int)size.x; int height = (int)size.y; // Pick output texture dimensions based on the largest input texture. for (int i = 0; i < 4; ++i) { if (settings.SelectedModes[i] != TextureValueChannelMode.Texture || settings.GetTexture(i) == null) { continue; } mipmapCount = Math.Max(mipmapCount, GetMipmapCount(settings.GetTexture(i))); } bool doMips; if (settings.NormalMapTexture != null) { var tex = settings.NormalMapTexture; var count = GetMipmapCount(tex); mipmapCount = Math.Max(mipmapCount, count); doMips = true; } else { mipmapCount = 1; doMips = false; } if (target.width != width || target.height != height) { target.Resize(width, height); } if (!Mathf.IsPowerOfTwo(width) || !Mathf.IsPowerOfTwo(height)) { Debug.LogWarning( "Alloy: Texture resolution is not power of 2; will have issues generating correct mip maps if custom sizing is specified in generated texture platform settings."); } var readableTextures = new Texture2D[settings.TexturesGUID.Length]; for (int i = 0; i < settings.TexturesGUID.Length; ++i) { if (settings.SelectedModes[i] != TextureValueChannelMode.Texture) { continue; } var settingsTex = settings.GetTexture(i); if (settingsTex == null) { readableTextures[i] = null; } else { readableTextures[i] = AlloyTextureReader.GetReadable(settingsTex, false); } } var normal = AlloyTextureReader.GetReadable(settings.NormalMapTexture, true); try { // Use renderer to sample mipmaps. for (int mipLevel = 0; mipLevel < mipmapCount; mipLevel++) { // CPU Method - more reliable/consistent across GPUs, but slower. if (mipmapCount > 1) { EditorUtility.DisplayProgressBar("Calculating Mip Maps...", "MipLevel " + mipLevel, (float)mipLevel / mipmapCount); } else { EditorUtility.DisplayProgressBar("Calculating Packed map...", "Packing...", 1.0f); } var normalCache = new AlloyTextureColorCache(normal, target); Profiler.BeginSample("Read"); var texCache = readableTextures.Select(tex => new AlloyTextureColorCache(tex, target)).ToArray(); Profiler.EndSample(); AlloyPackerCompositor.CompositeMips(target, settings, texCache, normalCache, mipLevel); } } finally { EditorUtility.ClearProgressBar(); } foreach (var texture in readableTextures) { Object.DestroyImmediate(texture); } Object.DestroyImmediate(normal); int maxResolution = 0; settings.Width = width; settings.Height = height; settings.MaxResolution = maxResolution; EditorUtility.SetDirty(settings); // Tells Unity to save changes to the settings .asset object on disk target.Apply(!doMips, false); }