Ejemplo n.º 1
0
        public static SceneSectionData[] WriteEntityScene(Scene scene, GameObjectConversionSettings settings, List <ReferencedUnityObjects> sectionRefObjs = null)
        {
            int framesToRetainBlobAssets = RetainBlobAssetsSetting.GetFramesToRetainBlobAssets(settings.BuildConfiguration);

            var world         = new World("ConversionWorld");
            var entityManager = world.EntityManager;

            settings.DestinationWorld = world;

            bool disposeBlobAssetCache = false;

            if (settings.BlobAssetStore == null)
            {
                settings.BlobAssetStore = new BlobAssetStore();
                disposeBlobAssetCache   = true;
            }

            List <(int, LogEventData)> journalData = null;

            settings.ConversionWorldPreDispose += conversionWorld =>
            {
                var mappingSystem = conversionWorld.GetExistingSystem <GameObjectConversionMappingSystem>();
                journalData = mappingSystem.JournalData.SelectLogEventsOrdered().ToList();
            };

            ConvertScene(scene, settings);
            EntitySceneOptimization.Optimize(world);

            if (settings.AssetImportContext != null)
            {
                using (var allTypes = new NativeHashMap <ComponentType, int>(100, Allocator.Temp))
                    using (var archetypes = new NativeList <EntityArchetype>(Allocator.Temp))
                    {
                        entityManager.GetAllArchetypes(archetypes);
                        foreach (var archetype in archetypes)
                        {
                            using (var componentTypes = archetype.GetComponentTypes())
                                foreach (var componentType in componentTypes)
                                {
                                    if (allTypes.TryAdd(componentType, 0))
                                    {
                                        TypeDependencyCache.AddDependency(settings.AssetImportContext, componentType);
                                    }
                                }
                        }
                    }

                TypeDependencyCache.AddAllSystemsDependency(settings.AssetImportContext);
            }


            var sceneSections = new List <SceneSectionData>();

            var subSectionList = new List <SceneSection>();

            entityManager.GetAllUniqueSharedComponentData(subSectionList);
            var extRefInfoEntities = new NativeArray <Entity>(subSectionList.Count, Allocator.Temp);

            NativeArray <Entity> entitiesInMainSection;

            var sectionQuery = entityManager.CreateEntityQuery(
                new EntityQueryDesc
            {
                All     = new[] { ComponentType.ReadWrite <SceneSection>() },
                Options = EntityQueryOptions.IncludePrefab | EntityQueryOptions.IncludeDisabled
            }
                );

            var sectionBoundsQuery = entityManager.CreateEntityQuery(
                new EntityQueryDesc
            {
                All     = new[] { ComponentType.ReadWrite <SceneBoundingVolume>(), ComponentType.ReadWrite <SceneSection>() },
                Options = EntityQueryOptions.IncludePrefab | EntityQueryOptions.IncludeDisabled
            }
                );

            var sceneGUID = settings.SceneGUID;

            {
                var section = new SceneSection {
                    SceneGUID = sceneGUID, Section = 0
                };
                sectionQuery.SetSharedComponentFilter(new SceneSection {
                    SceneGUID = sceneGUID, Section = 0
                });
                sectionBoundsQuery.SetSharedComponentFilter(new SceneSection {
                    SceneGUID = sceneGUID, Section = 0
                });
                entitiesInMainSection = sectionQuery.ToEntityArray(Allocator.TempJob);


                var bounds = GetBoundsAndRemove(entityManager, sectionBoundsQuery);

                // Each section will be serialized in its own world, entities that don't have a section are part of the main scene.
                // An entity that holds the array of external references to the main scene is required for each section.
                // We need to create them all before we start moving entities to section scenes,
                // otherwise they would reuse entities that have been moved and mess up the remapping tables.
                for (int sectionIndex = 1; sectionIndex < subSectionList.Count; ++sectionIndex)
                {
                    if (subSectionList[sectionIndex].Section == 0)
                    {
                        // Main section, the only one that doesn't need an external ref array
                        continue;
                    }

                    var extRefInfoEntity = entityManager.CreateEntity();
                    entityManager.AddSharedComponentData(extRefInfoEntity, subSectionList[sectionIndex]);
                    extRefInfoEntities[sectionIndex] = extRefInfoEntity;
                }

                // Public references array, only on the main section.
                var refInfoEntity = entityManager.CreateEntity();
                entityManager.AddBuffer <PublicEntityRef>(refInfoEntity);
                entityManager.AddSharedComponentData(refInfoEntity, section);
                var publicRefs = entityManager.GetBuffer <PublicEntityRef>(refInfoEntity);

//                entityManager.Debug.CheckInternalConsistency();

                //@TODO do we need to keep this index? doesn't carry any additional info
                for (int i = 0; i < entitiesInMainSection.Length; ++i)
                {
                    PublicEntityRef.Add(ref publicRefs,
                                        new PublicEntityRef {
                        entityIndex = i, targetEntity = entitiesInMainSection[i]
                    });
                }

                UnityEngine.Debug.Assert(publicRefs.Length == entitiesInMainSection.Length);

                // Save main section
                var sectionWorld   = new World("SectionWorld");
                var sectionManager = sectionWorld.EntityManager;

                var entityRemapping = entityManager.CreateEntityRemapArray(Allocator.TempJob);
                sectionManager.MoveEntitiesFrom(entityManager, sectionQuery, entityRemapping);

                AddRetainBlobAssetsEntity(sectionManager, framesToRetainBlobAssets);

                // The section component is only there to break the conversion world into different sections
                // We don't want to store that on the disk
                //@TODO: Component should be removed but currently leads to corrupt data file. Figure out why.
                //sectionManager.RemoveComponent(sectionManager.UniversalQuery, typeof(SceneSection));

                var sectionFileSize = WriteEntityScene(sectionManager, sceneGUID, "0", settings, out var objectRefCount, out var objRefs);
                sectionRefObjs?.Add(objRefs);
                sceneSections.Add(new SceneSectionData
                {
                    FileSize             = sectionFileSize,
                    SceneGUID            = sceneGUID,
                    ObjectReferenceCount = objectRefCount,
                    SubSectionIndex      = 0,
                    BoundingVolume       = bounds
                });

                entityRemapping.Dispose();
                sectionWorld.Dispose();
            }

            {
                // Index 0 is the default value of the shared component, not an actual section
                for (int subSectionIndex = 0; subSectionIndex < subSectionList.Count; ++subSectionIndex)
                {
                    var subSection = subSectionList[subSectionIndex];
                    if (subSection.Section == 0)
                    {
                        continue;
                    }

                    sectionQuery.SetSharedComponentFilter(subSection);
                    sectionBoundsQuery.SetSharedComponentFilter(subSection);

                    var bounds = GetBoundsAndRemove(entityManager, sectionBoundsQuery);

                    var entitiesInSection = sectionQuery.ToEntityArray(Allocator.TempJob);

                    if (entitiesInSection.Length > 0)
                    {
                        // Fetch back the external reference entity we created earlier to not disturb the mapping
                        var refInfoEntity = extRefInfoEntities[subSectionIndex];
                        entityManager.AddBuffer <ExternalEntityRef>(refInfoEntity);
                        var externRefs = entityManager.GetBuffer <ExternalEntityRef>(refInfoEntity);

                        // Store the mapping to everything in the main section
                        //@TODO maybe we don't need all that? is this worth worrying about?
                        for (int i = 0; i < entitiesInMainSection.Length; ++i)
                        {
                            ExternalEntityRef.Add(ref externRefs, new ExternalEntityRef {
                                entityIndex = i
                            });
                        }

                        var entityRemapping = entityManager.CreateEntityRemapArray(Allocator.TempJob);

                        // Entities will be remapped to a contiguous range in the section world, but they will
                        // also come with an unpredictable amount of meta entities. We have the guarantee that
                        // the entities in the main section won't be moved over, so there's a free range of that
                        // size at the end of the remapping table. So we use that range for external references.
                        var externEntityIndexStart = entityRemapping.Length - entitiesInMainSection.Length;

                        entityManager.AddComponentData(refInfoEntity,
                                                       new ExternalEntityRefInfo
                        {
                            SceneGUID        = sceneGUID,
                            EntityIndexStart = externEntityIndexStart
                        });

                        var sectionWorld   = new World("SectionWorld");
                        var sectionManager = sectionWorld.EntityManager;

                        // Insert mapping for external references, conversion world entity to virtual index in section
                        for (int i = 0; i < entitiesInMainSection.Length; ++i)
                        {
                            EntityRemapUtility.AddEntityRemapping(ref entityRemapping, entitiesInMainSection[i],
                                                                  new Entity {
                                Index = i + externEntityIndexStart, Version = 1
                            });
                        }

                        sectionManager.MoveEntitiesFrom(entityManager, sectionQuery, entityRemapping);

                        AddRetainBlobAssetsEntity(sectionManager, framesToRetainBlobAssets);
                        // Now that all the required entities have been moved over, we can get rid of the gap between
                        // real entities and external references. This allows remapping during load to deal with a
                        // smaller remap table, containing only useful entries.

                        int highestEntityIndexInUse = 0;
                        for (int i = 0; i < externEntityIndexStart; ++i)
                        {
                            var targetIndex = entityRemapping[i].Target.Index;
                            if (targetIndex < externEntityIndexStart && targetIndex > highestEntityIndexInUse)
                            {
                                highestEntityIndexInUse = targetIndex;
                            }
                        }

                        var oldExternEntityIndexStart = externEntityIndexStart;
                        externEntityIndexStart = highestEntityIndexInUse + 1;

                        sectionManager.SetComponentData
                        (
                            EntityRemapUtility.RemapEntity(ref entityRemapping, refInfoEntity),
                            new ExternalEntityRefInfo
                        {
                            SceneGUID        = sceneGUID,
                            EntityIndexStart = externEntityIndexStart
                        }
                        );

                        // When writing the scene, references to missing entities are set to Entity.Null by default
                        // (but only if they have been used, otherwise they remain untouched)
                        // We obviously don't want that to happen to our external references, so we add explicit mapping
                        // And at the same time, we put them back at the end of the effective range of real entities.
                        for (int i = 0; i < entitiesInMainSection.Length; ++i)
                        {
                            var src = new Entity {
                                Index = i + oldExternEntityIndexStart, Version = 1
                            };
                            var dst = new Entity {
                                Index = i + externEntityIndexStart, Version = 1
                            };
                            EntityRemapUtility.AddEntityRemapping(ref entityRemapping, src, dst);
                        }

                        // The section component is only there to break the conversion world into different sections
                        // We don't want to store that on the disk
                        //@TODO: Component should be removed but currently leads to corrupt data file. Figure out why.
                        //sectionManager.RemoveComponent(sectionManager.UniversalQuery, typeof(SceneSection));
                        var fileSize = WriteEntityScene(sectionManager, sceneGUID, subSection.Section.ToString(), settings, out var objectRefCount, out var objRefs, entityRemapping);
                        sectionRefObjs?.Add(objRefs);
                        sceneSections.Add(new SceneSectionData
                        {
                            FileSize             = fileSize,
                            SceneGUID            = sceneGUID,
                            ObjectReferenceCount = objectRefCount,
                            SubSectionIndex      = subSection.Section,
                            BoundingVolume       = bounds
                        });

                        entityRemapping.Dispose();
                        sectionWorld.Dispose();
                    }

                    entitiesInSection.Dispose();
                }
            }

            {
                var noSectionQuery = entityManager.CreateEntityQuery(
                    new EntityQueryDesc
                {
                    None    = new[] { ComponentType.ReadWrite <SceneSection>() },
                    Options = EntityQueryOptions.IncludePrefab | EntityQueryOptions.IncludeDisabled
                }
                    );
                if (noSectionQuery.CalculateEntityCount() != 0)
                {
                    Debug.LogWarning($"{noSectionQuery.CalculateEntityCount()} entities in the scene '{scene.path}' had no SceneSection and as a result were not serialized at all.");
                }
            }

            sectionQuery.Dispose();
            sectionBoundsQuery.Dispose();
            entitiesInMainSection.Dispose();
            world.Dispose();

            // Save the new header

            var sceneSectionsArray = sceneSections.ToArray();

            WriteHeader(sceneGUID, sceneSectionsArray, scene.name, settings.AssetImportContext);

            // If we are writing assets to assets folder directly, then we need to make sure the asset database see them so they can be loaded.
            if (settings.AssetImportContext == null)
            {
                AssetDatabase.Refresh();
            }

            if (disposeBlobAssetCache)
            {
                settings.BlobAssetStore.Dispose();
            }

            // Save the log of issues that happened during conversion

            WriteConversionLog(sceneGUID, journalData, scene.name, settings.AssetImportContext);

            return(sceneSectionsArray);
        }
Ejemplo n.º 2
0
        public static SceneData[] WriteEntityScene(Scene scene, Hash128 sceneGUID, ConversionFlags conversionFlags)
        {
            var world         = new World("ConversionWorld");
            var entityManager = world.EntityManager;

            ConvertScene(scene, sceneGUID, world, conversionFlags);
            EntitySceneOptimization.Optimize(world);

            var sceneSections = new List <SceneData>();

            var subSectionList = new List <SceneSection>();

            entityManager.GetAllUniqueSharedComponentData(subSectionList);
            var extRefInfoEntities = new NativeArray <Entity>(subSectionList.Count, Allocator.Temp);

            NativeArray <Entity> entitiesInMainSection;

            var sectionQuery = entityManager.CreateEntityQuery(
                new EntityQueryDesc
            {
                All     = new[] { ComponentType.ReadWrite <SceneSection>() },
                Options = EntityQueryOptions.IncludePrefab | EntityQueryOptions.IncludeDisabled
            }
                );

            var sectionBoundsQuery = entityManager.CreateEntityQuery(
                new EntityQueryDesc
            {
                All     = new[] { ComponentType.ReadWrite <SceneBoundingVolume>(), ComponentType.ReadWrite <SceneSection>() },
                Options = EntityQueryOptions.IncludePrefab | EntityQueryOptions.IncludeDisabled
            }
                );

            {
                var section = new SceneSection {
                    SceneGUID = sceneGUID, Section = 0
                };
                sectionQuery.SetFilter(new SceneSection {
                    SceneGUID = sceneGUID, Section = 0
                });
                sectionBoundsQuery.SetFilter(new SceneSection {
                    SceneGUID = sceneGUID, Section = 0
                });
                entitiesInMainSection = sectionQuery.ToEntityArray(Allocator.TempJob);


                var bounds = GetBoundsAndDestroy(entityManager, sectionBoundsQuery);

                // Each section will be serialized in its own world, entities that don't have a section are part of the main scene.
                // An entity that holds the array of external references to the main scene is required for each section.
                // We need to create them all before we start moving entities to section scenes,
                // otherwise they would reuse entities that have been moved and mess up the remapping tables.
                for (int sectionIndex = 1; sectionIndex < subSectionList.Count; ++sectionIndex)
                {
                    if (subSectionList[sectionIndex].Section == 0)
                    {
                        // Main section, the only one that doesn't need an external ref array
                        continue;
                    }

                    var extRefInfoEntity = entityManager.CreateEntity();
                    entityManager.AddSharedComponentData(extRefInfoEntity, subSectionList[sectionIndex]);
                    extRefInfoEntities[sectionIndex] = extRefInfoEntity;
                }

                // Public references array, only on the main section.
                var refInfoEntity = entityManager.CreateEntity();
                entityManager.AddBuffer <PublicEntityRef>(refInfoEntity);
                entityManager.AddSharedComponentData(refInfoEntity, section);
                var publicRefs = entityManager.GetBuffer <PublicEntityRef>(refInfoEntity);

//                entityManager.Debug.CheckInternalConsistency();

                //@TODO do we need to keep this index? doesn't carry any additional info
                for (int i = 0; i < entitiesInMainSection.Length; ++i)
                {
                    PublicEntityRef.Add(ref publicRefs,
                                        new PublicEntityRef {
                        entityIndex = i, targetEntity = entitiesInMainSection[i]
                    });
                }

                Debug.Assert(publicRefs.Length == entitiesInMainSection.Length);

                // Save main section
                var sectionWorld   = new World("SectionWorld");
                var sectionManager = sectionWorld.EntityManager;

                var entityRemapping = entityManager.CreateEntityRemapArray(Allocator.TempJob);
                sectionManager.MoveEntitiesFrom(entityManager, sectionQuery, entityRemapping);

                // The section component is only there to break the conversion world into different sections
                // We don't want to store that on the disk
                //@TODO: Component should be removed but currently leads to corrupt data file. Figure out why.
                //sectionManager.RemoveComponent(sectionManager.UniversalQuery, typeof(SceneSection));

                var sectionFileSize = WriteEntityScene(sectionManager, sceneGUID, "0");
                sceneSections.Add(new SceneData
                {
                    FileSize             = sectionFileSize,
                    SceneGUID            = sceneGUID,
                    SharedComponentCount = sectionManager.GetSharedComponentCount() - 1,
                    SubSectionIndex      = 0,
                    BoundingVolume       = bounds
                });

                entityRemapping.Dispose();
                sectionWorld.Dispose();
            }

            {
                // Index 0 is the default value of the shared component, not an actual section
                for (int subSectionIndex = 0; subSectionIndex < subSectionList.Count; ++subSectionIndex)
                {
                    var subSection = subSectionList[subSectionIndex];
                    if (subSection.Section == 0)
                    {
                        continue;
                    }

                    sectionQuery.SetFilter(subSection);
                    sectionBoundsQuery.SetFilter(subSection);

                    var bounds = GetBoundsAndDestroy(entityManager, sectionBoundsQuery);

                    var entitiesInSection = sectionQuery.ToEntityArray(Allocator.TempJob);

                    if (entitiesInSection.Length > 0)
                    {
                        // Fetch back the external reference entity we created earlier to not disturb the mapping
                        var refInfoEntity = extRefInfoEntities[subSectionIndex];
                        entityManager.AddBuffer <ExternalEntityRef>(refInfoEntity);
                        var externRefs = entityManager.GetBuffer <ExternalEntityRef>(refInfoEntity);

                        // Store the mapping to everything in the main section
                        //@TODO maybe we don't need all that? is this worth worrying about?
                        for (int i = 0; i < entitiesInMainSection.Length; ++i)
                        {
                            ExternalEntityRef.Add(ref externRefs, new ExternalEntityRef {
                                entityIndex = i
                            });
                        }

                        var entityRemapping = entityManager.CreateEntityRemapArray(Allocator.TempJob);

                        // Entities will be remapped to a contiguous range in the section world, but they will
                        // also come with an unpredictable amount of meta entities. We have the guarantee that
                        // the entities in the main section won't be moved over, so there's a free range of that
                        // size at the end of the remapping table. So we use that range for external references.
                        var externEntityIndexStart = entityRemapping.Length - entitiesInMainSection.Length;

                        entityManager.AddComponentData(refInfoEntity,
                                                       new ExternalEntityRefInfo
                        {
                            SceneGUID        = sceneGUID,
                            EntityIndexStart = externEntityIndexStart
                        });

                        var sectionWorld   = new World("SectionWorld");
                        var sectionManager = sectionWorld.EntityManager;

                        // Insert mapping for external references, conversion world entity to virtual index in section
                        for (int i = 0; i < entitiesInMainSection.Length; ++i)
                        {
                            EntityRemapUtility.AddEntityRemapping(ref entityRemapping, entitiesInMainSection[i],
                                                                  new Entity {
                                Index = i + externEntityIndexStart, Version = 1
                            });
                        }

                        sectionManager.MoveEntitiesFrom(entityManager, sectionQuery, entityRemapping);

                        // Now that all the required entities have been moved over, we can get rid of the gap between
                        // real entities and external references. This allows remapping during load to deal with a
                        // smaller remap table, containing only useful entries.

                        int highestEntityIndexInUse = 0;
                        for (int i = 0; i < externEntityIndexStart; ++i)
                        {
                            var targetIndex = entityRemapping[i].Target.Index;
                            if (targetIndex < externEntityIndexStart && targetIndex > highestEntityIndexInUse)
                            {
                                highestEntityIndexInUse = targetIndex;
                            }
                        }

                        var oldExternEntityIndexStart = externEntityIndexStart;
                        externEntityIndexStart = highestEntityIndexInUse + 1;

                        sectionManager.SetComponentData
                        (
                            EntityRemapUtility.RemapEntity(ref entityRemapping, refInfoEntity),
                            new ExternalEntityRefInfo
                        {
                            SceneGUID        = sceneGUID,
                            EntityIndexStart = externEntityIndexStart
                        }
                        );

                        // When writing the scene, references to missing entities are set to Entity.Null by default
                        // (but only if they have been used, otherwise they remain untouched)
                        // We obviously don't want that to happen to our external references, so we add explicit mapping
                        // And at the same time, we put them back at the end of the effective range of real entities.
                        for (int i = 0; i < entitiesInMainSection.Length; ++i)
                        {
                            var src = new Entity {
                                Index = i + oldExternEntityIndexStart, Version = 1
                            };
                            var dst = new Entity {
                                Index = i + externEntityIndexStart, Version = 1
                            };
                            EntityRemapUtility.AddEntityRemapping(ref entityRemapping, src, dst);
                        }

                        // The section component is only there to break the conversion world into different sections
                        // We don't want to store that on the disk
                        //@TODO: Component should be removed but currently leads to corrupt data file. Figure out why.
                        //sectionManager.RemoveComponent(sectionManager.UniversalQuery, typeof(SceneSection));

                        var fileSize = WriteEntityScene(sectionManager, sceneGUID, subSection.Section.ToString(), entityRemapping);
                        sceneSections.Add(new SceneData
                        {
                            FileSize             = fileSize,
                            SceneGUID            = sceneGUID,
                            SharedComponentCount = sectionManager.GetSharedComponentCount() - 1,
                            SubSectionIndex      = subSection.Section,
                            BoundingVolume       = bounds
                        });

                        entityRemapping.Dispose();
                        sectionWorld.Dispose();
                    }

                    entitiesInSection.Dispose();
                }
            }

            {
                var noSectionQuery = entityManager.CreateEntityQuery(
                    new EntityQueryDesc
                {
                    None    = new[] { ComponentType.ReadWrite <SceneSection>() },
                    Options = EntityQueryOptions.IncludePrefab | EntityQueryOptions.IncludeDisabled
                }
                    );
                if (noSectionQuery.CalculateLength() != 0)
                {
                    Debug.LogWarning($"{noSectionQuery.CalculateLength()} entities in the scene '{scene.path}' had no SceneSection and as a result were not serialized at all.");
                }
            }

            sectionQuery.Dispose();
            sectionBoundsQuery.Dispose();
            entitiesInMainSection.Dispose();
            world.Dispose();

            // Save the new header
            var header = ScriptableObject.CreateInstance <SubSceneHeader>();

            header.Sections = sceneSections.ToArray();

            WriteHeader(sceneGUID, header);

            return(sceneSections.ToArray());
        }
Ejemplo n.º 3
0
        public static SceneData[] WriteEntityScene(Scene scene, Hash128 sceneGUID, ConversionFlags conversionFlags)
        {
            var world         = new World("ConversionWorld");
            var entityManager = world.GetOrCreateManager <EntityManager>();

            var boundsEntity = entityManager.CreateEntity(typeof(SceneBoundingVolume));

            entityManager.SetComponentData(boundsEntity, new SceneBoundingVolume {
                Value = MinMaxAABB.Empty
            });

            ConvertScene(scene, sceneGUID, world, conversionFlags);
            EntitySceneOptimization.Optimize(world);

            var bounds = entityManager.GetComponentData <SceneBoundingVolume>(boundsEntity).Value;

            entityManager.DestroyEntity(boundsEntity);

            var sceneSections = new List <SceneData>();

            var subSectionList = new List <SceneSection>();

            entityManager.GetAllUniqueSharedComponentData(subSectionList);
            var extRefInfoEntities = new NativeArray <Entity>(subSectionList.Count, Allocator.Temp);

            NativeArray <Entity> entitiesInMainSection;

            var sectionGrp = entityManager.CreateComponentGroup(
                new EntityArchetypeQuery
            {
                All     = new[] { ComponentType.ReadWrite <SceneSection>() },
                Options = EntityArchetypeQueryOptions.IncludePrefab | EntityArchetypeQueryOptions.IncludeDisabled
            }
                );

            {
                var section = new SceneSection {
                    SceneGUID = sceneGUID, Section = 0
                };
                sectionGrp.SetFilter(new SceneSection {
                    SceneGUID = sceneGUID, Section = 0
                });
                entitiesInMainSection = sectionGrp.ToEntityArray(Allocator.TempJob);

                // Each section will be serialized in its own world, entities that don't have a section are part of the main scene.
                // An entity that holds the array of external references to the main scene is required for each section.
                // We need to create them all before we start moving entities to section scenes,
                // otherwise they would reuse entities that have been moved and mess up the remapping tables.
                for (int sectionIndex = 1; sectionIndex < subSectionList.Count; ++sectionIndex)
                {
                    var extRefInfoEntity = entityManager.CreateEntity();
                    entityManager.AddSharedComponentData(extRefInfoEntity, subSectionList[sectionIndex]);
                    extRefInfoEntities[sectionIndex] = extRefInfoEntity;
                }

                // Public references array, only on the main section.
                var refInfoEntity = entityManager.CreateEntity();
                entityManager.AddBuffer <PublicEntityRef>(refInfoEntity);
                entityManager.AddSharedComponentData(refInfoEntity, section);
                var publicRefs = entityManager.GetBuffer <PublicEntityRef>(refInfoEntity);

//                entityManager.Debug.CheckInternalConsistency();

                //@TODO do we need to keep this index? doesn't carry any additional info
                for (int i = 0; i < entitiesInMainSection.Length; ++i)
                {
                    PublicEntityRef.Add(ref publicRefs,
                                        new PublicEntityRef {
                        entityIndex = i, targetEntity = entitiesInMainSection[i]
                    });
                }

                Debug.Assert(publicRefs.Length == entitiesInMainSection.Length);

                // Save main section
                var sectionWorld   = new World("SectionWorld");
                var sectionManager = sectionWorld.GetOrCreateManager <EntityManager>();

                var entityRemapping = entityManager.CreateEntityRemapArray(Allocator.TempJob);
                sectionManager.MoveEntitiesFrom(entityManager, sectionGrp, entityRemapping);

                // The section component is only there to break the conversion world into different sections
                // We don't want to store that on the disk
                //@TODO: Component should be removed but currently leads to corrupt data file. Figure out why.
                //sectionManager.RemoveComponent(sectionManager.UniversalGroup, typeof(SceneSection));

                var sectionFileSize = WriteEntityScene(sectionManager, sceneGUID, "0");
                sceneSections.Add(new SceneData
                {
                    FileSize             = sectionFileSize,
                    SceneGUID            = sceneGUID,
                    SharedComponentCount = sectionManager.GetSharedComponentCount() - 1,
                    SubSectionIndex      = 0,
                    BoundingVolume       = bounds
                });

                entityRemapping.Dispose();
                sectionWorld.Dispose();
            }

            {
                // Index 0 is the default value of the shared component, not an actual section
                for (int subSectionIndex = 0; subSectionIndex < subSectionList.Count; ++subSectionIndex)
                {
                    var subSection = subSectionList[subSectionIndex];
                    if (subSection.Section == 0)
                    {
                        continue;
                    }

                    sectionGrp.SetFilter(subSection);
                    var entitiesInSection = sectionGrp.ToEntityArray(Allocator.TempJob);

                    if (entitiesInSection.Length > 0)
                    {
                        // Fetch back the external reference entity we created earlier to not disturb the mapping
                        var refInfoEntity = extRefInfoEntities[subSectionIndex];
                        entityManager.AddBuffer <ExternalEntityRef>(refInfoEntity);
                        var externRefs = entityManager.GetBuffer <ExternalEntityRef>(refInfoEntity);

                        // Store the mapping to everything in the main section
                        //@TODO maybe we don't need all that? is this worth worrying about?
                        for (int i = 0; i < entitiesInMainSection.Length; ++i)
                        {
                            ExternalEntityRef.Add(ref externRefs, new ExternalEntityRef {
                                entityIndex = i
                            });
                        }

                        // Entities will be remapped to a contiguous range in the section world,
                        // so any range after that is fine for the external references
                        //@TODO why are we not mapping anything to entity 0? we use the range [1;count], hence +1
                        var externEntityIndexStart = entitiesInSection.Length + 1;

                        entityManager.AddComponentData(refInfoEntity,
                                                       new ExternalEntityRefInfo
                        {
                            SceneGUID        = sceneGUID,
                            EntityIndexStart = externEntityIndexStart
                        });

                        var sectionWorld   = new World("SectionWorld");
                        var sectionManager = sectionWorld.GetOrCreateManager <EntityManager>();

                        var entityRemapping = entityManager.CreateEntityRemapArray(Allocator.TempJob);

                        // Insert mapping for external references, conversion world entity to virtual index in section
                        for (int i = 0; i < entitiesInMainSection.Length; ++i)
                        {
                            EntityRemapUtility.AddEntityRemapping(ref entityRemapping, entitiesInMainSection[i],
                                                                  new Entity {
                                Index = i + externEntityIndexStart, Version = 1
                            });
                        }

                        sectionManager.MoveEntitiesFrom(entityManager, sectionGrp, entityRemapping);

                        // When writing the scene, references to missing entities are set to Entity.Null by default
                        // We obviously don't want that to happen to our external references, so we add explicit mapping
                        for (int i = 0; i < entitiesInMainSection.Length; ++i)
                        {
                            var entity = new Entity {
                                Index = i + externEntityIndexStart, Version = 1
                            };
                            EntityRemapUtility.AddEntityRemapping(ref entityRemapping, entity, entity);
                        }

                        // The section component is only there to break the conversion world into different sections
                        // We don't want to store that on the disk
                        //@TODO: Component should be removed but currently leads to corrupt data file. Figure out why.
                        //sectionManager.RemoveComponent(sectionManager.UniversalGroup, typeof(SceneSection));

                        var fileSize = WriteEntityScene(sectionManager, sceneGUID, subSection.Section.ToString(), entityRemapping);
                        sceneSections.Add(new SceneData
                        {
                            FileSize             = fileSize,
                            SceneGUID            = sceneGUID,
                            SharedComponentCount = sectionManager.GetSharedComponentCount() - 1,
                            SubSectionIndex      = subSection.Section,
                            BoundingVolume       = bounds
                        });

                        entityRemapping.Dispose();
                        sectionWorld.Dispose();
                    }

                    entitiesInSection.Dispose();
                }
            }

            {
                var noSectionGrp = entityManager.CreateComponentGroup(
                    new EntityArchetypeQuery
                {
                    None    = new[] { ComponentType.ReadWrite <SceneSection>() },
                    Options = EntityArchetypeQueryOptions.IncludePrefab | EntityArchetypeQueryOptions.IncludeDisabled
                }
                    );
                if (noSectionGrp.CalculateLength() != 0)
                {
                    Debug.LogWarning($"{noSectionGrp.CalculateLength()} entities in the scene '{scene.path}' had no SceneSection and as a result were not serialized at all.");
                }
            }

            sectionGrp.Dispose();
            entitiesInMainSection.Dispose();
            world.Dispose();

            // Save the new header
            var header = ScriptableObject.CreateInstance <SubSceneHeader>();

            header.Sections = sceneSections.ToArray();

            WriteHeader(sceneGUID, header);

            return(sceneSections.ToArray());
        }