コード例 #1
0
ファイル: Scene3D.cs プロジェクト: nashmit/OpenSAGE
        internal Scene3D(Game game, MapFile mapFile, int randomSeed)
            : this(game, () => game.Viewport, game.InputMessageBuffer, randomSeed, false)
        {
            var contentManager = game.ContentManager;

            _players = Player.FromMapData(mapFile.SidesList.Players, game.AssetStore).ToList();

            // TODO: This is completely wrong.
            LocalPlayer = _players.FirstOrDefault();

            _teams = (mapFile.SidesList.Teams ?? mapFile.Teams.Items)
                     .Select(team => Team.FromMapData(team, _players))
                     .ToList();

            MapFile    = mapFile;
            Terrain    = AddDisposable(new Terrain.Terrain(mapFile, game.AssetStore.LoadContext));
            WaterAreas = AddDisposable(new WaterAreaCollection(mapFile.PolygonTriggers, mapFile.StandingWaterAreas, mapFile.StandingWaveAreas, game.AssetStore.LoadContext));
            Navigation = new Navigation.Navigation(mapFile.BlendTileData, Terrain.HeightMap);

            Audio            = game.Audio;
            AssetLoadContext = game.AssetStore.LoadContext;

            Lighting = new WorldLighting(
                mapFile.GlobalLighting.LightingConfigurations.ToLightSettingsDictionary(),
                mapFile.GlobalLighting.Time);

            LoadObjects(
                game.AssetStore.LoadContext,
                game.CivilianPlayer,
                Terrain.HeightMap,
                mapFile.ObjectsList.Objects,
                MapFile.NamedCameras,
                _teams,
                out var waypoints,
                out var gameObjects,
                out var roads,
                out var bridges,
                out var cameras);

            Roads       = roads;
            Bridges     = bridges;
            GameObjects = gameObjects;
            Waypoints   = waypoints;
            Cameras     = cameras;

            PlayerScripts = mapFile
                            .GetPlayerScriptsList()
                            .ScriptLists
                            .Select(s => new MapScriptCollection(s))
                            .ToArray();

            CameraController = new RtsCameraController(game.AssetStore.GameData.Current)
            {
                TerrainPosition = Terrain.HeightMap.GetPosition(
                    Terrain.HeightMap.Width / 2,
                    Terrain.HeightMap.Height / 2)
            };

            contentManager.GraphicsDevice.WaitForIdle();
        }
コード例 #2
0
        public ModelMeshPartInstance(
            ModelMeshPart modelMeshPart,
            ModelMeshInstance modelMeshInstance,
            AssetLoadContext loadContext)
        {
            ModelMeshPart = modelMeshPart;
            MeshInstance  = modelMeshInstance;

            _renderItemConstantsResourceSet = AddDisposable(
                loadContext.ShaderResources.Mesh.CreateRenderItemConstantsResourceSet(
                    modelMeshPart.ModelMesh.MeshConstantsBuffer,
                    modelMeshInstance.RenderItemConstantsBufferVS,
                    modelMeshInstance.ModelInstance.SkinningBuffer,
                    modelMeshInstance.ModelInstance.RenderItemConstantsBufferPS));

            BeforeRenderCallback = (CommandList cl, in RenderItem renderItem) =>
            {
                OnBeforeRender(cl, renderItem);
            };

            BeforeRenderCallbackDepth = (CommandList cl, in RenderItem renderItem) =>
            {
                OnBeforeRender(cl, renderItem);
            };
        }
コード例 #3
0
 /// <summary>
 /// Loads the content of the file.
 /// </summary>
 /// <param name="context"></param>
 /// <param name="stream"></param>
 /// <param name="extensionHint"></param>
 protected override void Load(AssetLoadContext context, System.IO.Stream stream, string extensionHint)
 {
     using (var sr = new StreamReader(stream))
     {
         this.Content = sr.ReadToEnd();
     }
 }
コード例 #4
0
 private WaterArea(
     AssetLoadContext loadContext,
     StandingWaveArea area) : this(loadContext, area.Name)
 {
     CreateGeometry(loadContext, area.Points, area.FinalHeight);
     //TODO: add waves
 }
