Beispiel #1
0
 public void RenderAllStages(GraphicsDevice gd, CommandList cl, GraphicsSystem sc)
 {
     if (ThreadedRendering)
     {
         RenderAllMultiThreaded(gd, cl, sc);
     }
     else
     {
         RenderAllSingleThread(gd, cl, sc);
     }
 }
Beispiel #2
0
        public void CreateAllDeviceObjects(GraphicsDevice gd, CommandList cl, GraphicsSystem sc)
        {
            _cullableStage[0].Clear();
            _octree.GetAllContainedObjects(_cullableStage[0]);
            foreach (ICullRenderable cr in _cullableStage[0])
            {
                cr.CreateDeviceObjects(gd, cl, sc);
            }
            foreach (IRenderable r in _freeRenderables)
            {
                r.CreateDeviceObjects(gd, cl, sc);
            }

            _resourceUpdateCL      = gd.ResourceFactory.CreateCommandList();
            _resourceUpdateCL.Name = "Scene Resource Update Command List";
        }
        public virtual void CreateDeviceObjects(GraphicsDevice gd, CommandList cl, GraphicsSystem sc)
        {
            ResourceFactory factory = gd.ResourceFactory;

            ProjectionMatrixBuffer          = factory.CreateBuffer(new BufferDescription(64, BufferUsage.UniformBuffer | BufferUsage.Dynamic));
            ViewMatrixBuffer                = factory.CreateBuffer(new BufferDescription(64, BufferUsage.UniformBuffer | BufferUsage.Dynamic));
            LightViewProjectionBuffer0      = factory.CreateBuffer(new BufferDescription(64, BufferUsage.UniformBuffer | BufferUsage.Dynamic));
            LightViewProjectionBuffer0.Name = "LightViewProjectionBuffer0";
            LightViewProjectionBuffer1      = factory.CreateBuffer(new BufferDescription(64, BufferUsage.UniformBuffer | BufferUsage.Dynamic));
            LightViewProjectionBuffer1.Name = "LightViewProjectionBuffer1";
            LightViewProjectionBuffer2      = factory.CreateBuffer(new BufferDescription(64, BufferUsage.UniformBuffer | BufferUsage.Dynamic));
            LightViewProjectionBuffer2.Name = "LightViewProjectionBuffer2";
            DepthLimitsBuffer               = factory.CreateBuffer(new BufferDescription((uint)Unsafe.SizeOf <DepthCascadeLimits>(), BufferUsage.UniformBuffer | BufferUsage.Dynamic));
            LightInfoBuffer  = factory.CreateBuffer(new BufferDescription((uint)Unsafe.SizeOf <DirectionalLightInfo>(), BufferUsage.UniformBuffer | BufferUsage.Dynamic));
            CameraInfoBuffer = factory.CreateBuffer(new BufferDescription((uint)Unsafe.SizeOf <CameraInfo>(), BufferUsage.UniformBuffer | BufferUsage.Dynamic));
            if (Camera != null)
            {
                UpdateCameraBuffers(cl);
            }

            PointLightsBuffer = factory.CreateBuffer(new BufferDescription((uint)Unsafe.SizeOf <PointLightsInfo.Blittable>(), BufferUsage.UniformBuffer));

            PointLightsInfo pli = new PointLightsInfo();

            pli.NumActiveLights = 4;
            pli.PointLights     = new PointLightInfo[4]
            {
                new PointLightInfo {
                    Color = new Vector3(1f, 1f, 1f), Position = new Vector3(-50, 5, 0), Range = 75f
                },
                new PointLightInfo {
                    Color = new Vector3(1f, .75f, .9f), Position = new Vector3(0, 5, 0), Range = 100f
                },
                new PointLightInfo {
                    Color = new Vector3(1f, 1f, 0.6f), Position = new Vector3(50, 5, 0), Range = 40f
                },
                new PointLightInfo {
                    Color = new Vector3(0.75f, 0.75f, 1f), Position = new Vector3(25, 5, 45), Range = 150f
                },
            };

            cl.UpdateBuffer(PointLightsBuffer, 0, pli.GetBlittable());

            TextureSamplerResourceLayout = factory.CreateResourceLayout(new ResourceLayoutDescription(
                                                                            new ResourceLayoutElementDescription("SourceTexture", ResourceKind.TextureReadOnly, ShaderStages.Fragment),
                                                                            new ResourceLayoutElementDescription("SourceSampler", ResourceKind.Sampler, ShaderStages.Fragment)));

            uint ReflectionMapSize = 2048;

            ReflectionColorTexture   = factory.CreateTexture(TextureDescription.Texture2D(ReflectionMapSize, ReflectionMapSize, 12, 1, PixelFormat.R8_G8_B8_A8_UNorm, TextureUsage.RenderTarget | TextureUsage.Sampled | TextureUsage.GenerateMipmaps));
            ReflectionDepthTexture   = factory.CreateTexture(TextureDescription.Texture2D(ReflectionMapSize, ReflectionMapSize, 1, 1, PixelFormat.R32_Float, TextureUsage.DepthStencil));
            ReflectionColorView      = factory.CreateTextureView(ReflectionColorTexture);
            ReflectionFramebuffer    = factory.CreateFramebuffer(new FramebufferDescription(ReflectionDepthTexture, ReflectionColorTexture));
            ReflectionViewProjBuffer = factory.CreateBuffer(new BufferDescription(64, BufferUsage.UniformBuffer | BufferUsage.Dynamic));

            MirrorClipPlaneBuffer = factory.CreateBuffer(new BufferDescription(32, BufferUsage.UniformBuffer));
            gd.UpdateBuffer(MirrorClipPlaneBuffer, 0, new ClipPlaneInfo(MirrorMesh.Plane, true));
            NoClipPlaneBuffer = factory.CreateBuffer(new BufferDescription(32, BufferUsage.UniformBuffer));
            gd.UpdateBuffer(NoClipPlaneBuffer, 0, new ClipPlaneInfo());

            RecreateWindowSizedResources(gd, cl);

            ShadowMaps.CreateDeviceResources(gd);
        }
