diff --git a/Editor/ExportContext.cs b/Editor/ExportContext.cs index 1264fa1..9b2cf4d 100644 --- a/Editor/ExportContext.cs +++ b/Editor/ExportContext.cs @@ -1,3 +1,4 @@ +using UnityEngine; using UnityEngine.SceneManagement; namespace UnityToRebelFork.Editor @@ -6,7 +7,10 @@ public class ExportContext { public ExportContext(UnityEngine.SceneManagement.Scene scene) { + } + public ExportContext(GameObject prefabRoot) + { } public string TempFolder { get; set; } = "Tmp/"; diff --git a/Editor/ExportSettingsEditor.cs b/Editor/ExportSettingsEditor.cs index c399c8d..f076008 100644 --- a/Editor/ExportSettingsEditor.cs +++ b/Editor/ExportSettingsEditor.cs @@ -7,6 +7,7 @@ using UnityEditor; using UnityEngine.Rendering; using UnityEngine.SceneManagement; +using UnityEditor.SceneManagement; namespace UnityToRebelFork.Editor { @@ -51,13 +52,29 @@ public override void OnInspectorGUI() //} //else { - if (GUILayout.Button("Export current scene", GUILayout.Height(40))) + var prefabStage = PrefabStageUtility.GetCurrentPrefabStage(); + + if (prefabStage != null) { - _container = new DiContainer(new[] { StaticContext.Container }); - var activeScene = SceneManager.GetActiveScene(); - RebelForkInstaller.Install(_container, script, new ExportContext(activeScene)); - var orchestrator = _container.Resolve(); - orchestrator.ScheduleExport(activeScene); + if (GUILayout.Button($"Export Prefab ({Path.GetFileName(prefabStage.prefabAssetPath)})", GUILayout.Height(40))) + { + _container = new DiContainer(new[] { StaticContext.Container }); + var prefabRootObject = AssetDatabase.LoadAssetAtPath(prefabStage.assetPath); + RebelForkInstaller.Install(_container, script, new ExportContext(prefabRootObject)); + var orchestrator = _container.Resolve(); + orchestrator.ScheduleExport(prefabRootObject); + } + } + else + { + if (GUILayout.Button($"Export Scene ({Path.GetFileName(SceneManager.GetActiveScene().path)})", GUILayout.Height(40))) + { + _container = new DiContainer(new[] { StaticContext.Container }); + var activeScene = SceneManager.GetActiveScene(); + RebelForkInstaller.Install(_container, script, new ExportContext(activeScene)); + var orchestrator = _container.Resolve(); + orchestrator.ScheduleExport(activeScene); + } } } diff --git a/Editor/Helpers.cs b/Editor/Helpers.cs index 7a8a213..c62bcba 100644 --- a/Editor/Helpers.cs +++ b/Editor/Helpers.cs @@ -1,3 +1,5 @@ +using UnityEditor; + namespace UnityToRebelFork.Editor { public class Helpers @@ -15,9 +17,11 @@ public static Scene CreateDefaultScene(string name) scene.Name = name; scene.Components.Add(new RenderPipeline()); - scene.Components.Add(new Octree()); + var octree = new Octree(); + scene.Components.Add(octree); //scene.Components.Add(new ReflectionProbeManager()); scene.Components.Add(new PhysicsWorld()); + scene.Components.Add(new Zone() { ZoneTexture = ResourceRef.Create("Textures/DefaultSkybox.xml"), BoundingBoxMin = octree.BoundingBoxMin, BoundingBoxMax = octree.BoundingBoxMax }); scene.Components.Add(new Skybox() { Model = new ResourceRef("Model", "Models/Box.mdl"), @@ -25,5 +29,66 @@ public static Scene CreateDefaultScene(string name) }); return scene; } + + /// + /// Get stable hash code. + /// + /// Object type. + /// Object. + /// Stable hash code. + public static int GetHashCode(T1 t1) + { + if (t1 == null) return 0; + if (t1 is UnityEngine.Object obj) + { + if (AssetDatabase.TryGetGUIDAndLocalFileIdentifier(obj, out var guid, out long localId)) + { + return CombineHashCodes(guid, localId); + } + } + return t1.GetHashCode(); + } + + /// + /// Combine hash codes with stable algorithm. + /// + /// First object type. + /// Second object type. + /// First object. + /// Second object. + /// Combined stable hash code. + public static int CombineHashCodes(T1 t1, T2 t2) + { + var code = GetHashCode(t1); + code = code * 31 + GetHashCode(t2); + return code; + } + + /// + /// Combine hash codes with stable algorithm. + /// + /// Second object type. + /// Existing hashcode. + /// Second object. + /// Combined stable hash code. + public static int CombineHashCodes(int code, T2 t2) + { + code = code * 31 + GetHashCode(t2); + return code; + } + + /// + /// Combine hash codes with stable algorithm. + /// + /// Combined stable hash code. + public static int CombineHashCodes(params object[] objects) + { + int code = 0; + foreach (var o in objects) + { + code = CombineHashCodes(code, o); + } + return code; + } } } \ No newline at end of file diff --git a/Editor/Material/StandardShaderMapping.cs b/Editor/Material/StandardShaderMapping.cs index 343bc47..7723962 100644 --- a/Editor/Material/StandardShaderMapping.cs +++ b/Editor/Material/StandardShaderMapping.cs @@ -45,6 +45,41 @@ public MaterialModel Map(UnityEngine.Material material) if (shaderArgs._OcclusionMap != null || shaderArgs._MetallicGlossMap != null) { + var recipe = new TextureRecipe(); + // R - Roughness ------------ + if (shaderArgs._SmoothnessTextureChannel < 0.5) + { + if (shaderArgs._MetallicGlossMap != null) + { + recipe.RChannel = shaderArgs._MetallicGlossMap; + recipe.RMask = new Vector4(0.0f, 0.0f, 0.0f, 1.0f); + recipe.RSourceRange = new Vector2(1.0f, 0.0f); + } + } + else + { + if (shaderArgs._MainTex != null) + { + recipe.RChannel = shaderArgs._MainTex; + recipe.RMask = new Vector4(0.0f, 0.0f, 0.0f, 1.0f); + recipe.RSourceRange = new Vector2(1.0f, 0.0f); + } + } + // G - Metallic ------------ + if (shaderArgs._MetallicGlossMap != null) + { + recipe.GChannel = shaderArgs._MetallicGlossMap; + recipe.GMask = new Vector4(0.299f, 0.587f, 0.114f, 0.0f); + } + + // A - Occlusion ------------ + if (shaderArgs._OcclusionMap != null) + { + recipe.AChannel = shaderArgs._OcclusionMap; + recipe.AMask = new Vector4(0.299f, 0.587f, 0.114f, 0.0f); + } + + model.Properties = orchestrator.ScheduleExport(recipe); } return model; diff --git a/Editor/MeshReference.cs b/Editor/MeshReference.cs index 6556efc..5c3d3ce 100644 --- a/Editor/MeshReference.cs +++ b/Editor/MeshReference.cs @@ -19,36 +19,6 @@ public struct MaterialAndTopology public IList Levels { get; } = new List(); - public String HashCodeTest - { - get - { - var s = new StringBuilder(); - foreach (var level in Levels) - { - s.AppendLine($"ScreenRelativeTransitionHeight = {level.ScreenRelativeTransitionHeight.GetHashCode()}"); - foreach (var renderer in level.Renderers) - { - AssetDatabase.TryGetGUIDAndLocalFileIdentifier(renderer.Mesh, out string guid, out long localId); - s.AppendLine($"Mesh {guid.GetHashCode()}, {localId.GetHashCode()} = {GetMeshHashCode(renderer.Mesh)}"); - s.AppendLine($"Transform = {renderer.Transform.GetHashCode()}"); - foreach (var rendererMaterial in renderer.Materials) - { - s.AppendLine($"{rendererMaterial} = {rendererMaterial.GetHashCode()}"); - } - } - } - return s.ToString(); - } - } - - static int CombineHashCodes(T1 t1, T2 t2) - { - var code = t1.GetHashCode(); - code = code * 31 + t2.GetHashCode(); - return code; - } - public MeshReference(LODGroup lodGroup, ExportOrchestrator orchestrator) { var worldToLocalMatrix = lodGroup.transform.worldToLocalMatrix; @@ -118,7 +88,7 @@ public override int GetHashCode() var hashCode = 0; foreach (var lodLevel in Levels) { - hashCode = CombineHashCodes(hashCode, lodLevel); + hashCode = Helpers.CombineHashCodes(hashCode, lodLevel); } return hashCode; } @@ -179,10 +149,10 @@ public override bool Equals(object obj) public override int GetHashCode() { int meshHashCode = GetMeshHashCode(Mesh); - var hash = CombineHashCodes(Transform, meshHashCode); + var hash = Helpers.CombineHashCodes(Transform, meshHashCode); foreach (var material in Materials) { - hash = CombineHashCodes(hash, material != null ? material.GetHashCode() : 0); + hash = Helpers.CombineHashCodes(hash, material != null ? material.GetHashCode() : 0); } return hash; } @@ -209,7 +179,7 @@ public static int GetMeshHashCode(UnityEngine.Mesh mesh) { if (AssetDatabase.TryGetGUIDAndLocalFileIdentifier(mesh, out string guid, out long localId)) { - return CombineHashCodes(guid, localId); + return Helpers.CombineHashCodes(guid, localId); } } @@ -257,7 +227,7 @@ public override bool Equals(object obj) public override int GetHashCode() { var hash = ScreenRelativeTransitionHeight.GetHashCode(); - foreach (var renderer in Renderers) hash = CombineHashCodes(hash, renderer); + foreach (var renderer in Renderers) hash = Helpers.CombineHashCodes(hash, renderer); return hash; } diff --git a/Editor/Model/ExtensionMethods.cs b/Editor/Model/ExtensionMethods.cs index 8863643..d193d96 100644 --- a/Editor/Model/ExtensionMethods.cs +++ b/Editor/Model/ExtensionMethods.cs @@ -58,6 +58,7 @@ public static string GetName(this VariantType valueType) case VariantType.VarInt64: return "Int64"; case VariantType.VarCustom: return "Custom"; case VariantType.VarStringvariantmap: return "StringVariantMap"; + case VariantType.VarVariantList: return "VariantList"; //case VariantType.VaVe: return "VariantVector"; //case VariantType.VarM: return "VariantMap"; //case VariantType.VariantCurve: return "VariantCurve"; diff --git a/Editor/Model/Variant.cs b/Editor/Model/Variant.cs index 3283369..1ab92d7 100644 --- a/Editor/Model/Variant.cs +++ b/Editor/Model/Variant.cs @@ -34,6 +34,8 @@ public bool Equals(Variant other) case VariantType.VarStringList: return this.Compare(this.StringList, other.StringList); case VariantType.VarStringvariantmap: return this.Compare(this.Stringvariantmap, other.Stringvariantmap); case VariantType.VarColor: return this.Color == other.Color; + case VariantType.VarVariantList: return this.VariantList == other.VariantList; + case VariantType.VarBuffer: return true; // Buffer is not supported. default: throw new NotImplementedException(_type.GetName()); } } diff --git a/Editor/Model/VariantList.cs b/Editor/Model/VariantList.cs index 637876d..ac4b916 100644 --- a/Editor/Model/VariantList.cs +++ b/Editor/Model/VariantList.cs @@ -1,9 +1,57 @@ +using System; using System.Collections.Generic; namespace UnityToRebelFork.Editor { - public class VariantList : List + public class VariantList : List, IEquatable { + public bool Equals(VariantList other) + { + if (other == null) + { + return this.Count == 0; + } + if (other.Count != this.Count) + { + return false; + } + + for (var index = 0; index < this.Count; index++) + { + if (this[index] != other[index]) + return false; + } + + return true; + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj.GetType() != this.GetType()) return false; + return Equals((VariantList)obj); + } + + public override int GetHashCode() + { + var code = new HashCode(); + foreach (var val in this) + { + code.Add(val); + } + return code.ToHashCode(); + } + + public static bool operator ==(VariantList left, VariantList right) + { + return Equals(left, right); + } + + public static bool operator !=(VariantList left, VariantList right) + { + return !Equals(left, right); + } } } \ No newline at end of file diff --git a/Editor/PrefabExporter.cs b/Editor/PrefabExporter.cs index 564c6e4..b1cc92d 100644 --- a/Editor/PrefabExporter.cs +++ b/Editor/PrefabExporter.cs @@ -39,6 +39,17 @@ public override IEnumerable Export(GameObject asset) scene.Children.Add(root); if (!Settings.EmptyNodes) new EmptyNodeCleanupVisitor().Visit(root); + scene.Children.Add(new Node() + { + Name = "Prefab Light", + Rotation = Quaternion.AngleAxis(90, Vector3.right), + Components = { + new Light() + { + LightType = LightType.Directional + } + } + }); new UniqueIdVisitor().Vist(scene); using (var stream = _settings.CreateFile(EvaluateResourcePath(asset))) diff --git a/Editor/RebelForkInstaller.cs b/Editor/RebelForkInstaller.cs index 329049f..c21f4d3 100644 --- a/Editor/RebelForkInstaller.cs +++ b/Editor/RebelForkInstaller.cs @@ -19,6 +19,7 @@ public override void InstallBindings() Container.BindInterfacesAndSelfTo().AsSingle(); Container.BindInterfacesAndSelfTo().AsSingle(); Container.BindInterfacesAndSelfTo().AsSingle(); + Container.BindInterfacesAndSelfTo().AsSingle(); Container.BindInterfacesAndSelfTo().AsSingle(); Container.BindInterfacesAndSelfTo().AsSingle(); diff --git a/Editor/TextureProcessor.cs b/Editor/TextureProcessor.cs index 2c68fb4..d52d9ec 100644 --- a/Editor/TextureProcessor.cs +++ b/Editor/TextureProcessor.cs @@ -170,7 +170,7 @@ public void ProcessAndSaveTexture(UnityEngine.Texture sourceTexture, int width, texture => SaveTexture(texture, fullOutputPath, hasAlpha)); } - private void SaveTexture(UnityEngine.Texture2D texture, string fullOutputPath, bool hasAlpha = true) + public void SaveTexture(UnityEngine.Texture2D texture, string fullOutputPath, bool hasAlpha = true) { if (string.IsNullOrWhiteSpace(fullOutputPath)) return; diff --git a/Editor/TextureRecipe.cs b/Editor/TextureRecipe.cs new file mode 100644 index 0000000..da35431 --- /dev/null +++ b/Editor/TextureRecipe.cs @@ -0,0 +1,77 @@ +using System; +using UnityEngine; + +namespace UnityToRebelFork.Editor +{ + public class TextureRecipe : IEquatable + { + public static readonly TextureRecipe Default = new TextureRecipe(); + + public bool Equals(TextureRecipe other) + { + if (ReferenceEquals(null, other)) return false; + if (ReferenceEquals(this, other)) return true; + return Equals(RChannel, other.RChannel) && RSourceRange.Equals(other.RSourceRange) && RDestinationRange.Equals(other.RDestinationRange) && RMask.Equals(other.RMask) && Equals(GChannel, other.GChannel) && GSourceRange.Equals(other.GSourceRange) && GDestinationRange.Equals(other.GDestinationRange) && GMask.Equals(other.GMask) && Equals(BChannel, other.BChannel) && BSourceRange.Equals(other.BSourceRange) && BDestinationRange.Equals(other.BDestinationRange) && BMask.Equals(other.BMask) && Equals(AChannel, other.AChannel) && ASourceRange.Equals(other.ASourceRange) && ADestinationRange.Equals(other.ADestinationRange) && AMask.Equals(other.AMask); + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj.GetType() != this.GetType()) return false; + return Equals((TextureRecipe)obj); + } + + public override int GetHashCode() + { + return Helpers.CombineHashCodes( + RChannel, + RSourceRange, + RDestinationRange, + RMask, + GChannel, + GSourceRange, + GDestinationRange, + GMask, + BChannel, + BSourceRange, + BDestinationRange, + BMask, + AChannel, + ASourceRange, + ADestinationRange, + AMask); + } + + public static bool operator ==(TextureRecipe left, TextureRecipe right) + { + return Equals(left, right); + } + + public static bool operator !=(TextureRecipe left, TextureRecipe right) + { + return !Equals(left, right); + } + + public UnityEngine.Texture RChannel; + public Vector2 RSourceRange = new Vector2(0, 1); + public Vector2 RDestinationRange = new Vector2(0, 1); + public Vector4 RMask = new Vector4(1, 0, 0, 0); + + public UnityEngine.Texture GChannel; + public Vector2 GSourceRange = new Vector2(0, 1); + public Vector2 GDestinationRange = new Vector2(0, 1); + public Vector4 GMask = new Vector4(0, 1, 0, 0); + + public UnityEngine.Texture BChannel; + public Vector2 BSourceRange = new Vector2(0, 1); + public Vector2 BDestinationRange = new Vector2(0, 1); + public Vector4 BMask = new Vector4(0, 0, 1, 0); + + public UnityEngine.Texture AChannel; + public Vector2 ASourceRange = new Vector2(0, 1); + public Vector2 ADestinationRange = new Vector2(0, 1); + public Vector4 AMask = new Vector4(0, 0, 0, 1); + + } +} \ No newline at end of file diff --git a/Editor/TextureRecipe.cs.meta b/Editor/TextureRecipe.cs.meta new file mode 100644 index 0000000..1b76ca0 --- /dev/null +++ b/Editor/TextureRecipe.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: fdbc67688fee99b4d91fb8bc1f34fbdf +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/TextureRecipeExporter.cs b/Editor/TextureRecipeExporter.cs new file mode 100644 index 0000000..0858b3b --- /dev/null +++ b/Editor/TextureRecipeExporter.cs @@ -0,0 +1,80 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using UnityEditor; +using UnityEngine; + +namespace UnityToRebelFork.Editor +{ + public class TextureRecipeExporter : ExporterBase + { + public TextureRecipeExporter() + { + } + + public override string EvaluateResourcePath(TextureRecipe asset) + { + return EvaluateResourcePath(asset.RChannel ?? asset.GChannel ?? asset.BChannel ?? asset.AChannel, "." + asset.GetHashCode() + ".dds"); + } + + public override IEnumerable Export(TextureRecipe asset) + { + var resourcePath = EvaluateResourcePath(asset); + + yield return Path.GetFileName(resourcePath); + + UnityEngine.Material material = null; + + var shader = UnityEngine.Shader.Find("Hidden/RebelFork/BuildTexture"); + try + { + material = new UnityEngine.Material(shader); + if (asset.RChannel != null) + { + material.SetTexture("_RChannelMap", asset.RChannel); + material.SetVector("_RChannelRanges", new Vector4(asset.RSourceRange.x, asset.RSourceRange.y, asset.RDestinationRange.x, asset.RDestinationRange.y)); + material.SetVector("_RChannelMask", asset.RMask); + } + if (asset.GChannel != null) + { + material.SetTexture("_GChannelMap", asset.GChannel); + material.SetVector("_GChannelRanges", new Vector4(asset.GSourceRange.x, asset.GSourceRange.y, asset.GDestinationRange.x, asset.GDestinationRange.y)); + material.SetVector("_GChannelMask", asset.GMask); + } + if (asset.BChannel != null) + { + material.SetTexture("_BChannelMap", asset.BChannel); + material.SetVector("_BChannelRanges", new Vector4(asset.BSourceRange.x, asset.BSourceRange.y, asset.BDestinationRange.x, asset.BDestinationRange.y)); + material.SetVector("_BChannelMask", asset.BMask); + } + if (asset.AChannel != null) + { + material.SetTexture("_AChannelMap", asset.AChannel); + material.SetVector("_AChannelRanges", new Vector4(asset.ASourceRange.x, asset.ASourceRange.y, asset.ADestinationRange.x, asset.ADestinationRange.y)); + material.SetVector("_AChannelMask", asset.AMask); + } + + int width = 1; + int height = 1; + UnityEngine.Texture sourceTexture = null; + foreach (var texture in new []{asset.RChannel, asset.GChannel, asset.BChannel, asset.AChannel}.Where(_=>_!=null)) + { + width = Math.Max(width, texture.width); + height = Math.Max(height, texture.width); + if (sourceTexture == null) + sourceTexture = texture; + } + + var textureProcessor = new TextureProcessor(); + textureProcessor.ProcessTexture(sourceTexture, width, height, material, + texture => textureProcessor.SaveTexture(texture, _settings.ResolveResourceFileName(resourcePath), asset.AChannel != null)); + } + finally + { + UnityEngine.Object.DestroyImmediate(material); + } + } + } +} \ No newline at end of file diff --git a/Editor/TextureRecipeExporter.cs.meta b/Editor/TextureRecipeExporter.cs.meta new file mode 100644 index 0000000..9381c32 --- /dev/null +++ b/Editor/TextureRecipeExporter.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 3b90131684c763e4c933efce3ff72970 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: