🎉 Celebrating 25 Years of GameDev.net! 🎉

Not many can claim 25 years on the Internet! Join us in celebrating this milestone. Learn more about our history, and thank you for being a part of our community!

Integrate Level Editor on Top of Game Engine

Started by
11 comments, last by Juliean 3 years, 10 months ago

Hello,

Im trying to create my own Game Engine. Currently I'm thinking about how I could integrate my Editor into the game and face some troubles.

My current approach for the general architecture of the engine is, that I have a main game class, this game class holds some systems, like a graphics system, a physics system etc. I then have an entity component system, that can communicate with this system, for example a renderable component sends its material and vertex data to the graphics system and the graphics system then produces the image.

For creating the editor my idea was to subclass my main class and then add functionality to display some editor UI. For creating the UI, I thought about using DearIMGui.

Now to my problem: I want to use IMGui with the Docking branch, so that the UI does not overlay the actual game. For this I need my graphics system to render the complete scene into an texture, this texture gets then displayed with an ImGui::Image. Currently I have no Idea how I should integrate this cleanly. The only idea that I have is that I somewhat change my renderer to include an ImGui pass. I don't want ImGui to become a main dependency of my engine. I just want to use it in the editor.

I would be really happy to hear from other people how they solve this.

Advertisement

There are many possible solutions, but for me personally I've done it like this:

The entire render-path can be represented by instantiating a RenderView-class. I can tell this how large the output should be, and whether to render to texture or to the backbuffer. With that design, you would instantiate the RenderView itself wherever you render your IMGui, and then fetch the target-texture and render that to the gui. There are many other benefits of having an approach like that; ie. being able to have multiple scene-views; rendering previews etc…

@Juliean Thanks for your reply. Your approach sounds interesting. So does your RenderView class have some RenderPath object. This RenderPath object has in itself the various RenderPasses coded? So the RenderView can then tell the RenderPath, that it should render it's result to a backbuffer or to a texture. You would then do the actual ImGui rendering in the RenderView class after the RenderPath finished?

One question remains: How would you sent the actual imgui draw data to the graphics system?

Could elaborate a little bit how your actual classes look like and how they interact with each other?

flexw said:
@Juliean Thanks for your reply. Your approach sounds interesting. So does your RenderView class have some RenderPath object. This RenderPath object has in itself the various RenderPasses coded? So the RenderView can then tell the RenderPath, that it should render it's result to a backbuffer or to a texture. You would then do the actual ImGui rendering in the RenderView class after the RenderPath finished?

One question remains: How would you sent the actual imgui draw data to the graphics system?

Could elaborate a little bit how your actual classes look like and how they interact with each other?

I don't think my exact classes are that helpful or interesting, as I'm using a node-based frontend for configuring the render"path" as you call it. So there's lots of magic auto-setup/reflection-code in it, but I'll try to give you the gist of it anyway.

Let's remember that in my approach, RenderView is just a renderview, and not the renderivew. Just like a game-loop, there is a render-loop that is asking the whole engine/systems for submitting their render-data. So the way that you describe how IMGui works I would have your editor be implemented so that there is a system that renders IMGui, which also contains a RenderView. That render-view is composed of all the RenderPath that make up the game. Then your imgui-system would use that render-output to display the game in an IMGui-image, and then simply render itself afterwards. Here's some pseudo-code:

void IMGuiSystem::Init()
{
    auto [pGameView, pRenderTarget] = RenderView::CreateToTexture(...);
    
    m_pGameView = std::move(pGameView);
    // TODO: use render-target texture to store in IMGui-image
}

void IMGuiSystem::Render()
{
    m_pGameView->Render();
    
    // TODO: render IMGUI    
}

I think this would be the simpliest approach; as neigther RenderView nor RenderPasses have some special privileg, in my system you could code everything directly (or you use a separate RenderPass if that appears cleaner to you).

Alternatively I have the option to add custom renderPasses to a RenderView view Renderview::AddCustomPass, but that is rather a legacy-thing that is surpasses by simple composite-pattern like rendering of everything.