Beispiel #4
0
        public void Render(
            GraphicsDevice gd,
            CommandList rc,
            GraphicsSystem sc,
            RenderPasses pass,
            BoundingFrustum frustum,
            Vector3 viewPosition,
            RenderQueue renderQueue,
            List <ICullRenderable> ICullRenderableList,
            List <IRenderable> renderableList,
            Comparer <RenderItemIndex> comparer,
            bool threaded)
        {
            renderQueue.Clear();

            ICullRenderableList.Clear();
            CollectVisibleObjects(ref frustum, pass, ICullRenderableList);
            renderQueue.AddRange(ICullRenderableList, viewPosition);

            renderableList.Clear();
            CollectFreeObjects(pass, renderableList);
            renderQueue.AddRange(renderableList, viewPosition);

            if (comparer == null)
            {
                renderQueue.Sort();
            }
            else
            {
                renderQueue.Sort(comparer);
            }

            foreach (IRenderable renderable in renderQueue)
            {
                renderable.Render(gd, rc, sc, pass);
            }

            if (threaded)
            {
                lock (_allPerFrameRenderablesSet)
                {
                    foreach (ICullRenderable thing in ICullRenderableList)
                    {
                        _allPerFrameRenderablesSet.Add(thing);
                    }
                    foreach (IRenderable thing in renderableList)
                    {
                        _allPerFrameRenderablesSet.Add(thing);
                    }
                }
            }
            else
            {
                foreach (ICullRenderable thing in ICullRenderableList)
                {
                    _allPerFrameRenderablesSet.Add(thing);
                }
                foreach (IRenderable thing in renderableList)
                {
                    _allPerFrameRenderablesSet.Add(thing);
                }
            }
        }
