[Solved] Scene instances and management [1.3.4]

Hey all!

I see a lot of great videos and tutorials on how to manage scenes and scene children, but it’s all done in the studio editor - which I’m not using. Additionally, I’m adding a few other relevant (in my case) questions. How would one go about:

  1. Get current scene instance, from within an external class (not script or anything that is attached to anything in the scene).
    1b. …or just set up a new scene and use that.
  2. Load assets (assets seem to be managed completely different from 1.2).
  3. Attach these assets to entities and entity components, like models and materials
  4. Create a custom model, using the geometric primitives provided in the SiliconStudio.Paradox.Graphics.GeometricPrimitives lib.

I know this is a lot to ask, but I’m constantly banging my head into a wall when I get to these points.
I’ve tried to scour the internet for resources and trying to pick up scraps, left and right, and put them together to something useful, but I fail over and over.

I know my coding skills are a little rusty, and getting to know a new engine takes a lot of time and energy, but if anyone can answer these questions, it would really give me a headstart.
Thanks in advance! :slight_smile:

What you need to do is check out the source code and specifically look at the test-cases. In the latter you’ll find examples on how to achieve what you’ve listed.

I had similar ambitions - basically I want to use the editor only as an asset manager and do all the scene and pipeline construction programmatically. To that end below is one experiment I did (started with the forward lighting sample and incrementally modified it to be entirely procedural); note this was some time ago so it’s 1.2 and I don’t recall what state I left it in (it worked, but was playing around and might have broken something before I left it). Posting it anyway fwiw.