(Also if you still would like more detail please just ask for it. I'm trying to first give an overview of everything before going into 2 pages of details ?�

Have a configurable render graph is always a good idea. I've seen so many bad solutions like Unity's where you have to add a render texture to the camera to be able to do another processing step on it >.<

The approach of our custom engine is going even a step deeper. We also have a node graph that you can edit in the editor with some sort of live preview. After you're done, it gets compiled from our build tool into C++ code that is integrated into the engine assembly for less memory usage and more performance than a simple connected graph of nodes can offer

@Juliean Thank you for taking the time to explain these things to me. So for me this sounds like this: You have some systems. They have a Render() method. The GraphicsSystem / Renderer calls these methods one after one in a loop. In these methods the Systems submit render data. A RenderView is just like a window into the Renderer, where the results from the renderer get written to. What I don't get at the moment is how the Render() method actually works. Does it work like this, that the Systems Render methods are somewhat render passes and when the IMGui::Renderer Method is called, the render view just contains everything rendered from the systems that where called before? But I think I don't get it because when my thoughts were right, the m_pGameView→Render() call would make no sense.

@Juliean Thank you for taking the time to explain these things to me. So for me this sounds like this: You have some systems. They have a Render() method. The GraphicsSystem / Renderer calls these methods one after one in a loop. In these methods the Systems submit render data. A RenderView is just like a window into the Renderer, where the results from the renderer get written to. What I don't get at the moment is how the Render() method actually works. Does it work like this, that the Systems Render methods are somewhat render passes and when the IMGui::Renderer Method is called, the render view just contains everything rendered from the systems that where called before? But I think I don't get it because when my thoughts were right, the m_pGameView→Render() call would make no sense.

flexw said:
Thank you for taking the time to explain these things to me. So for me this sounds like this: You have some systems. They have a Render() method. The GraphicsSystem / Renderer calls these methods one after one in a loop. In these methods the Systems submit render data. A RenderView is just like a window into the Renderer, where the results from the renderer get written to. What I don't get at the moment is how the Render() method actually works. Does it work like this, that the Systems Render methods are somewhat render passes and when the IMGui::Renderer Method is called, the render view just contains everything rendered from the systems that where called before? But I think I don't get it because when my thoughts were right, the m_pGameView→Render() call would make no sense.

When RenderView::Render is called, it actually processes the entire structure of render-passes that make up the game. Models, Lights, Postprocessing etc… So a RenderView represents the entire game-world in its displayable state. So from the perspective of the engine, when the systems Render()-methods are invoked, its entirely up to the systems what they do. There is no predefined structure that does anything and where anything gets put into, its just what the systems themselv then do (so w/o any custom system code, render would just be black after Present().

So to understand that, you have to think of that code as more imperative/composition-based. You render what you need. A RenderView itself is just one possible configuration of things that get rendered, specialised in representing the games world. So when you create a RenderView and call Render in the RenderLoop, it processes all its RenderPasses and produces a RenderTexture (or alternatively, backbuffer-image). Obviously in the player-application, there is just one RenderView that is configured to output to backbuffer; but in the editor you can have a number of those configured to show different angles, scenes etc… and use their target RenderTextures for further processing (to display in certain windows, draw stuff on top etc…).

If thats still not enough, I could try giving you some more real-world examples ?

Ah I think I'm getting it. Sounds great. Thanks a lot. This has nothing to do with the editor question, but say for example I wanted to have a forward and a deferred renderer, I could implement each of them as a render view? So I could easily swap between them.

flexw said:
Ah I think I'm getting it. Sounds great. Thanks a lot. This has nothing to do with the editor question, but say for example I wanted to have a forward and a deferred renderer, I could implement each of them as a render view? So I could easily swap between them.

Yes, pretty much. In my node-based system deferred and forward are even easier to express, kind of like you would have a condition in regular code (unfortunately I don't have working picture of that right now). But in a code-based approach, you could make a different configuration/type of RenderView for that.

This topic is closed to new replies.

Advertisement