// SPDX-FileCopyrightText: 2023 Unity Technologies and the glTFast authors // SPDX-License-Identifier: Apache-2.0 #if UNITY_ENTITIES_GRAPHICS || UNITY_DOTS_HYBRID using System.IO; using System.Threading.Tasks; using GLTFast.Loading; using GLTFast.Logging; using GLTFast.Materials; using Unity.Burst; using Unity.Entities; using Unity.Transforms; using UnityEngine; using Unity.Collections; #if UNITY_ENTITIES_GRAPHICS using Unity.Mathematics; #endif namespace GLTFast { /// /// Loads a glTF from a MonoBehaviour but instantiates Entities. /// Intermediate solution and drop-in replacement for GltfAsset /// TODO: To be replaced with a pure ECS concept /// [BurstCompile] public class GltfEntityAsset : GltfAssetBase { public string Url => url; /// /// Automatically load at start /// public bool LoadOnStartup { get => loadOnStartup; set => loadOnStartup = value; } /// /// Scene to load (-1 loads glTFs default scene) /// protected int SceneId => sceneId; /// /// If true, url is treated as relative StreamingAssets path /// public bool StreamingAsset => streamingAsset; /// public InstantiationSettings InstantiationSettings { get => instantiationSettings; set => instantiationSettings = value; } [SerializeField] [Tooltip("URL to load the glTF from.")] string url; [SerializeField] [Tooltip("Automatically load at start.")] bool loadOnStartup = true; [SerializeField] [Tooltip("Override scene to load (-1 loads glTFs default scene)")] int sceneId = -1; [SerializeField] [Tooltip("If checked, url is treated as relative StreamingAssets path.")] bool streamingAsset; [SerializeField] InstantiationSettings instantiationSettings; Entity m_SceneRoot; public string FullUrl => streamingAsset ? Path.Combine(Application.streamingAssetsPath, url) : url; protected virtual async void Start() { if(loadOnStartup && !string.IsNullOrEmpty(url)) { // Automatic load on startup await Load(FullUrl); } } public override async Task Load( string gltfUrl, IDownloadProvider downloadProvider=null, IDeferAgent deferAgent=null, IMaterialGenerator materialGenerator=null, ICodeLogger logger = null ) { logger = logger ?? new ConsoleLogger(); var success = await base.Load(gltfUrl, downloadProvider, deferAgent, materialGenerator, logger); if(success) { if (deferAgent != null) await deferAgent.BreakPoint(); // Auto-Instantiate if (sceneId>=0) { await InstantiateScene(sceneId,logger); } else { await Instantiate(logger); } } return success; } protected override IInstantiator GetDefaultInstantiator(ICodeLogger logger) { var entityManager = World.DefaultGameObjectInjectionWorld.EntityManager; var sceneArchetype = entityManager.CreateArchetype( #if UNITY_DOTS_HYBRID typeof(Translation), typeof(Rotation), typeof(Scale), typeof(LocalToWorld) #else typeof(LocalTransform), typeof(LocalToWorld) #endif // typeof(LinkedEntityGroup) ); m_SceneRoot = entityManager.CreateEntity(sceneArchetype); #if UNITY_EDITOR entityManager.SetName(m_SceneRoot, string.IsNullOrEmpty(name) ? "glTF" : name); #endif #if UNITY_DOTS_HYBRID entityManager.SetComponentData(m_SceneRoot,new Translation {Value = transform.position}); entityManager.SetComponentData(m_SceneRoot,new Rotation {Value = transform.rotation}); entityManager.SetComponentData(m_SceneRoot,new Scale {Value = transform.localScale.x}); // entityManager.AddBuffer(sceneRoot); #else var transformCached = transform; entityManager.SetComponentData( m_SceneRoot, new LocalTransform { Position = transformCached.position, Rotation = transformCached.rotation, Scale = transformCached.localScale.x, }); entityManager.SetComponentData(m_SceneRoot, new LocalToWorld{Value = float4x4.identity}); #endif return new EntityInstantiator(Importer, m_SceneRoot, logger, instantiationSettings); } protected override void PostInstantiation(IInstantiator instantiator, bool success) { CurrentSceneId = success ? Importer.DefaultSceneIndex : null; } /// /// Removes previously instantiated scene(s) /// public override void ClearScenes() { if (m_SceneRoot != Entity.Null) { var world = World.DefaultGameObjectInjectionWorld; var entityManager = world.EntityManager; DestroyEntityHierarchy(ref m_SceneRoot, ref entityManager); m_SceneRoot = Entity.Null; } } #if UNITY_ENTITIES_GRAPHICS [BurstCompile] #endif static void DestroyEntityHierarchy(ref Entity rootEntity, ref EntityManager entityManager) { var ecb = new EntityCommandBuffer(Allocator.Temp); DestroyEntity(ref rootEntity, ref entityManager, ref ecb); ecb.Playback(entityManager); ecb.Dispose(); } #if UNITY_ENTITIES_GRAPHICS [BurstCompile] #endif static void DestroyEntity(ref Entity entity, ref EntityManager entityManager, ref EntityCommandBuffer ecb) { if (entityManager.HasComponent(entity)) { var children = entityManager.GetBuffer(entity); foreach (var child in children) { var c = child.Value; DestroyEntity(ref c, ref entityManager, ref ecb); } } ecb.DestroyEntity(entity); } } } #endif // UNITY_DOTS_HYBRID