private void OnGUI() { titleContent = Contents.title; mode = (BakeMode)EditorGUILayout.EnumPopup(Contents.mode, mode); switch (mode) { case BakeMode.Mesh: OnGUI_Mesh(); break; case BakeMode.Texture: OnGUI_Texture(); break; } }
void BakeDropDownCallback(object data) { BakeMode mode = (BakeMode)data; switch (mode) { case BakeMode.Clear: DoClear(); break; case BakeMode.BakeReflectionProbes: DoBakeReflectionProbes(); break; } }
/// <summary> /// Builds the project. /// </summary> /// <param name="path"></param> /// <param name="mode"></param> public static void Bake(DirectoryInfo path, BakeMode mode) { // Read the configuration file at destination var config = SiteConfig.Read(path); if (config.Languages == null || config.Languages.Count == 0) { // Make sure we have a default language config.Languages = new List<string>(); config.Languages.Add("default"); } Tracing.Info("Bake", "Baking: " + path.FullName); foreach(var language in config.Languages) { // Load the project and fetch the files using (var project = SiteProject.FromDisk(path, language)) { // Bake the project project.Bake(mode); } } }
/// <summary> /// Builds the project. /// </summary> /// <param name="path"></param> /// <param name="mode"></param> public static void Bake(DirectoryInfo path, BakeMode mode) { // Read the configuration file at destination var config = SiteConfig.Read(path); if (config.Languages == null || config.Languages.Count == 0) { // Make sure we have a default language config.Languages = new List <string>(); config.Languages.Add("default"); } Tracing.Info("Bake", "Baking: " + path.FullName); foreach (var language in config.Languages) { // Load the project and fetch the files using (var project = SiteProject.FromDisk(path, language)) { // Bake the project project.Bake(mode); } } }
void OnGUI() { bakeMode = (BakeMode)EditorGUILayout.EnumPopup("Bake Mode", bakeMode); if (bakeMode != BakeMode.BentNormal) { EditorGUI.BeginDisabledGroup(true); } bentNormalsSpace = (NormalsSpace)EditorGUILayout.EnumPopup("Normals Space", bentNormalsSpace); if (bakeMode != BakeMode.BentNormal) { EditorGUI.EndDisabledGroup(); } if (bakeMode != BakeMode.NormalsConversion) { EditorGUI.BeginDisabledGroup(true); } normalsConversionMode = (NormalsConversionMode)EditorGUILayout.EnumPopup("Conversion Mode", normalsConversionMode); if (bakeMode != BakeMode.NormalsConversion) { EditorGUI.EndDisabledGroup(); } uvChannel = (UVChannel)EditorGUILayout.EnumPopup(new GUIContent("Texture Channel", "The UV channel to use when generating the output texture(s)."), uvChannel); bakeRes = (Resolution)EditorGUILayout.EnumPopup(new GUIContent("Output resolution", "The resolution of the output texture(s)."), bakeRes); if (bakeMode == BakeMode.NormalsConversion) { EditorGUI.BeginDisabledGroup(true); } samples = EditorGUILayout.IntSlider(new GUIContent("Sample Count", "The number of depth map samples used for each pixel."), samples, 64, 8192); shadowMapRes = (Resolution)EditorGUILayout.EnumPopup(new GUIContent("Depth Map Resolution", "The resolution of the depth map. Probably only have to increase this if baking high-resolution maps for multiple, large objects."), shadowMapRes); if (bakeMode == BakeMode.NormalsConversion) { EditorGUI.EndDisabledGroup(); } dilation = Mathf.Max(EditorGUILayout.IntField(new GUIContent("Dilation", "Adds edge padding to the output."), dilation), 0); if (bakeMode == BakeMode.NormalsConversion) { EditorGUI.BeginDisabledGroup(true); } shadowBias = EditorGUILayout.Slider(new GUIContent("Depth Bias", "Depth map sampling bias. A larger value will generally give you less artifacts at the cost of loss of accuracy."), shadowBias, 0f, 1f); if (bakeMode == BakeMode.NormalsConversion) { EditorGUI.EndDisabledGroup(); } if (bakeMode != BakeMode.AmbientOcclusion) { EditorGUI.BeginDisabledGroup(true); } aoBias = EditorGUILayout.Slider(new GUIContent("AO Bias", "Ambient Occlusion output bias. A value of 0.5 is considered neutral."), aoBias, 0f, 1f); if (bakeMode != BakeMode.AmbientOcclusion) { EditorGUI.EndDisabledGroup(); } if (bakeMode == BakeMode.NormalsConversion) { EditorGUI.BeginDisabledGroup(true); } clampToHemisphere = EditorGUILayout.Toggle(new GUIContent("Clamp To Hemisphere", "Discard samples that would intersect the pixel's own surface."), clampToHemisphere); includeScene = EditorGUILayout.Toggle(new GUIContent("Include Scene", "If checked, will include other non-selected objects in the scene when rendering the depth map."), includeScene); if (bakeMode == BakeMode.NormalsConversion) { EditorGUI.EndDisabledGroup(); } forceSharedTexture = EditorGUILayout.Toggle(new GUIContent("Force Shared Texture", "If checked, will render all selected objects into the same output texture. Useful if you are baking multiple objects which share a texture, but don't share materials. Objects with the same material will still be grouped even if this is not checked."), forceSharedTexture); if (bakeMode == BakeMode.NormalsConversion) { EditorGUI.BeginDisabledGroup(true); } if (forceSharedTexture) { EditorGUI.BeginDisabledGroup(true); } useNormalMaps = EditorGUILayout.Toggle(new GUIContent("Use Normal Maps", "If checked, the baker will include any normal maps present on the original materials when baking. This can give a higher quality result if the bake resolution is high enough."), useNormalMaps); if (forceSharedTexture) { EditorGUI.EndDisabledGroup(); } useOriginalShaders = EditorGUILayout.Toggle(new GUIContent("Use Original Shaders", "Bake the depth maps using the objects' original shaders. This is useful if you are using vertex-modifying shaders, but also prevents overriding the face cull mode."), useOriginalShaders); if (useOriginalShaders) { EditorGUI.BeginDisabledGroup(true); } cullOverrideMode = (CullOverrideMode)EditorGUILayout.EnumPopup(new GUIContent("Face Cull Override", "Force double or single-sided rendering. In most cases you probably want to force double-sided."), cullOverrideMode); if (useOriginalShaders) { EditorGUI.EndDisabledGroup(); } if (bakeMode == BakeMode.NormalsConversion) { EditorGUI.EndDisabledGroup(); } transparentPixels = EditorGUILayout.Toggle(new GUIContent("Transparent Background", "Whether to fill background pixels in the output texture with neutral values or leave them blank."), transparentPixels); GUILayout.Space(4); outputPath = EditorGUILayout.TextField("Output Folder", outputPath); nameMode = (NameMode)EditorGUILayout.EnumPopup(new GUIContent("Output Names", "How to determine the output file name. Only used if multiple objects which share materials are selected."), nameMode); var rect = GUILayoutUtility.GetLastRect(); if (Selection.gameObjects.Length < 1) { EditorGUI.BeginDisabledGroup(true); } if (GUILayout.Button(Selection.gameObjects.Length < 1 ? "Select some objects to bake!" : (Selection.gameObjects.Length == 1 ? "Bake Selected Object" : "Bake Selected Objects"))) { Bake(Selection.gameObjects); } if (Selection.gameObjects.Length < 1) { EditorGUI.EndDisabledGroup(); } }
private void AnimationTextureEditor() { animationRigContainer = (GameObject)EditorGUILayout.ObjectField("Rig Container", animationRigContainer, typeof(GameObject), true); animatorController = (RuntimeAnimatorController)EditorGUILayout.ObjectField("Animator", animatorController, typeof(RuntimeAnimatorController), false); if (animationRigContainer == null || animatorController == null) { return; } bool checkNextError = !SetError(Errors.MissingRigObject, animationTextureErrors, animationRigContainer == null); if (checkNextError) { checkNextError = !SetError(Errors.MissingSkinnedMeshRenderer, animationTextureErrors, animationRigContainer.GetComponentInChildren <SkinnedMeshRenderer>() == null); } if (checkNextError) { checkNextError = !SetError(Errors.MissingAnimator, animationTextureErrors, animationRigContainer.GetComponentInChildren <Animator>() == null); } AnimationClip[] animationClips = animatorController.animationClips; if (checkNextError) { checkNextError = !SetError(Errors.NoAnimationClips, animationTextureErrors, animationClips.Length == 0); } if (!checkNextError) { return; } animationTextureColorMode = (ColorMode)EditorGUILayout.EnumPopup("Color Mode", animationTextureColorMode); bakeMode = (BakeMode)EditorGUILayout.EnumPopup(new GUIContent("Bake Mode", "Bake mode for faster iteration"), bakeMode); framesPerSecondCapture = EditorGUILayout.IntSlider(new GUIContent("FPS Capture", "How many frames per second the clip will be captured at."), framesPerSecondCapture, 1, 120); powerOfTwoOptimization = EditorGUILayout.Toggle(new GUIContent("PoT Optimization", "Optimize textures for PoT compression"), powerOfTwoOptimization); if (powerOfTwoOptimization) { sizeOptimizationIteration = EditorGUILayout.IntSlider(new GUIContent("Size Optimization Iterations", "How many times the script try to optimize the texture size."), sizeOptimizationIteration, 0, 8); } SkinnedMeshRenderer[] meshRenderers = animationRigContainer.GetComponentsInChildren <SkinnedMeshRenderer>(); Animator[] animators = animationRigContainer.GetComponentsInChildren <Animator>(); int[] vertexCount = new int[meshRenderers.Length]; Vector2Int textureSize = Vector2Int.zero; Vector2Int minTextureSize = new Vector2Int(8192, 8192); Vector2Int[][] textureSizes = new Vector2Int[meshRenderers.Length][]; for (int i = 0; i < meshRenderers.Length; i++) { animators[i].runtimeAnimatorController = animatorController; vertexCount[i] = meshRenderers[i].sharedMesh.vertexCount; textureSizes[i] = new Vector2Int[animationClips.Length]; } float totalTime = 0; int totalFrames = 0; int squareFrames = 0; AnimationClip clipToBake = null; switch (bakeMode) { case BakeMode.Single: clipToBakeIndex = EditorGUILayout.Popup(new GUIContent("Clip to Bake", "Which animation clip will be baked."), clipToBakeIndex, GetAnimationClipNames(animationClips)); clipToBake = animationClips[clipToBakeIndex]; totalTime = clipToBake.length; for (int i = 0; i < meshRenderers.Length; i++) { totalFrames = (int)(totalTime * framesPerSecondCapture); if (powerOfTwoOptimization) { int pixelCount = (int)(totalTime * vertexCount[i]); squareFrames = Mathf.FloorToInt(Mathf.Sqrt(pixelCount)); int pot = Mathf.NextPowerOfTwo(squareFrames); Vector2Int optimizedSquareFrames = new Vector2Int(pot, pot); optimizedSquareFrames = GetSmallestPOT(optimizedSquareFrames, pixelCount, sizeOptimizationIteration); minTextureSize = Vector2Int.Min(optimizedSquareFrames, minTextureSize); textureSize = Vector2Int.Max(optimizedSquareFrames, textureSize); textureSizes[i][0] = textureSize; } else { for (int j = 0; j < animationClips.Length; j++) { textureSizes[i][0] = new Vector2Int(vertexCount[i], totalFrames); minTextureSize = Vector2Int.Min(textureSizes[i][j], minTextureSize); textureSize = Vector2Int.Max(textureSizes[i][j], textureSize); } } } break; case BakeMode.AllIndividual: foreach (AnimationClip clip in animationClips) { totalTime += clip.length; } for (int i = 0; i < meshRenderers.Length; i++) { if (powerOfTwoOptimization) { for (int j = 0; j < animationClips.Length; j++) { float clipTime = animationClips[j].length; float clipFrames = (int)(clipTime * framesPerSecondCapture); int pixelCount = (int)(clipFrames * vertexCount[i]); squareFrames = Mathf.FloorToInt(Mathf.Sqrt(pixelCount)); int pot = Mathf.NextPowerOfTwo(squareFrames); Vector2Int optimizedSquareFrames = new Vector2Int(pot, pot); optimizedSquareFrames = GetSmallestPOT(optimizedSquareFrames, pixelCount, sizeOptimizationIteration); minTextureSize = Vector2Int.Min(optimizedSquareFrames, minTextureSize); textureSize = Vector2Int.Max(optimizedSquareFrames, textureSize); textureSizes[i][j] = optimizedSquareFrames; } } else { for (int j = 0; j < animationClips.Length; j++) { float clipTime = animationClips[j].length; int clipFrames = (int)(clipTime * framesPerSecondCapture); textureSizes[i][j] = new Vector2Int(vertexCount[i], clipFrames); minTextureSize = Vector2Int.Min(textureSizes[i][j], minTextureSize); textureSize = Vector2Int.Max(textureSizes[i][j], textureSize); } } } totalFrames = (int)(totalTime * framesPerSecondCapture); break; } EditorGUILayout.Space(EditorGUIUtility.singleLineHeight); EditorGUILayout.LabelField($"Animations: {(bakeMode != BakeMode.Single ? animationClips.Length : 1)}"); EditorGUILayout.LabelField($"Frames to bake: {totalFrames}"); switch (bakeMode) { case BakeMode.Single: EditorGUILayout.LabelField($"Result texture size: {textureSize}"); break; case BakeMode.AllIndividual: EditorGUILayout.LabelField($"Result texture size: {minTextureSize} (min), {textureSize} (max)"); break; } EditorGUILayout.LabelField($"Estimated bake time: {totalTime / (60 / framesPerSecondCapture)} seconds"); switch (bakeMode) { case BakeMode.Single: if (GUILayout.Button("Bake Animation (Single)")) { for (int i = 0; i < meshRenderers.Length; i++) { EditorCoroutineUtility.StartCoroutine(CreateSingleAnimationTextureForRig(textureSizes[i][0], vertexCount[i], totalFrames, framesPerSecondCapture, clipToBake, animationRigContainer.transform.GetChild(i).gameObject), this); } } break; case BakeMode.AllIndividual: if (GUILayout.Button("Bake Animations (All)")) { for (int i = 0; i < meshRenderers.Length; i++) { EditorCoroutineUtility.StartCoroutine(CreateAllAnimationTexturesForRig(textureSizes[i], vertexCount[i], framesPerSecondCapture, animationClips, animationRigContainer.transform.GetChild(i).gameObject), this); } } break; } }
private void AnimationTextureEditor() { animationRigObject = (GameObject)EditorGUILayout.ObjectField("Animation Rig", animationRigObject, typeof(GameObject), true); if (animationRigObject == null) { return; } bool checkNextError = !SetError(Errors.MissingRigObject, animationTextureErrors, animationRigObject == null); if (checkNextError) { checkNextError = !SetError(Errors.MissingSkinnedMeshRenderer, animationTextureErrors, animationRigObject.GetComponentInChildren <SkinnedMeshRenderer>() == null); } if (checkNextError) { checkNextError = !SetError(Errors.MissingAnimator, animationTextureErrors, animationRigObject.GetComponentInChildren <Animator>() == null); } if (checkNextError) { checkNextError = !SetError(Errors.MissingRuntimeAnimatorController, animationTextureErrors, animationRigObject.GetComponentInChildren <Animator>().runtimeAnimatorController == null); } AnimationClip[] animationClips = animationRigObject?.GetComponentInChildren <Animator>()?.runtimeAnimatorController?.animationClips; if (checkNextError) { checkNextError = !SetError(Errors.NoAnimationClips, animationTextureErrors, animationClips.Length == 0); } if (!checkNextError) { return; } animationTextureColorMode = (ColorMode)EditorGUILayout.EnumPopup("Color Mode", animationTextureColorMode); bakeMode = (BakeMode)EditorGUILayout.EnumPopup(new GUIContent("Bake Mode", "Bake mode for faster iteration"), bakeMode); framesPerSecondCapture = EditorGUILayout.IntSlider(new GUIContent("FPS Capture", "How many frames per second the clip will be captured at."), framesPerSecondCapture, 1, 120); bakeRotation = EditorGUILayout.Toggle(new GUIContent("Bake Rotation", "Apply the game object's rotation to the baked texture"), bakeRotation); animationTextureScaler = EditorGUILayout.FloatField(new GUIContent("Bake Scale", "Scale the mesh before baking to reduce the chance of baked pixels being out of range."), animationTextureScaler); int vertexCount = animationRigObject.GetComponentInChildren <SkinnedMeshRenderer>().sharedMesh.vertexCount; float totalTime = 0; int totalFrames; int squareFrames; Vector2Int textureSize = Vector2Int.zero; Vector2Int minTextureSize = new Vector2Int(8192, 8192); Vector2Int[] textureSizes = new Vector2Int[animationClips.Length]; AnimationClip clipToBake = null; int highestPOT = 0; switch (bakeMode) { case BakeMode.Single: clipToBakeIndex = EditorGUILayout.Popup(new GUIContent("Clip to Bake", "Which animation clip will be baked."), clipToBakeIndex, GetAnimationClipNames(animationClips)); clipToBake = animationClips[clipToBakeIndex]; totalTime = clipToBake.length; minFrame = EditorGUILayout.IntSlider(new GUIContent("Min Capture Frame", "The frame to start capturing at."), minFrame, 0, maxFrame); maxFrame = EditorGUILayout.IntSlider(new GUIContent("Max Capture Frame", "The frame to end capturing at."), maxFrame, minFrame, (int)(totalTime * framesPerSecondCapture)); totalFrames = maxFrame - minFrame; int pixelCountSingle = totalFrames * vertexCount; squareFrames = Mathf.FloorToInt(Mathf.Sqrt(pixelCountSingle)); int potSingle = Mathf.NextPowerOfTwo(squareFrames); Vector2Int optimizedSquareFramesSingle = new Vector2Int(potSingle, potSingle); if ((potSingle * potSingle) / 2 > pixelCountSingle) { optimizedSquareFramesSingle.x /= 2; } textureSize = optimizedSquareFramesSingle; break; default: for (int i = 0; i < animationClips.Length; i++) { float clipTime = animationClips[i].length; float clipFrames = (int)(clipTime * framesPerSecondCapture); totalTime += clipTime; int pixelCount = (int)(clipFrames * vertexCount); squareFrames = Mathf.FloorToInt(Mathf.Sqrt(pixelCount)); int pot = Mathf.NextPowerOfTwo(squareFrames); Vector2Int optimizedSquareFrames = new Vector2Int(pot, pot); highestPOT = Mathf.Max(pot, highestPOT); if ((pot * pot) / 2 > pixelCount) { optimizedSquareFrames.x /= 2; } minTextureSize = Vector2Int.Min(optimizedSquareFrames, minTextureSize); textureSize = Vector2Int.Max(optimizedSquareFrames, textureSize); textureSizes[i] = optimizedSquareFrames; } totalFrames = (int)(totalTime * framesPerSecondCapture); break; } EditorGUILayout.Space(EditorGUIUtility.singleLineHeight); EditorGUILayout.LabelField($"Animations: {(bakeMode != BakeMode.Single ? animationClips.Length : 1)}"); EditorGUILayout.LabelField($"Frames to bake: {totalFrames}"); EditorGUILayout.LabelField($"Pixels to fill: {vertexCount * totalFrames}"); switch (bakeMode) { case BakeMode.Single: EditorGUILayout.LabelField($"Result texture size: {textureSize}"); break; case BakeMode.AllIndividual: EditorGUILayout.LabelField($"Result texture size: {minTextureSize} (min), {textureSize} (max)"); break; case BakeMode.AllTexture2DArray: EditorGUILayout.LabelField($"Result texture size: {textureSize}x{animationClips.Length}"); break; } EditorGUILayout.LabelField($"Estimated bake time: {totalTime / (60 / framesPerSecondCapture)} seconds"); Quaternion rotation = bakeRotation ? Quaternion.Inverse(animationRigObject.transform.GetChild(0).localRotation) : Quaternion.identity; switch (bakeMode) { case BakeMode.Single: if (GUILayout.Button("Bake Animation (Single)")) { EditorCoroutineUtility.StartCoroutine(CreateAnimationTexture(textureSize, vertexCount, totalFrames, framesPerSecondCapture, rotation, clipToBake), this); } break; case BakeMode.AllIndividual: if (GUILayout.Button("Bake Animations (Individual)")) { EditorCoroutineUtility.StartCoroutine(CreateAnimationTextureIndividual(textureSizes, vertexCount, framesPerSecondCapture, rotation, animationClips), this); } break; case BakeMode.AllTexture2DArray: if (GUILayout.Button("Bake Animations (Texture2DArray)")) { EditorCoroutineUtility.StartCoroutine(CreateAnimationTextureArray(highestPOT, vertexCount, framesPerSecondCapture, rotation, animationClips), this); } break; } }
/// <summary> /// The main pipeline for project processing. /// </summary> public void Bake(BakeMode mode) { try { Tracing.Info("Bake", String.Format("Building the website ({0}) ...", this.Language)); // If we're baking in Optimized mode, make sure the destination and all subdirectories and files are removed first. if (mode == BakeMode.Optimized) { DirectoryInfo output = new DirectoryInfo( Path.Combine(this.Directory.FullName, this.Configuration.Destination) ); if (output.Exists) { output.Delete(true); } } // Fetch all the files from the source directory. IEnumerable<IAssetFile> files = this.Assets.Fetch(this); // Exclude folders and files listed in the _config.yaml excludes array. // Note: These excluded folders and files are only excluded from processing not from being copied. string[] excludes = new String[] { }; List<string> exclude = this.Configuration.Exclude; if (exclude != null && exclude.Count > 0) { excludes = exclude.ToArray(); } IEnumerable<IAssetFile> filesExceptExcludes = files.Except(excludes); // Load all templates TranslationProcessor.Default .Next(RazorProcessor.Default) .On(filesExceptExcludes.Only("*.cshtml")) .Export(); // Handle markup processing TranslationProcessor.Default .Next(HeaderProcessor.Default) .Next(MarkdownProcessor.Default) .Next(LayoutProcessor.Default) .Next(HtmlMinifier.Default) .On(filesExceptExcludes.Only("*.md")) .Export(); // If we're NOT baking in Fast mode, optimize the files. if (mode != BakeMode.Fast) { StyleProcessor.Default .Next(FileOptimizer.Default) .On(filesExceptExcludes.Except(DefaultExclude)) .Export(); } // Copy files TranslationProcessor.Default .Next((IProcessor<IAssetFile, IAssetFile>)FileCopier.Default) .On(files.Except(DefaultExclude)) .Export(); } catch (Exception ex) { // Catch all errors that occured during bake. Tracing.Error("Bake", ex); } }
/// <summary> /// The main pipeline for project processing. /// </summary> public void Bake(BakeMode mode) { try { Tracing.Info("Bake", String.Format("Building the website ({0}) ...", this.Language)); // If we're baking in Optimized mode, make sure the destination and all subdirectories and files are removed first. if (mode == BakeMode.Optimized) { DirectoryInfo output = new DirectoryInfo( Path.Combine(this.Directory.FullName, this.Configuration.Destination) ); if (output.Exists) { output.Delete(true); } } // Fetch all the files from the source directory. IEnumerable <IAssetFile> files = this.Assets.Fetch(this); // Exclude folders and files listed in the _config.yaml excludes array. // Note: These excluded folders and files are only excluded from processing not from being copied. string[] excludes = new String[] { }; List <string> exclude = this.Configuration.Exclude; if (exclude != null && exclude.Count > 0) { excludes = exclude.ToArray(); } IEnumerable <IAssetFile> filesExceptExcludes = files.Except(excludes); // Load all templates TranslationProcessor.Default .Next(RazorProcessor.Default) .On(filesExceptExcludes.Only("*.cshtml")) .Export(); // Handle markup processing TranslationProcessor.Default .Next(HeaderProcessor.Default) .Next(MarkdownProcessor.Default) .Next(LayoutProcessor.Default) .Next(HtmlMinifier.Default) .On(filesExceptExcludes.Only("*.md")) .Export(); // If we're NOT baking in Fast mode, optimize the files. if (mode != BakeMode.Fast) { StyleProcessor.Default .Next(FileOptimizer.Default) .On(filesExceptExcludes.Except(DefaultExclude)) .Export(); } // Copy files TranslationProcessor.Default .Next((IProcessor <IAssetFile, IAssetFile>)FileCopier.Default) .On(files.Except(DefaultExclude)) .Export(); } catch (Exception ex) { // Catch all errors that occured during bake. Tracing.Error("Bake", ex); } }