Beispiel #5
0
        private Matrix4x4 UpdateDirectionalLightMatrices(
            bool useReverseDepth,
            GraphicsSystem sc,
            float near,
            float far,
            uint shadowMapWidth,
            out BoundingFrustum lightFrustum)
        {
            Vector3        lightDir = sc.DirectionalLight.Direction;
            Vector3        viewDir  = sc.Camera.LookDirection;
            Vector3        viewPos  = sc.Camera.Position;
            Vector3        unitY    = Vector3.UnitY;
            FrustumCorners cameraCorners;

            if (useReverseDepth)
            {
                FrustumHelpers.ComputePerspectiveFrustumCorners(
                    ref viewPos,
                    ref viewDir,
                    ref unitY,
                    sc.Camera.FieldOfView,
                    far,
                    near,
                    sc.Camera.AspectRatio,
                    out cameraCorners);
            }
            else
            {
                FrustumHelpers.ComputePerspectiveFrustumCorners(
                    ref viewPos,
                    ref viewDir,
                    ref unitY,
                    sc.Camera.FieldOfView,
                    near,
                    far,
                    sc.Camera.AspectRatio,
                    out cameraCorners);
            }

            // Approach used: http://alextardif.com/ShadowMapping.html

            Vector3 frustumCenter = Vector3.Zero;

            frustumCenter += cameraCorners.NearTopLeft;
            frustumCenter += cameraCorners.NearTopRight;
            frustumCenter += cameraCorners.NearBottomLeft;
            frustumCenter += cameraCorners.NearBottomRight;
            frustumCenter += cameraCorners.FarTopLeft;
            frustumCenter += cameraCorners.FarTopRight;
            frustumCenter += cameraCorners.FarBottomLeft;
            frustumCenter += cameraCorners.FarBottomRight;
            frustumCenter /= 8f;

            float radius        = (cameraCorners.NearTopLeft - cameraCorners.FarBottomRight).Length() / 2.0f;
            float texelsPerUnit = shadowMapWidth / (radius * 2.0f);

            Matrix4x4 scalar = Matrix4x4.CreateScale(texelsPerUnit, texelsPerUnit, texelsPerUnit);

            Vector3 baseLookAt = -lightDir;

            Matrix4x4 lookat = Matrix4x4.CreateLookAt(Vector3.Zero, baseLookAt, Vector3.UnitY);

            lookat = scalar * lookat;
            Matrix4x4.Invert(lookat, out Matrix4x4 lookatInv);

            frustumCenter   = Vector3.Transform(frustumCenter, lookat);
            frustumCenter.X = (int)frustumCenter.X;
            frustumCenter.Y = (int)frustumCenter.Y;
            frustumCenter   = Vector3.Transform(frustumCenter, lookatInv);

            Vector3 lightPos = frustumCenter - (lightDir * radius * 2f);

            Matrix4x4 lightView = Matrix4x4.CreateLookAt(lightPos, frustumCenter, Vector3.UnitY);

            Matrix4x4 lightProjection;

            if (useReverseDepth)
            {
                lightProjection = Matrix4x4.CreateOrthographicOffCenter(
                    -radius * _lScale,
                    radius * _rScale,
                    -radius * _bScale,
                    radius * _tScale,
                    radius * _fScale,
                    -radius * _nScale);
            }
            else
            {
                lightProjection = Matrix4x4.CreateOrthographicOffCenter(
                    -radius * _lScale,
                    radius * _rScale,
                    -radius * _bScale,
                    radius * _tScale,
                    -radius * _nScale,
                    radius * _fScale);
            }
            Matrix4x4 viewProjectionMatrix = lightView * lightProjection;

            lightFrustum = new BoundingFrustum(viewProjectionMatrix);
            return(viewProjectionMatrix);
        }