コード例 #5
0
        internal RoadCollection(RoadTopology topology, AssetLoadContext loadContext, HeightMap heightMap)
            : this()
        {
            // The map stores road segments with no connectivity:
            // - a segment is from point A to point B
            // - with a road type name
            // - and start and end curve types (angled, tight curve, broad curve).

            // The goal is to create road networks of connected road segments,
            // where a network has only a single road type.

            // A road network is composed of 2 or more nodes.
            // A network is a (potentially) cyclic graph.

            // A road node has > 1 and <= 4 edges connected to it.
            // A node can be part of multiple networks.

            // An edge can only exist in one network.

            var roadTemplateList = new RoadTemplateList(loadContext.AssetStore.RoadTemplates);

            var networks = RoadNetwork.BuildNetworks(topology, roadTemplateList);

            foreach (var network in networks)
            {
                _roads.Add(AddDisposable(new Road(
                                             loadContext,
                                             heightMap,
                                             network)));
            }
        }
コード例 #6
0
        internal ConstructBuildingOrderGenerator(
            ObjectDefinition buildingDefinition,
            int definitionIndex,
            GameData config,
            Player player,
            AssetLoadContext loadContext,
            Scene3D scene)
        {
            _buildingDefinition = buildingDefinition;
            _definitionIndex    = definitionIndex;
            _config             = config;
            _scene = scene;

            // TODO: Should this be relative to the current camera angle?
            _baseAngle = MathUtility.ToRadians(_buildingDefinition.PlacementViewAngle);
            _angle     = _baseAngle;

            _previewObject = new GameObject(
                buildingDefinition,
                loadContext,
                player,
                null,
                null,
                null);
            _previewObject.IsPlacementPreview = true;

            UpdatePreviewObjectPosition();
            UpdatePreviewObjectAngle();
            UpdateValidity();

            // TODO: This should work for all collider types.
            _collider = Collider.Create(_buildingDefinition, _previewObject.Transform) as BoxCollider;
        }
コード例 #7
0
        private void CreateGeometry(AssetLoadContext loadContext,
                                    Vector2[] points, uint height)
        {
            Triangulator.Triangulate(
                points,
                WindingOrder.CounterClockwise,
                out var trianglePoints,
                out var triangleIndices);

            var vertices = trianglePoints
                           .Select(x =>
                                   new WaterShaderResources.WaterVertex
            {
                Position = new Vector3(x.X, x.Y, height)
            })
                           .ToArray();

            _boundingBox = AxisAlignedBoundingBox.CreateFromPoints(vertices.Select(x => x.Position));

            _vertexBuffer = AddDisposable(loadContext.GraphicsDevice.CreateStaticBuffer(
                                              vertices,
                                              BufferUsage.VertexBuffer));

            _numIndices = (uint)triangleIndices.Length;

            _indexBuffer = AddDisposable(loadContext.GraphicsDevice.CreateStaticBuffer(
                                             triangleIndices,
                                             BufferUsage.IndexBuffer));

            _world             = Matrix4x4.Identity;
            _world.Translation = new Vector3(0, height, 0);
        }
コード例 #8
0
ファイル: WaterArea.cs プロジェクト: VectorIV/OpenSAGE
        private WaterArea(AssetLoadContext loadContext, string bumpTexName = null)
        {
            _shaderSet = loadContext.ShaderResources.Water.ShaderSet;
            _pipeline  = loadContext.ShaderResources.Water.Pipeline;

            _resourceSets = new Dictionary <TimeOfDay, ResourceSet>();

            Texture bumpTexture = null;

            if (bumpTexName != null)
            {
                bumpTexture = loadContext.AssetStore.Textures.GetByName(bumpTexName);
            }
            else
            {
                bumpTexture = loadContext.StandardGraphicsResources.SolidWhiteTexture;
            }

            foreach (var waterSet in loadContext.AssetStore.WaterSets)
            {
                // TODO: Cache these resource sets in some sort of scoped data context.
                var resourceSet = AddDisposable(loadContext.ShaderResources.Water.CreateMaterialResourceSet(waterSet.WaterTexture.Value, bumpTexture));

                _resourceSets.Add(waterSet.TimeOfDay, resourceSet);
            }

            _beforeRender = (cl, context) =>
            {
                cl.SetGraphicsResourceSet(4, _resourceSets[context.Scene3D.Lighting.TimeOfDay]);
                cl.SetVertexBuffer(0, _vertexBuffer);
            };
        }
