using System.Collections.Generic; using GLTF.Schema; using Needle.Engine.Shaders; using Needle.Engine.Utils; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using UnityEngine; using UnityEngine.Experimental.Rendering; using UnityGLTF; namespace Needle.Engine.Gltf.UnityGltf { public class UnityGltfBridge : IGltfBridge { private readonly GLTFSceneExporter exporter; private readonly Dictionary exportedMeshesMap = new Dictionary(); public UnityGltfBridge(GLTFSceneExporter exporter) { this.exporter = exporter; } public int TryGetNodeId(Transform t) { return exporter.GetTransformIndex(t); } public int TryGetMaterialId(Material mat) { return exporter.GetMaterialIndex(mat); } public int TryGetMeshId(Mesh m) { if (exportedMeshesMap.TryGetValue(m, out var id)) return id.Id; return -1; } // for cubemaps private readonly Dictionary textureMap = new Dictionary(); public int TryGetTextureId(Texture tex) { var id = exporter.GetTextureId(exporter.GetRoot(), tex); if (id == null) { if (textureMap.TryGetValue(tex, out var tid)) return tid.Id; return -1; } return id.Id; } public int TryGetAnimationId(AnimationClip clip, Transform transform) { return exporter.GetAnimationId(clip, transform); } public bool AddTextureExtension(int textureId, string name, T extension) { if (extension is IExtension ext) { AddExtensionUsed(name); var texture = exporter.GetRoot().Textures[textureId]; texture.AddExtension(name, ext); return true; } return false; } public bool AddNodeExtension(int nodeId, string name, object extension) { if (EnsureExtension(name, ref extension) && extension is IExtension ext) { AddExtensionUsed(name); var node = exporter.GetRoot().Nodes[nodeId]; node.AddExtension(name, ext); return true; } return false; } public bool AddMaterialExtension(int materialId, string name, object extension) { if (EnsureExtension(name, ref extension) && extension is IExtension ext) { AddExtensionUsed(name); var mat = this.exporter.GetRoot().Materials[materialId]; mat.AddExtension(name, ext); return true; } return false; } public void AddExtension(string name, object extension) { if (EnsureExtension(name, ref extension) && extension is IExtension ext) { AddExtensionUsed(name); this.exporter.GetRoot().AddExtension(name, ext); } } private bool EnsureExtension(string name, ref object obj) { if (obj is IExtension) { return true; } var json = JsonConvert.SerializeObject(obj); var op = new UnityGltfOpaqueExtension(name, JObject.Parse(json)); obj = op; return true; } public void AddMaterial(Material material) { exporter.ExportMaterial(material); } public int AddMesh(Mesh mesh) { if (exportedMeshesMap.TryGetValue(mesh, out var id)) return id.Id; id = exporter.ExportMesh(mesh); if (id != null) { exportedMeshesMap.Add(mesh, id); return id.Id; } Debug.LogWarning("Exporting mesh failed", mesh); return -1; } public int AddTexture(Texture texture) { // we only do this when using reflection probes // because we can not export a cubemap right now with UnityGLTF var tex = texture; var didChange = false; if (texture is Cubemap cube) { TextureUtils.ValidateCubemapSettings(cube, TextureUtils.CubemapUsage.Unknown); var size = texture.width; using var cubemapExporter = new CubemapExporter(size, OutputFormat.EXR); var res = cubemapExporter.ConvertCubemapToEquirectTexture(cube, false); if (res) { res.name = cube.name; texture = res; didChange = true; } } // check if the texture is linear or not var isLinear = !GraphicsFormatUtility.IsSRGBFormat(texture.graphicsFormat); // HDR textures can be added as EXR now var textureMapType = texture.IsHDR() ? GLTFSceneExporter.TextureMapType.Custom_HDR : isLinear ? GLTFSceneExporter.TextureMapType.Custom_Unknown : GLTFSceneExporter.TextureMapType.sRGB; var tid = exporter.ExportTexture(texture, textureMapType); // we only need to cache this for reflection probes/cubemap textures if (didChange && !textureMap.ContainsKey(tex)) textureMap.Add(tex, tid); return tid.Id; } public int AddAnimationClip(AnimationClip clip, Transform transform, float speed) { #if UNITY_EDITOR // FIXME: the name is just a hack until we have a proper timeline extension var anim = exporter.ExportAnimationClip(clip, clip.name, transform, speed); return exporter.GetRoot().Animations.IndexOf(anim); #else return -1; #endif } private void AddExtensionUsed(string name) { var root = this.exporter.GetRoot(); if (root.ExtensionsUsed == null) root.ExtensionsUsed = new List(); if (!root.ExtensionsUsed.Contains(name)) root.ExtensionsUsed.Add(name); } } }