Beispiel #6
0
        private void RenderAllMultiThreaded(GraphicsDevice gd, CommandList cl, GraphicsSystem sc)
        {
            float     depthClear  = gd.IsDepthRangeZeroToOne ? 0f : 1f;
            Matrix4x4 cameraProj  = Camera.ProjectionMatrix;
            Vector4   nearLimitCS = Vector4.Transform(new Vector3(0, 0, -_nearCascadeLimit), cameraProj);
            Vector4   midLimitCS  = Vector4.Transform(new Vector3(0, 0, -_midCascadeLimit), cameraProj);
            Vector4   farLimitCS  = Vector4.Transform(new Vector3(0, 0, -_farCascadeLimit), cameraProj);
            Vector3   lightPos    = sc.DirectionalLight.Transform.Position - sc.DirectionalLight.Direction * 1000f;

            _resourceUpdateCL.Begin();
            CommandList[] cls = new CommandList[5];
            for (int i = 0; i < cls.Length; i++)
            {
                cls[i] = gd.ResourceFactory.CreateCommandList(); cls[i].Begin();
            }

            _resourceUpdateCL.UpdateBuffer(sc.DepthLimitsBuffer, 0, new DepthCascadeLimits
            {
                NearLimit = nearLimitCS.Z,
                MidLimit  = midLimitCS.Z,
                FarLimit  = farLimitCS.Z
            });

            _resourceUpdateCL.UpdateBuffer(sc.LightInfoBuffer, 0, sc.DirectionalLight.GetInfo());

            _allPerFrameRenderablesSet.Clear();
            _tasks[0] = Task.Run(() =>
            {
                // Near
                Matrix4x4 viewProj0 = UpdateDirectionalLightMatrices(
                    gd.IsDepthRangeZeroToOne,
                    sc,
                    Camera.NearDistance,
                    _nearCascadeLimit,
                    sc.ShadowMapTexture.Width,
                    out BoundingFrustum lightFrustum0);
                cls[1].UpdateBuffer(sc.LightViewProjectionBuffer0, 0, ref viewProj0);

                cls[1].SetFramebuffer(sc.NearShadowMapFramebuffer);
                cls[1].SetViewport(0, new Viewport(0, 0, sc.ShadowMapTexture.Width, sc.ShadowMapTexture.Height, 0, 1));
                cls[1].SetScissorRect(0, 0, 0, sc.ShadowMapTexture.Width, sc.ShadowMapTexture.Height);
                cls[1].ClearDepthStencil(depthClear);
                Render(gd, cls[1], sc, RenderPasses.ShadowMapNear, lightFrustum0, lightPos, _renderQueues[0], _cullableStage[0], _renderableStage[0], null, true);
            });

            _tasks[1] = Task.Run(() =>
            {
                // Mid
                Matrix4x4 viewProj1 = UpdateDirectionalLightMatrices(
                    gd.IsDepthRangeZeroToOne,
                    sc,
                    _nearCascadeLimit,
                    _midCascadeLimit,
                    sc.ShadowMapTexture.Width,
                    out var lightFrustum1);
                cls[2].UpdateBuffer(sc.LightViewProjectionBuffer1, 0, ref viewProj1);

                cls[2].SetFramebuffer(sc.MidShadowMapFramebuffer);
                cls[2].SetViewport(0, new Viewport(0, 0, sc.ShadowMapTexture.Width, sc.ShadowMapTexture.Height, 0, 1));
                cls[2].SetScissorRect(0, 0, 0, sc.ShadowMapTexture.Width, sc.ShadowMapTexture.Height);
                cls[2].ClearDepthStencil(depthClear);
                Render(gd, cls[2], sc, RenderPasses.ShadowMapMid, lightFrustum1, lightPos, _renderQueues[1], _cullableStage[1], _renderableStage[1], null, true);
            });

            _tasks[2] = Task.Run(() =>
            {
                // Far
                Matrix4x4 viewProj2 = UpdateDirectionalLightMatrices(
                    gd.IsDepthRangeZeroToOne,
                    sc,
                    _midCascadeLimit,
                    _farCascadeLimit,
                    sc.ShadowMapTexture.Width,
                    out var lightFrustum2);
                cls[3].UpdateBuffer(sc.LightViewProjectionBuffer2, 0, ref viewProj2);

                cls[3].SetFramebuffer(sc.FarShadowMapFramebuffer);
                cls[3].SetViewport(0, new Viewport(0, 0, sc.ShadowMapTexture.Width, sc.ShadowMapTexture.Height, 0, 1));
                cls[3].SetScissorRect(0, 0, 0, sc.ShadowMapTexture.Width, sc.ShadowMapTexture.Height);
                cls[3].ClearDepthStencil(depthClear);
                Render(gd, cls[3], sc, RenderPasses.ShadowMapFar, lightFrustum2, lightPos, _renderQueues[2], _cullableStage[2], _renderableStage[2], null, true);
            });

            _tasks[3] = Task.Run(() =>
            {
                // Reflections
                cls[4].SetFramebuffer(sc.ReflectionFramebuffer);
                float scWidth  = sc.ReflectionFramebuffer.Width;
                float scHeight = sc.ReflectionFramebuffer.Height;
                cls[4].SetViewport(0, new Viewport(0, 0, scWidth, scHeight, 0, 1));
                cls[4].SetFullViewports();
                cls[4].SetFullScissorRects();
                cls[4].ClearColorTarget(0, RgbaFloat.Black);
                cls[4].ClearDepthStencil(depthClear);

                // Render reflected scene.
                Matrix4x4 planeReflectionMatrix = Matrix4x4.CreateReflection(MirrorMesh.Plane);
                CameraInfo camInfo                = new CameraInfo();
                camInfo.CameraLookDirection       = Vector3.Normalize(Vector3.Reflect(_camera.LookDirection, MirrorMesh.Plane.Normal));
                camInfo.CameraPosition_WorldSpace = Vector3.Transform(_camera.Position, planeReflectionMatrix);
                cls[4].UpdateBuffer(sc.CameraInfoBuffer, 0, ref camInfo);

                Matrix4x4 view = sc.Camera.ViewMatrix;
                view           = planeReflectionMatrix * view;
                cls[4].UpdateBuffer(sc.ViewMatrixBuffer, 0, view);

                Matrix4x4 projection = _camera.ProjectionMatrix;
                cls[4].UpdateBuffer(sc.ReflectionViewProjBuffer, 0, view * projection);

                BoundingFrustum cameraFrustum = new BoundingFrustum(view * projection);
                Render(gd, cls[4], sc, RenderPasses.ReflectionMap, cameraFrustum, _camera.Position, _renderQueues[3], _cullableStage[3], _renderableStage[3], null, true);

                cl.GenerateMipmaps(sc.ReflectionColorTexture);

                // Main scene
                cls[4].SetFramebuffer(sc.MainSceneFramebuffer);
                scWidth  = sc.MainSceneFramebuffer.Width;
                scHeight = sc.MainSceneFramebuffer.Height;
                cls[4].SetViewport(0, new Viewport(0, 0, scWidth, scHeight, 0, 1));
                cls[4].SetScissorRect(0, 0, 0, (uint)scWidth, (uint)scHeight);
                cls[4].ClearColorTarget(0, RgbaFloat.Black);
                cls[4].ClearDepthStencil(depthClear);
                sc.UpdateCameraBuffers(cls[4]);
                cameraFrustum = new BoundingFrustum(_camera.ViewMatrix * _camera.ProjectionMatrix);
                Render(gd, cls[4], sc, RenderPasses.Standard, cameraFrustum, _camera.Position, _renderQueues[3], _cullableStage[3], _renderableStage[3], null, true);
                Render(gd, cls[4], sc, RenderPasses.AlphaBlend, cameraFrustum, _camera.Position, _renderQueues[3], _cullableStage[3], _renderableStage[3], null, true);
                Render(gd, cls[4], sc, RenderPasses.Overlay, cameraFrustum, _camera.Position, _renderQueues[3], _cullableStage[3], _renderableStage[3], null, true);
            });

            Task.WaitAll(_tasks);

            foreach (IRenderable renderable in _allPerFrameRenderablesSet)
            {
                renderable.UpdatePerFrameResources(gd, _resourceUpdateCL, sc);
            }
            _resourceUpdateCL.End();
            gd.SubmitCommands(_resourceUpdateCL);

            for (int i = 0; i < cls.Length; i++)
            {
                cls[i].End(); gd.SubmitCommands(cls[i]); cls[i].Dispose();
            }

            if (sc.MainSceneColorTexture.SampleCount != TextureSampleCount.Count1)
            {
                cl.ResolveTexture(sc.MainSceneColorTexture, sc.MainSceneResolvedColorTexture);
            }

            cl.SetFramebuffer(sc.DuplicatorFramebuffer);
            uint fbWidth  = sc.DuplicatorFramebuffer.Width;
            uint fbHeight = sc.DuplicatorFramebuffer.Height;

            cl.SetViewport(0, new Viewport(0, 0, fbWidth, fbHeight, 0, 1));
            cl.SetViewport(1, new Viewport(0, 0, fbWidth, fbHeight, 0, 1));
            cl.SetScissorRect(0, 0, 0, fbWidth, fbHeight);
            cl.SetScissorRect(1, 0, 0, fbWidth, fbHeight);
            Render(gd, cl, sc, RenderPasses.Duplicator, new BoundingFrustum(), _camera.Position, _renderQueues[0], _cullableStage[0], _renderableStage[0], null, false);

            cl.SetFramebuffer(gd.SwapchainFramebuffer);
            fbWidth  = gd.SwapchainFramebuffer.Width;
            fbHeight = gd.SwapchainFramebuffer.Height;
            cl.SetViewport(0, new Viewport(0, 0, fbWidth, fbHeight, 0, 1));
            cl.SetScissorRect(0, 0, 0, fbWidth, fbHeight);
            Render(gd, cl, sc, RenderPasses.SwapchainOutput, new BoundingFrustum(), _camera.Position, _renderQueues[0], _cullableStage[0], _renderableStage[0], null, false);

            cl.End();
            gd.SubmitCommands(cl);
        }