コード例 #9
0
        public ParticleSystemManager(AssetLoadContext assetLoadContext)
        {
            _loadContext      = assetLoadContext;
            _maxParticleCount = assetLoadContext.AssetStore.GameData.Current.MaxParticleCount;

            _particleSystems = new List <ParticleSystem>();
        }
コード例 #10
0
ファイル: WaterArea.cs プロジェクト: wu162/OpenSAGE
 private WaterArea(
     AssetLoadContext loadContext,
     StandingWaterArea area) : this(loadContext)
 {
     CreateGeometry(loadContext, area.Points, area.WaterHeight);
     //TODO: use depthcolors
 }
コード例 #11
0
        // One ModelMeshPart for each unique shader in a W3D_CHUNK_MATERIAL_PASS.
        private ModelMeshPart CreateModelMeshPart(
            AssetLoadContext context,
            DeviceBuffer texCoordsVertexBuffer,
            uint startIndex,
            uint indexCount,
            W3dMesh w3dMesh,
            FixedFunctionShaderResources.VertexMaterial[] vertexMaterials,
            FixedFunctionShaderResources.ShadingConfiguration[] shadingConfigurations,
            uint vertexMaterialID,
            uint shaderID,
            uint numTextureStages,
            uint?textureIndex0,
            uint?textureIndex1)
        {
            var w3dShader = w3dMesh.Shaders.Items[(int)shaderID];

            var cullMode = w3dMesh.Header.Attributes.HasFlag(W3dMeshFlags.TwoSided)
                ? FaceCullMode.None
                : FaceCullMode.Back;

            var depthWriteEnabled = w3dShader.DepthMask == W3dShaderDepthMask.WriteEnable;
            var depthComparison   = w3dShader.DepthCompare.ToComparison();

            var blendEnabled           = w3dShader.SrcBlend != W3dShaderSrcBlendFunc.One || w3dShader.DestBlend != W3dShaderDestBlendFunc.Zero;
            var sourceFactor           = w3dShader.SrcBlend.ToBlend();
            var destinationColorFactor = w3dShader.DestBlend.ToBlend(false);
            var destinationAlphaFactor = w3dShader.DestBlend.ToBlend(true);

            var pipeline = context.ShaderResources.FixedFunction.GetCachedPipeline(
                cullMode,
                depthWriteEnabled,
                depthComparison,
                blendEnabled,
                sourceFactor,
                destinationColorFactor,
                destinationAlphaFactor);

            var materialConstantsBuffer = AddDisposable(context.GraphicsDevice.CreateStaticBuffer(
                                                            new FixedFunctionShaderResources.MaterialConstantsType
            {
                Material         = vertexMaterials[vertexMaterialID],
                Shading          = shadingConfigurations[shaderID],
                NumTextureStages = (int)numTextureStages
            },
                                                            BufferUsage.UniformBuffer));

            var materialResourceSet = AddDisposable(context.ShaderResources.FixedFunction.CreateMaterialResourceSet(
                                                        materialConstantsBuffer,
                                                        CreateTexture(context, w3dMesh, textureIndex0) ?? context.StandardGraphicsResources.NullTexture,
                                                        CreateTexture(context, w3dMesh, textureIndex1) ?? context.StandardGraphicsResources.NullTexture));

            return(new ModelMeshPart(
                       texCoordsVertexBuffer,
                       startIndex,
                       indexCount,
                       blendEnabled,
                       pipeline,
                       materialResourceSet));
        }
コード例 #12
0
 internal ParticleSystem(
     FXParticleSystemTemplate template,
     AssetLoadContext loadContext,
     GetMatrixReferenceDelegate getWorldMatrix)
     : this(template, loadContext)
 {
     _getWorldMatrix = getWorldMatrix;
 }
コード例 #13
0
 internal GameObjectCollection(AssetLoadContext loadContext,
                               Player civilianPlayer, Navigation.Navigation navigation)
 {
     _loadContext    = loadContext;
     _items          = new List <GameObject>();
     _civilianPlayer = civilianPlayer;
     _navigation     = navigation;
 }
コード例 #14
0
        internal ModelBox(W3dBox w3dBox, AssetLoadContext context)
        {
            _boundingBox = new AxisAlignedBoundingBox(
                w3dBox.Center - w3dBox.Extent,
                w3dBox.Center + w3dBox.Extent);

            // TODO
        }