using SiliconStudio.Core;
using SiliconStudio.Core.Diagnostics;
using SiliconStudio.Core.Mathematics;
using SiliconStudio.Core.Serialization.Assets;
using SiliconStudio.Paradox.Engine;
using SiliconStudio.Paradox.Extensions;
using SiliconStudio.Paradox.Games;
using SiliconStudio.Paradox.Graphics;
using SiliconStudio.Paradox.Graphics.Data;
using SiliconStudio.Paradox.Graphics.GeometricPrimitives;
using SiliconStudio.Paradox.Input;
using SiliconStudio.Paradox.Rendering;
using SiliconStudio.Paradox.Rendering.Composers;
using SiliconStudio.Paradox.Rendering.Lights;
using SiliconStudio.Paradox.Rendering.ProceduralModels;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace ForwardLighting
{
    public class MyGame : Game
    {
        public static readonly Logger TestGameLogger = GlobalLogger.GetLogger("TestGameLogger");

        public int StopOnFrameCount { get; set; }

        private Texture[] textures;
        private int renderTargetToDisplayIndex = 0;
        private Entity teapot;

        private Scene scene;

        private Entity mainCamera;

        private RasterizerState wireframeState;

        private SpriteBatch spriteBatch;

        private SpriteFont font;

        //private bool rotateModel;

        public MyGame()
        {
            // Enable profiling
            //Profiler.EnableAll();

            GraphicsDeviceManager.PreferredBackBufferWidth = 800;
            GraphicsDeviceManager.PreferredBackBufferHeight = 480;
            GraphicsDeviceManager.PreferredDepthStencilFormat = PixelFormat.D24_UNorm_S8_UInt;
            GraphicsDeviceManager.DeviceCreationFlags = DeviceCreationFlags.Debug;
            GraphicsDeviceManager.PreferredGraphicsProfile = new[] { GraphicsProfile.Level_11_0 };

            StopOnFrameCount = -1;
            AutoLoadDefaultSettings = false;
        }

        protected override void Update(GameTime gameTime)
        {
            base.Update(gameTime);

            if (Input.IsKeyDown(Keys.Escape))
            {
                Exit();
            }

            if (gameTime.FrameCount == StopOnFrameCount)
            {
                Exit();
            }
        }

        protected override async Task LoadContent()
        {
            await base.LoadContent();

            spriteBatch = new SpriteBatch(GraphicsDevice);
            font = Asset.Load<SpriteFont>("Font");

            wireframeState = RasterizerState.New(GraphicsDevice, new RasterizerStateDescription(CullMode.Back) { FillMode = FillMode.Wireframe });

            var x = EffectSystem.FileProvider.RootPath;

            mainCamera = new Entity
            {
                new CameraComponent
                {
                    UseCustomAspectRatio = true,
                    AspectRatio = 8/4.8f,
                    FarClipPlane = 5,
                    NearClipPlane = 1,
                    VerticalFieldOfView = MathUtil.RadiansToDegrees(0.6f),
                    UseCustomViewMatrix = true,
                    ViewMatrix = Matrix.LookAtRH(new Vector3(2,1,2), new Vector3(), Vector3.UnitY),
                },
                new TransformComponent
                {
                    Position = new Vector3(2,1,2)
                }
            };

            CreatePipeline();

            var primitive = GeometricPrimitive.Teapot.New(GraphicsDevice);
            var drawPrimitive = primitive.ToMeshDraw();

            var material1 = Asset.Load<Material>("BasicMaterial");
            var material2 = Asset.Load<Material>("common_metal_0");

            var cube = new Entity("Cube") { new ModelComponent(new ProceduralModelDescriptor(new CubeProceduralModel { Size = new Vector3(1), MaterialInstance = { Material = material2 } }).GenerateModel(Services)) };
            var sphere = new Entity("Sphere") { new ModelComponent(new ProceduralModelDescriptor(new SphereProceduralModel { Diameter = 1, Tessellation = 5, MaterialInstance = { Material = material2 } }).GenerateModel(Services)) };

            /*
            var megalodon = new Entity { new ModelComponent { Model = Asset.Load<Model>("megalodon Model") } };
            megalodon.Transform.Position = new Vector3(0, -30f, -10f);

            var knight = new Entity { new ModelComponent { Model = Asset.Load<Model>("knight Model") } };
            knight.Transform.RotationEulerXYZ = new Vector3(-MathUtil.Pi / 2, MathUtil.Pi / 4, 0);
            knight.Transform.Position = new Vector3(0, -50f, 20f);
            knight.Transform.Scale = new Vector3(0.6f);
            */

            teapot = new Entity
            {
                new ModelComponent
                {
                    Model = new Model
                    {
                        material2,
                        new Mesh
                        {
                            Draw = drawPrimitive,
                            MaterialIndex = 0,
                        }
                    }
                },
                new TransformComponent()
            };

            /*
            CameraComponent = camera.Camera;
            Script.Add(camera);
            camera.Position = new Vector3(25, 45, 80);
            camera.SetTarget(currentEntity, true);
            */

            var ambientLight = new Entity("Ambient Light") { new LightComponent { Type = new LightAmbient(), Intensity = 1.0f } };

            //scene.AddChild(teapot);
            //scene.AddChild(cube);
            scene.AddChild(cube);
            scene.AddChild(mainCamera);
            scene.AddChild(ambientLight);

            /*
            var modelComponent = sphere.Get<ModelComponent>();
            modelComponent.Materials.Clear();
            for (int j = 0; j < modelComponent.Model.Materials.Count; j++)
                modelComponent.Materials.Add(material1);
            */

            // Add a custom script
            //if (rotateModel)
            //    Script.AddTask(GameScript1);

            //bool isWireframe = true;
            //material1.Parameters.Set(Effect.RasterizerStateKey, isWireframe ? wireframeState : GraphicsDevice.RasterizerStates.CullBack);

            //LightingKeys.EnableFixedAmbientLight(GraphicsDevice.Parameters, true);
            //GraphicsDevice.Parameters.Set(EnvironmentLightKeys.GetParameterKey(LightSimpleAmbientKeys.AmbientLight, 0), (Color3)Color.White);

        }

        private void CreatePipeline()
        {
            const int TargetWidth = 800;
            const int TargetHeight = 480;

            // Create render targets
            textures = new[]
            {
                Texture.New2D(GraphicsDevice, TargetWidth, TargetHeight, PixelFormat.R8G8B8A8_UNorm, TextureFlags.RenderTarget | TextureFlags.ShaderResource),
                Texture.New2D(GraphicsDevice, TargetWidth, TargetHeight, PixelFormat.R8G8B8A8_UNorm, TextureFlags.RenderTarget | TextureFlags.ShaderResource),
                Texture.New2D(GraphicsDevice, TargetWidth, TargetHeight, PixelFormat.R8G8B8A8_UNorm, TextureFlags.RenderTarget | TextureFlags.ShaderResource)
            };

            var depthBuffer = Texture.New2D(GraphicsDevice, TargetWidth, TargetHeight, PixelFormat.D24_UNorm_S8_UInt, TextureFlags.DepthStencil);

            var multipleRenderFrames = new DirectRenderFrameProvider(RenderFrame.FromTexture(textures, depthBuffer));

            // Setup the default rendering pipeline
            scene = new Scene
            {
                Settings =
                {
                    GraphicsCompositor = new SceneGraphicsCompositorLayers
                    {
                        Cameras = { mainCamera.Get<CameraComponent>() },
                        Master =
                        {
                            Renderers =
                            {
                                new ClearRenderFrameRenderer { Color = Color.Black, Output = multipleRenderFrames },
                                //new SceneCameraRenderer { Mode = new CameraRendererModeForward { ModelEffect = "MultipleRenderTargetsEffect" }, Output = multipleRenderFrames}, 
                                new SceneCameraRenderer { Mode = new CameraRendererModeForward { ModelEffect = "SoftInstanceEffect" }, Output = multipleRenderFrames}, 
                                new ClearRenderFrameRenderer { Output = new MasterRenderFrameProvider() },
                                new SceneDelegateRenderer(DisplayGBuffer) { Name = "DisplayGBuffer" },
                                //new SceneDelegateRenderer(PostCameraRendererDraw),
                            }
                        }
                    }
                }
            };

            SceneSystem.SceneInstance = new SceneInstance(this.Services, scene);
        }

        private void DisplayGBuffer(RenderContext context, RenderFrame frame)
        {
            GraphicsDevice.DrawTexture(textures[renderTargetToDisplayIndex]);
        }

        protected void PostCameraRendererDraw(RenderContext context, RenderFrame frame)
        {
            spriteBatch.Begin();
            spriteBatch.DrawString(font, "FPS: {0}".ToFormat(DrawTime.FramePerSecond), new Vector2(0, 20), Color.White);
            spriteBatch.End();
        }

    }
}
1 Like

Hmm… Fore some reason, looking at the source code of the project examples is something that eluded me. Didn’t really think of doing that, and kinda just accepted what was provided with the precompiled examples.
This will help me greatly and I really appreciate it :smile:

Your code snippet looks awfully similar to what I’m trying to pull off, although it differs quite a bit in some areas. It’ll be a good crutch while I try to figure this out.

Thanks man! :smile:

Just to be clear I’m referring not to any type of examples but the test cases. They are not documented but still can be instructive to see how the lower level bits function. For example here.

Ahh, makes more sense then. Cause I really couldn’t find much in the source code - this explains why.
Thanks for the heads up. I’ll dive deeper into the source.

[EDIT] So the above mentioned code example actually helped me to understand pretty much everything i was asking for. My problem was, that I tried to create (and draw) entities outside the game class. Apparently the solution was to treat my classes as scripts and add them to the game, and then access them from within the game class. If you ask me, thats a really arduous way to do it, but I’m not complaining, since I don’t understand a fraction of what happens behind the scene, and there probably is a good reason for why this is so.

Anyway, it works! Wohoo! One step closer to actually getting a project done with this engine.