Beispiel #7
0
        public GlitchDemo()
        {
            // Window & Graphics Device
            // --------------------------------------------------
            WindowCreateInfo windowCI = new WindowCreateInfo
            {
                X                  = 50,
                Y                  = 50,
                WindowWidth        = 960,
                WindowHeight       = 540,
                WindowInitialState = WindowState.Normal,
                WindowTitle        = "Glitch Demo"
            };
            GraphicsDeviceOptions gdOptions = new GraphicsDeviceOptions(false, null, false, ResourceBindingModel.Improved, true);

#if DEBUG
            gdOptions.Debug = true;
#endif

            VeldridStartup.CreateWindowAndGraphicsDevice(
                windowCI,
                gdOptions,
                //GraphicsBackend.Metal,
                //GraphicsBackend.Vulkan,
                GraphicsBackend.OpenGL,
                //GraphicsBackend.OpenGLES,
                out _window,
                out _gd);
            _window.Resized += () => _windowResized = true;


            // Project Manifest
            // --------------------------------------------------
            ProjectManifest projectManifest;
            string          currentDir   = AppContext.BaseDirectory;
            string          manifestName = null;

            foreach (var file in Directory.EnumerateFiles(currentDir + "Assets"))
            {
                if (file.EndsWith("manifest"))
                {
                    if (manifestName != null)
                    {
                        string errorMessage = "Error: Multiple project manifests in this directory: " + currentDir;
                        Console.WriteLine(errorMessage);
                        throw new System.Exception(errorMessage);
                    }
                    manifestName = file;
                }
            }

            using (var fs = File.OpenRead(manifestName))
                using (var sr = new StreamReader(fs))
                    using (var jtr = new JsonTextReader(sr))
                    {
                        var js = new JsonSerializer();
                        try
                        {
                            projectManifest = js.Deserialize <ProjectManifest>(jtr);
                        }
                        catch (Exception e)
                        {
                            string errorMessage = "An error was encountered while loading the project manifest.";
                            Console.WriteLine(errorMessage);
                            Console.WriteLine(e);
                            throw new System.NullReferenceException(errorMessage);
                        }
                    }

            // Initialize Game()
            // --------------------------------------------------
            _game = new Game();

            // Assembly & Asset System
            // --------------------------------------------------
            AssemblyLoadSystem als = new AssemblyLoadSystem();
            als.LoadFromProjectManifest(projectManifest, AppContext.BaseDirectory);
            _game.SystemRegistry.Register(als);

            AssetSystem assetSystem = new AssetSystem(Path.Combine(AppContext.BaseDirectory, projectManifest.AssetRoot), als.Binder);
            _game.SystemRegistry.Register(assetSystem);

            // Graphics System
            // --------------------------------------------------
            _gs = new GraphicsSystem(_gd);
            _game.SystemRegistry.Register(_gs);

            // Scene
            // --------------------------------------------------
            _scene = new Scene(_gd, _window.Width, _window.Height);

            // [For Debugging] - Custom SceneAsset Serializer
            // --------------------------------------------------
            SceneAsset programaticSceneAsset = new SceneAsset();
            programaticSceneAsset.Name = "MainMenu";
            // Custom GameObject (for camera & skybox)
            GameObject go1 = new GameObject();
            go1.Name    = "PlayerCamera";
            go1.Enabled = true;
            // Add custom camera to GameObject
            Camera camera = new Camera();
            camera.WindowHeight = _window.Height;
            camera.WindowWidth  = _window.Width;
            go1.AddComponent(camera);
            go1.Transform.LocalPosition = new Vector3(0f, 0f, 0f);
            // Add custom skybox to GameObject
            Skybox skybox = Skybox.LoadDefaultSkybox(_game.SystemRegistry);
            go1.AddComponent(skybox);
            // Custom GameObject (for sphere mesh)
            GameObject go2 = new GameObject();
            go2.Name    = "My Sphere";
            go2.Enabled = true;
            // Add custom sphere MeshRenderer component to GameObject
            Vector3    scale2           = new Vector3(1f);
            Vector3    offset2          = new Vector3(0f, 0f, -5f);
            Quaternion rotation2        = Quaternion.Identity;
            var        meshAssetID2     = new AssetID("Internal:SphereModel");
            var        meshAssetRef2    = new AssetRef <MeshData>(meshAssetID2);
            var        textureAssetID2  = new AssetID("Textures/rust.jpg");
            var        textureAssetRef2 = new AssetRef <ImageSharpTexture>(textureAssetID2);
            go2.Transform.LocalPosition = offset2;
            go2.Transform.LocalRotation = rotation2;
            go2.Transform.LocalScale    = scale2;
            MeshRenderer meshrenderer2 = new MeshRenderer(meshAssetRef2, textureAssetRef2);
            go2.AddComponent(meshrenderer2);
            // Custom GameObject (for plane mesh)
            GameObject go3 = new GameObject();
            go3.Name    = "My Plane Model";
            go3.Enabled = true;
            // Add custom Plane MeshRenderer component to GameObject
            Vector3    scale3           = new Vector3(10f);
            Vector3    offset3          = new Vector3(0f, -1f, -5f);
            Quaternion rotation3        = Quaternion.Identity;
            var        meshAssetID3     = new AssetID("Internal:PlaneModel");
            var        meshAssetRef3    = new AssetRef <MeshData>(meshAssetID3);
            var        textureAssetID3  = new AssetID("Textures/Wood.png");
            var        textureAssetRef3 = new AssetRef <ImageSharpTexture>(textureAssetID3);
            go3.Transform.LocalPosition = offset3;
            go3.Transform.LocalRotation = rotation3;
            go3.Transform.LocalScale    = scale3;
            MeshRenderer meshrenderer3 = new MeshRenderer(meshAssetRef3, textureAssetRef3);
            go3.AddComponent(meshrenderer3);
            // Custom GameObject (another sphere mesh)
            GameObject go4 = new GameObject();
            go4.Name    = "Another Sphere";
            go4.Enabled = true;
            Vector3    scale4           = new Vector3(0.5f);
            Vector3    offset4          = new Vector3(2f, -0.5f, -3f);
            Quaternion rotation4        = Quaternion.Identity;
            var        meshAssetID4     = new AssetID("Internal:SphereModel");
            var        meshAssetRef4    = new AssetRef <MeshData>(meshAssetID4);
            var        textureAssetID4  = new AssetID("Textures/rust.jpg");
            var        textureAssetRef4 = new AssetRef <ImageSharpTexture>(textureAssetID4);
            go4.Transform.LocalPosition = offset4;
            go4.Transform.LocalRotation = rotation4;
            go4.Transform.LocalScale    = scale4;
            MeshRenderer meshrenderer4 = new MeshRenderer(meshAssetRef4, textureAssetRef4);
            go4.AddComponent(meshrenderer4);
            // Add custom GameObject to SceneAsset
            SerializedGameObject sgo1 = new SerializedGameObject(go1);
            SerializedGameObject sgo2 = new SerializedGameObject(go2);
            SerializedGameObject sgo3 = new SerializedGameObject(go3);
            SerializedGameObject sgo4 = new SerializedGameObject(go4);
            programaticSceneAsset.GameObjects    = new SerializedGameObject[4];
            programaticSceneAsset.GameObjects[0] = sgo1;
            programaticSceneAsset.GameObjects[1] = sgo2;
            programaticSceneAsset.GameObjects[2] = sgo3;
            programaticSceneAsset.GameObjects[3] = sgo4;
            // Serialize SceneAsset
            LooseFileDatabase lfd          = new LooseFileDatabase("/Assets");
            StringWriter      stringwriter = new StringWriter(new StringBuilder());
            using (StreamWriter file = File.CreateText(@"DebugSceneAsset.json"))
            {
                JsonSerializer serializer = lfd.DefaultSerializer;
                serializer.Serialize(file, programaticSceneAsset);
            }

            // Scene Assets
            // --------------------------------------------------
            SceneAsset sceneAsset;
            AssetID    mainSceneID = projectManifest.OpeningScene.ID;
            if (mainSceneID.IsEmpty)
            {
                var scenes = assetSystem.Database.GetAssetsOfType(typeof(SceneAsset));
                if (!scenes.Any())
                {
                    Console.WriteLine("No scenes were available to load.");
                    throw new System.Exception("No scenes were available to load.");
                }
                else
                {
                    mainSceneID = scenes.First();
                }
            }

            var readSceneFromProgramaticAsset = true;
            sceneAsset = assetSystem.Database.LoadAsset <SceneAsset>(mainSceneID);
            _scene.LoadSceneAsset(readSceneFromProgramaticAsset ? programaticSceneAsset : sceneAsset);
            _gs.SetCurrentScene(_scene);

            // GUI
            // --------------------------------------------------
            _igRenderable   = new ImGuiRenderable(_window.Width, _window.Height);
            _resizeHandled += (w, h) => _igRenderable.WindowResized(w, h);
            _scene.AddRenderable(_igRenderable);
            _scene.AddUpdateable(_igRenderable);

            // Duplicate Screen (for post-processing filters)
            // --------------------------------------------------
            ScreenDuplicator duplicator = new ScreenDuplicator();
            _scene.AddRenderable(duplicator);

            // TODO: rename FullScreenQuad to FinalBufferObject or something
            _fsq = new FullScreenQuad();
            _scene.AddRenderable(_fsq);

            CreateAllObjects();
        }