コード例 #15
0
ファイル: WaterArea.cs プロジェクト: wu162/OpenSAGE
        private WaterArea(AssetLoadContext loadContext)
        {
            _shaderSet = loadContext.ShaderResources.Water.ShaderSet;
            _pipeline  = loadContext.ShaderResources.Water.Pipeline;

            _beforeRender = (cl, context) =>
            {
                cl.SetVertexBuffer(0, _vertexBuffer);
            };
        }
コード例 #16
0
        private WaterArea(
            AssetLoadContext loadContext,
            PolygonTrigger trigger) : this(loadContext, trigger.Name)
        {
            var triggerPoints = trigger.Points
                                .Select(x => new Vector2(x.X, x.Y))
                                .ToArray();

            CreateGeometry(loadContext, triggerPoints, (uint)trigger.Points[0].Z);
        }
コード例 #17
0
        public ParticleSystemManager(Scene3D scene, AssetLoadContext assetLoadContext)
        {
            _scene            = scene;
            _loadContext      = assetLoadContext;
            _maxParticleCount = assetLoadContext.AssetStore.GameData.Current.MaxParticleCount;

            _particleSystems = new List <ParticleSystem>();

            _renderBucket = scene.RenderScene.CreateRenderBucket("Particles", 15);
        }
コード例 #18
0
ファイル: GameContext.cs プロジェクト: wu162/OpenSAGE
 public GameContext(
     AssetLoadContext assetLoadContext,
     AudioSystem audioSystem,
     ParticleSystemManager particleSystems,
     Terrain.Terrain terrain)
 {
     AssetLoadContext = assetLoadContext;
     AudioSystem      = audioSystem;
     ParticleSystems  = particleSystems;
     Terrain          = terrain;
 }
コード例 #19
0
        internal Scene3D(
            Game game,
            MapFile mapFile,
            string mapPath,
            int randomSeed,
            Data.Map.Player[] mapPlayers,
            Data.Map.Team[] mapTeams,
            ScriptList[] mapScriptLists,
            GameType gameType)
            : this(game, () => game.Viewport, game.InputMessageBuffer, randomSeed, false, mapFile, mapPath)
        {
            var contentManager = game.ContentManager;

            PlayerManager.OnNewGame(mapPlayers, game, gameType);

            TeamFactory = new TeamFactory();
            TeamFactory.Initialize(mapTeams, PlayerManager);

            Audio            = game.Audio;
            AssetLoadContext = game.AssetStore.LoadContext;

            Lighting = new WorldLighting(
                mapFile.GlobalLighting.LightingConfigurations.ToLightSettingsDictionary(),
                mapFile.GlobalLighting.Time);

            LoadObjects(
                game.AssetStore.LoadContext,
                Terrain.HeightMap,
                mapFile.ObjectsList.Objects,
                MapFile.NamedCameras,
                out var waypoints,
                out var roads,
                out var bridges,
                out var cameras);

            Roads     = roads;
            Bridges   = bridges;
            Waypoints = waypoints;
            Cameras   = cameras;

            PlayerScripts = new PlayerScriptsList
            {
                ScriptLists = mapScriptLists
            };

            CameraController = new RtsCameraController(game.AssetStore.GameData.Current, Camera, Terrain.HeightMap)
            {
                TerrainPosition = Terrain.HeightMap.GetPosition(
                    Terrain.HeightMap.Width / 2,
                    Terrain.HeightMap.Height / 2)
            };

            contentManager.GraphicsDevice.WaitForIdle();
        }
コード例 #20
0
ファイル: Terrain.cs プロジェクト: DaanMeijer/OpenSAGE
        private void CreateTextures(
            AssetLoadContext loadContext,
            BlendTileData blendTileData,
            out Texture textureArray,
            out TerrainShaderResources.TextureInfo[] textureDetails)
        {
            var graphicsDevice = loadContext.GraphicsDevice;

            var numTextures = (uint)blendTileData.Textures.Length;

            var textureInfo        = new (uint size, FileSystemEntry entry)[numTextures];
コード例 #21
0
        private WaterArea(
            AssetLoadContext loadContext,
            PolygonTrigger trigger)
        {
            var triggerPoints = trigger.Points
                                .Select(x => new Vector2(x.X, x.Y))
                                .ToArray();

            Triangulator.Triangulate(
                triggerPoints,
                WindingOrder.CounterClockwise,
                out var trianglePoints,
                out var triangleIndices);

            var vertices = trianglePoints
                           .Select(x =>
                                   new WaterShaderResources.WaterVertex
            {
                Position = new Vector3(x.X, x.Y, trigger.Points[0].Z)
            })
                           .ToArray();

            _boundingBox = BoundingBox.CreateFromPoints(vertices.Select(x => x.Position));

            _vertexBuffer = AddDisposable(loadContext.GraphicsDevice.CreateStaticBuffer(
                                              vertices,
                                              BufferUsage.VertexBuffer));

            _numIndices = (uint)triangleIndices.Length;

            _indexBuffer = AddDisposable(loadContext.GraphicsDevice.CreateStaticBuffer(
                                             triangleIndices,
                                             BufferUsage.IndexBuffer));

            _shaderSet = loadContext.ShaderResources.Water.ShaderSet;
            _pipeline  = loadContext.ShaderResources.Water.Pipeline;

            _resourceSets = new Dictionary <TimeOfDay, ResourceSet>();

            foreach (var waterSet in loadContext.AssetStore.WaterSets)
            {
                // TODO: Cache these resource sets in some sort of scoped data context.
                var resourceSet = AddDisposable(loadContext.ShaderResources.Water.CreateMaterialResourceSet(waterSet.WaterTexture.Value));

                _resourceSets.Add(waterSet.TimeOfDay, resourceSet);
            }

            _beforeRender = (cl, context) =>
            {
                cl.SetGraphicsResourceSet(4, _resourceSets[context.Scene3D.Lighting.TimeOfDay]);
                cl.SetVertexBuffer(0, _vertexBuffer);
            };
        }
コード例 #22
0
 internal GameObjectCollection(
     AssetLoadContext loadContext,
     Player civilianPlayer,
     Navigation.Navigation navigation,
     Scene3D scene)
 {
     _loadContext    = loadContext;
     _items          = new List <GameObject>();
     _nameLookup     = new Dictionary <string, GameObject>();
     _civilianPlayer = civilianPlayer;
     _navigation     = navigation;
     _scene          = scene;
 }
コード例 #23
0
        internal Scene3D(Game game, MapFile mapFile, string mapPath, int randomSeed)
            : this(game, () => game.Viewport, game.InputMessageBuffer, randomSeed, false, mapFile, mapPath)
        {
            var contentManager = game.ContentManager;

            _players = Player.FromMapData(mapFile.SidesList.Players, game.AssetStore).ToList();

            LocalPlayer = _players.First();

            _teams = (mapFile.SidesList.Teams ?? mapFile.Teams.Items)
                     .Select(team => Team.FromMapData(team, _players))
                     .ToList();

            Audio            = game.Audio;
            AssetLoadContext = game.AssetStore.LoadContext;

            Lighting = new WorldLighting(
                mapFile.GlobalLighting.LightingConfigurations.ToLightSettingsDictionary(),
                mapFile.GlobalLighting.Time);

            LoadObjects(
                game.AssetStore.LoadContext,
                Terrain.HeightMap,
                mapFile.ObjectsList.Objects,
                MapFile.NamedCameras,
                _teams,
                out var waypoints,
                out var roads,
                out var bridges,
                out var cameras);

            Roads     = roads;
            Bridges   = bridges;
            Waypoints = waypoints;
            Cameras   = cameras;

            PlayerScripts = mapFile
                            .GetPlayerScriptsList()
                            .ScriptLists;

            CameraController = new RtsCameraController(game.AssetStore.GameData.Current, Camera, Terrain.HeightMap)
            {
                TerrainPosition = Terrain.HeightMap.GetPosition(
                    Terrain.HeightMap.Width / 2,
                    Terrain.HeightMap.Height / 2)
            };

            contentManager.GraphicsDevice.WaitForIdle();
        }
コード例 #24
0
        internal static bool TryCreate(
            AssetLoadContext loadContext,
            StandingWaveArea area,
            out WaterArea result)
        {
            if (area.Points.Length < 3)
            {
                // Some maps (such as Training01) have water areas with fewer than 3 points.
                result = null;
                return(false);
            }

            result = new WaterArea(loadContext, area);
            return(true);
        }
コード例 #25
0
        internal static bool TryCreate(
            AssetLoadContext loadContext,
            PolygonTrigger trigger,
            out WaterArea result)
        {
            if (trigger.Points.Length < 3)
            {
                // Some maps (such as Training01) have water areas with fewer than 3 points.
                result = null;
                return(false);
            }

            result = new WaterArea(loadContext, trigger);
            return(true);
        }
コード例 #26
0
 public WeaponStateContext(
     GameObject gameObject,
     Weapon weapon,
     WeaponTemplate weaponTemplate,
     int weaponIndex,
     AudioSystem audioSystem,
     AssetLoadContext assetLoadContext)
 {
     GameObject       = gameObject;
     Weapon           = weapon;
     WeaponTemplate   = weaponTemplate;
     WeaponIndex      = weaponIndex;
     AudioSystem      = audioSystem;
     AssetLoadContext = assetLoadContext;
 }
コード例 #27
0
 public GameContext(
     AssetLoadContext assetLoadContext,
     AudioSystem audioSystem,
     ParticleSystemManager particleSystems,
     ObjectCreationListManager objectCreationLists,
     Terrain.Terrain terrain,
     Navigation.Navigation navigation)
 {
     AssetLoadContext    = assetLoadContext;
     AudioSystem         = audioSystem;
     ParticleSystems     = particleSystems;
     ObjectCreationLists = objectCreationLists;
     Terrain             = terrain;
     Navigation          = navigation;
 }
コード例 #28
0
        internal W3dModelDraw(W3dModelDrawModuleData data, AssetLoadContext loadContext)
        {
            _data        = data;
            _loadContext = loadContext;

            _conditionStates = new List <ModelConditionState>();

            if (data.DefaultConditionState != null)
            {
                _defaultConditionState = data.DefaultConditionState;
            }

            foreach (var conditionState in data.ConditionStates)
            {
                _conditionStates.Add(conditionState);
            }

            if (_defaultConditionState == null)
            {
                _defaultConditionState = _conditionStates.Find(x => !x.ConditionFlags.AnyBitSet);

                if (_defaultConditionState != null)
                {
                    _conditionStates.Remove(_defaultConditionState);
                }
                else
                {
                    throw new InvalidOperationException();
                }
            }

            SetActiveConditionState(_defaultConditionState);

            _animationStates = new List <AnimationState>();

            if (data.IdleAnimationState != null)
            {
                _idleAnimationState = data.IdleAnimationState;
            }

            foreach (var animationState in data.AnimationStates)
            {
                _animationStates.Add(animationState);
            }
        }
コード例 #29
0
ファイル: WaterArea.cs プロジェクト: lanyizi/OpenSAGE
        private WaterArea(AssetLoadContext loadContext, string debugName)
        {
            _shaderSet = loadContext.ShaderResources.Water;
            _pipeline  = loadContext.ShaderResources.Water.Pipeline;

            _material = AddDisposable(
                new Material(
                    _shaderSet,
                    _pipeline,
                    null)); // TODO: MaterialResourceSet

            _debugName = debugName;

            _beforeRender = (CommandList cl, RenderContext context, in RenderItem renderItem) =>
            {
                cl.SetVertexBuffer(0, _vertexBuffer);
            };
        }
コード例 #30
0
ファイル: RoadCollection.cs プロジェクト: wu162/OpenSAGE
        internal RoadCollection(RoadTopology topology, AssetLoadContext loadContext, HeightMap heightMap)
            : this()
        {
            // The map stores road segments with no connectivity:
            // - a segment is from point A to point B
            // - with a road type name
            // - and start and end curve types (angled, tight curve, broad curve).

            // The goal is to create road networks of connected road segments,
            // where a network has only a single road type.

            // A road network is composed of 2 or more nodes.
            // A network is a (potentially) cyclic graph.

            // A road node has > 1 and <= 4 edges connected to it.
            // A node can be part of multiple networks.

            // An edge can only exist in one network.

            // TODO: If a node stored in the map has > 4 edges, the extra edges
            // are put into a separate network.

            var networks = RoadNetwork.BuildNetworks(topology);

            // Roads of different types are rendered in reverse template order:
            // the first template has the lowest z-index, the last one the highest.
            // Since we don't know the index here we start with the templates,
            // join them with the networks and reverse the result.
            var sortedNetworks = loadContext.AssetStore.RoadTemplates
                                 .Join(
                networks,
                t => t.InstanceId,
                n => n.Template.InstanceId,
                (t, n) => n)
                                 .Reverse();

            foreach (var network in sortedNetworks)
            {
                _roads.Add(AddDisposable(new Road(
                                             loadContext,
                                             heightMap,
                                             network)));
            }
        }