Jump to content

klepto2

Developers
  • Posts

    854
  • Joined

  • Last visited

Posts posted by klepto2

  1. On 9/8/2023 at 5:57 PM, Josh said:

    Although IMgui is using immediate mode to render, you still have an advantage with the Ultra architecture because all the mesh generation code is executing in the game logic thread (I assume). That means the only rendering cost is the bandwidth of uploading the updated mesh to the GPU, and it sounds like that mesh data is not huge.

    You*re completely right. And with the new updates, it is even better :) The only thing is that in debug mode the culling takes a lot of time (10 - 13ms at average), which leads to a lower update even only one mesh is on screen. I remember that @SpiderPig has made the suggestion, to be able to disable the culling for certain cameras. And for orthographic cameras i fully agree with him. Normally in a 2d space it could be leed to the programmer, what to render and what not.

    Here are some progress screens of the current Statistics panel i have done with Dear ImGui:

    image.thumb.png.6320b665c6c1a4326fa35df48a279997.png

    image.thumb.png.b20f20fe6253b209f49e9c1b52b5e17f.png

    image.thumb.png.92c92dedf559a56f7637e0b13bb4d5e9.png

    image.thumb.png.9e5da8cb73c9c966f562f52023c3cdbb.png

    • Like 2
    • Upvote 1
  2. small experiment, i couldn't resist ;)

    image.thumb.gif.768a10902fea2bf5b51142ff5dba960b.gif

    #include "UltraEngine.h"
    #include "Components/Player/CameraControls.hpp"
    
    using namespace UltraEngine;
    
    const float G = 667.4f;
    const float planet_mass = 1000.0;
    
    Vec3 calculateForce(shared_ptr<Entity> planet, shared_ptr<Entity> target)
    {
        Vec3 direction = (planet->GetPosition() - target->GetPosition());
        float distance = direction.Length();
        if (distance == 0)
            return Vec3(0.0);
    
        float forceMagnitude = G * (target->GetMass() * planet_mass) / pow(distance, 2);
        Vec3 force = direction.Normalize() * forceMagnitude;
    
        return force;
    }
    
    int main(int argc, const char* argv[])
    {
        auto displays = GetDisplays();
        auto window = CreateWindow("Ultra Engine", 0, 0, 1280, 720, displays[0], WINDOW_CENTER | WINDOW_TITLEBAR);
        auto world = CreateWorld();
        auto framebuffer = CreateFramebuffer(window);
    
        auto camera = CreateCamera(world);
        camera->SetClearColor(0.125);
        camera->SetFov(70);
        camera->SetPosition(0, 10, -5);
    
        auto light = CreateDirectionalLight(world);
        light->SetRotation(35, 45, 0);
    
        world->SetGravity(0.0, 0.0, 0.0);
       
        auto planet = CreateSphere(world, 100.0, 128);
        planet->SetMass(FLT_MAX - 1);
        planet->SetPosition(0.0f, -100.0f, 0.0f);
        planet->SetMaterial(LoadMaterial("Materials\\Developer\\bluegrid.mat"));
    
        camera->SetPosition(0.0f, 0.0f, -3.0f);
        camera->AddComponent<CameraControls>();
        camera->SetDebugPhysicsMode(true);
    
        vector<shared_ptr<Entity>> boxes;
    
        auto box_main = CreateBox(world, 4.0);
        for(int i = 0; i < 200; i ++)
        { 
            auto box = box_main->Instantiate(world);
            box->SetMass(1.0);
            box->SetColor(1.0, 0.0, 0.0, 1.0);
            box->SetPosition(Random(120.0, -120.0), Random(120.0, -120.0), Random(120.0, -120.0));
            box->Move(0.0, -100.0, 0.0);
            boxes.push_back(box);
        }
    
        box_main = NULL;
    
        bool stage = 0;
        while (window->Closed() == false and window->KeyDown(KEY_ESCAPE) == false)
        {
            if (window->KeyHit(KEY_F2) == true) { camera->SetWireframe(!camera->GetWireframe()); }
            if (window->KeyHit(KEY_F3) == true) { camera->SetDebugPhysicsMode(!camera->GetDebugPhysicsMode()); }
    
            bool hit_space = window->KeyHit(KEY_SPACE);
            for each (auto box in boxes)
            {
                box->AddForce(calculateForce(planet, box));
    
                if (hit_space)
                {
                    Vec3 direction = (planet->GetPosition() - box->GetPosition()).Normalize();
                    box->AddForce(-direction * 2500.0);
                }
            }
    
            world->Update();
            world->Render(framebuffer);
        }
        return 0;
    }

     

    • Like 3
  3. If i remember correctly (@Josh correct me if i am wrong) , the gravity is applied to each object in the "NewtonBodySetForceAndTorqueCallback" which means, that while currently only a fixed gravity force is applied, this could be extended to add several "Attractors" or planets. So in theory you could simulate whole a whole universe with planets. and correct gravity. Maybe it would be an option to open up the Callback to override the force calculation for gravity? 

     

    Maybe something like this (more or less pseudo code):

    vec3 world_gravity_callback(vec3 pos)
    {
    	vec3 force = vec3(0);
    	for(auto p : planets)
    	{
    		float distanceToPlanet = calcDistance(pos);
    		force += calcForce(pos, distanceToPlanet); // as Newton engine uses real world units, you can actually use the real physics equations here
    	}
    	return force;
    }
    
    void cb_applyForce(const NewtonBody* const body, dFloat timestep, int threadIndex)
    {
      // Fetch user data and body position.
      UserData *mydata = (UserData*)NewtonBodyGetUserData(body);
      dFloat pos[4];
      NewtonBodyGetPosition(body, pos);
    
      // Apply gravity.
      dFloat force[3] = world->Gravity;
      if(world->HasGravityCallback) // if custom gravity is used calculate this instead of the fixed gravity
      {
    	force = world->CalculateGravityForPosition(pos); 
      }
      NewtonBodySetForce(body, force);
    }

     

    • Upvote 1
  4. One small addition: While this Ui-System is mainly for prototyping or debugging/dev things. It might come in handy, that it has a lot of lowlevel drawing functions. Which means, that when i have done the Rendertarget implementation, it could be possible to draw all kinds of 2d shapes, etc into a texture, which can then be rendered into a UltraEngine::Widget. Or you can combine these things and use the Widget system and ImGui together, e.g.:  for something like this: https://github.com/thedmd/imgui-node-editor

    • Like 1
  5. yes, but that would mean, that the floor itself needs a mass ;) otherwise the force methods will have no impact. 

    To make it physically correct, ( i believe i read that somewhere on the Newtondynamics forums back in Blitzmax times ) Would be to turn off gravity at all and for each object apply a force pointing to the center of your "planet" or maybe in this case just downwards. Of course in the case of a plane you need to conquer the forces applied to the plane to make it stable.

  6. Next Step:

    Finished the update to the "Dear ImGui"  "docking"-branch. The docking branch is a stable dev branch which contains the latest features as docking or multi-viewport systems. Docking is self explaining and I show it in the image below. Essentially multi-viewport means, that you can move windows out of your application, i haven't tested that (new windows will be generated, etc.)

    image.thumb.png.8ae24e00e59b07234d220af6fd8b5ba7.png

    image.thumb.png.723ca8832a6926f424b997861655615e.png

    Other remarkable milestones:

    • Rendering is more or less optimized. (With some dirty hacks ;))
      • I have refactored the Model/Mesh generation and updating method and the main loop still updates with 60fps even with high complex ui's
    • Multiple Fonts rendering is also possible 
    • Texture-System is in place.

    What needs to be done:

    • Replace the current Win32 driver with a native UltraEngine way
    • Add feature to allow render to texture and proper Input handling in this case
    • and of course optimization
    • Like 3
  7. I think the Physicsmatrix is only updated for the box itself, not taking the parent into account. You can see this in the first sample: While the box is moving  you can actually move through it even though the collision shape is displayed correct. When you move to the original Position the collison will occur.

  8. Maybe

    1 hour ago, Josh said:

    The reason you can see flickering when a mesh changes is because the old visibility set is still being used, the old mesh goes out of scope and is deleted, but the new mesh has not cycled through the culling update yet. It's a tricky problem.

    Maybe a flag to disable the Culling step for particular models/meshes might do the trick, in Orthographic projection (especially GUIs) it might be useful to always assume the mesh is visible as long as the model itself is not hidden.

     

    I was able to optimize the rendering to be flicker free and still performant. The previous flickering was due to a misconception on my side earlier. This is how Imgui provides the render data: 

    1. you notify ImGui that you will render a new frame
    2. you define the GUI 
    3. last you ask ImGui for the Renderdata
      1. The RenderData consists of Displaylists (for each window, foreground or background operation)
        1. The Displaylists itself contains the VertexBuffer and the indexbuffer and a commandlist 
        2. the commandlists specify which texture to use and which indices (by providing an Offset and an ElementCount)
      2. The Render data is ordered front to back representing the Z-Index.

    In my first implementations i assumed that I need a separate Model for each command in the commandlists, and i reused these models and reordered them every frame. The Problem: if in the previous frame a big window with lots of content was rendered focused, but the next frame a small and simple window was in front. The Model of the big window was resized to the smaller window and vice versa. 

    In the current implementation i only use one model per displaylist (which contains the _ownername) and add meshes according to the commandbuffer. So know i can identify which model belongs to a specific window (displaylist) and I only reuse this model for that particular window, when a window is closed I just hide the model and reuse it once the window is back.  If a window has fewer commands than before i clear it and rebuild als meshes needed. Otherwise i use the resize approach.

    Surprisingly, this eliminates all the flickering but still maintains a good performance.

    1 hour ago, Josh said:

    I encountered a similar problem with the Sprite::SetText method, and I solved it like this:

    1. If the new mesh data is bigger than the existing mesh, get rid of the existing mesh and create a new mesh.
    2. If the new mesh data is smaller or equal to the size of the existing mesh, copy the new mesh data to the existing mesh and set the extra vertices to all use position (0,0,0), so they will be invisible.

    This is what i do when the cmds are lower than the previously created meshes, but this only works by removing all meshes from the model and adding them again afterwards. There currently is no way to remove a specific mesh from a model.

  9. I have found a very hackish workaround and this should not be used, unless you know exactly what you do:

    class ModifiableMesh : public Mesh
    	{
    	public:
    		void ResizeVertexBuffer(shared_ptr<Mesh> base, int newSize)
    		{
    			static_cast<ModifiableMesh*>(base.get())->Mesh::m_vertices.resize(newSize);
    		};
    
    		void ResizeIndexBuffer(shared_ptr<Mesh> base, int newSize)
    		{
    			static_cast<ModifiableMesh*>(base.get())->Mesh::m_indices.resize(newSize);
    		};
    
    		int VertexSize(shared_ptr<Mesh> base)
    		{
    			return static_cast<ModifiableMesh*>(base.get())->Mesh::m_vertices.size();
    		};
    
    		int IndexSize(shared_ptr<Mesh> base)
    		{
    			return static_cast<ModifiableMesh*>(base.get())->Mesh::m_indices.size();
    		};
    	};
    
    	const shared_ptr<ModifiableMesh> ModMesh = std::make_shared<ModifiableMesh>();

    with this you can now resize the Vectors even when they are protected:

    int v_size = ModMesh->VertexSize(mesh);
    int i_size = ModMesh->IndexSize(mesh);
    
    int v_diff = v_size - newVertices.size() - INDEX_FLOAT_RANGE;
    int i_diff = i_size - newIndices.size() - INDEX_FLOAT_RANGE;
    
    
    if (abs(v_diff) > INDEX_FLOAT_RANGE)
    {
    	Print("Resizing (Vertex-Buffer): " + String(v_diff) + " | " +
    		String(v_size) + "-" + String(newVertices.size()));
    
    	ModMesh->ResizeVertexBuffer(mesh,newVertices.size() + INDEX_FLOAT_RANGE);
    }
    
    if (abs(i_diff) > INDEX_FLOAT_RANGE)//newIndices.size() > mesh->m_indices.size())
    {
    	Print("Resizing (Index-Buffer): " + String(i_diff) + " | " +
    		String(i_size) + "-" + String(newIndices.size()));
    	ModMesh->ResizeIndexBuffer(mesh,newIndices.size() + INDEX_FLOAT_RANGE);
    }
    mesh->Modify(newVertices);
    mesh->Modify(newIndices);

    Not really a good way, but it will do it as long as we have no ability to access the vectors directly.

    • Like 1
  10. 22 hours ago, klepto2 said:

    Most Vulkan or modern DirectX/OpenGL use one VertexBuffer for big models, and just separate Indexbuffers per material.

    Maybe I should clarify this a bit.  The Architecture of 3rd party libraries (including Dear ImGui, or most nvidia libs) normalliy provide a shared VertexStructure and then just an Indexbuffer for each "surface". To replicate this behaviour in UltraEngine, you currently have to create multiple meshs and recalculate the Vertexbuffer (I have done so for ImGui) or you need to duplicate the Vertexbuffer for each mesh.

    The above thing is a nice to have but not essential.

    What is most important for me is the abillity to resize the Vertices and Indices at will. While I understand that Ultraengine is layedout for high performance I believe that some parts should also be a bit flexible. (It doesn't cost to much if you just  need to resize every now and then and just upload small bits to the gpu instead of a big buffer where just a small bit is used anyway).

    • Upvote 1
  11. While the Mesh API is very fast and also flexible, I think it could be more flexible. 

    Background: 

    I am working on this: 

     

    and I have encountered a small issue with the mesh API. While you're able to modify vertices and indices on the fly it is not possible to change and update the size of these Vectors. I found a post where Josh describes this by design and i agree that from a performance point this is useful. On the other side it limits the usage of the class.  Lets take the "Dear ImGui' as a sample: 

    As this is an intermediate Gui system the layout is not calculated once, but instead each frame, This means that a window with small content has a small vertex and index count, but when you modify (eg: expand trees or add text) the mesh is regenerated and the vertex size and index buffer changes in size.

    Currently there are 2 ways to solve this: 

    1.  Recreate meshes on the fly (delete the old mesh, and create a new one with the new size)
      1. Surprisingly fast, but due to sync with the graphics card it leads to high flickering
    2. Create the meshes with hight vertex and inddex counts (lets say 10000 / 30000) and use the Mesh::Modify methods
      1. Works without flickering, but is extremly slow, not because of the modifcation itself, but the extreme size and unneeded uploads to the gpu.

    I have tested something which i assumed will not work, but surprisingly this worked flawless with nearly no impact:

    1. I modfied mesh.h and changed the protected modifier to public
      1. This gave me access to the actual Vectors for vertices and indices
    2. With access to these Vectors i can now resize them at will.
    3. I give them just a small bigger size (around 300 items) than needed to not perform resizing every frame
    4. After that i just use the Mesh::Modify methods

    Results:

    • with the recreation method i get around 150 to 180 fps, but flickering
    • with the preserved Size method i get 20 to 50 fps (depending on debug or release mode)
    • with the resizing method i get 180 to 240 fps 

    So maybe the resizing should be added optionaly to the Modify methods or maybe another DynamicMesh class.

    Another thing which might be useful for some  scenarios: 

    Shared VertexBuffers per Model or MeshGroup:

    Most Vulkan or modern DirectX/OpenGL use one VertexBuffer for big models, and just separate Indexbuffers per material.

    In Ultraengine this could be used to further increase the GPU-Bandwidth: I could Imagine that you define a Main-Mesh and this mesh can be assigned to other Meshs as parent. Then these Child meshes only need to provide indices and the vertexbuffer is the same as the the parent mesh.

    Use Cases: Lod, Voxel terrains etc.

     

    • Upvote 1
  12. Made a lot of progress today :)

    I have finished the basic ImGuiManager for UltraEngine (not yet commited) with some additional features: 

    • you can now register your own UltraEngine Textures to be used by ImGui
      • ImGuiManager::RegisterTexture(texture) will return an ImTextureID which can be used then by eg: ImGui::Image(...)
    • Multiple-Fonts are now supported + reload and recreation of the texture-atlas

    This is a small sample which i am using to test some features:

    #include "UltraEngine.h"
    #include "Components/Motion/Mover.hpp"
    #include "imgui-integration/ImGuiManager.h"
    
    using namespace UltraEngine;
    using namespace UltraEngine::ImGuiIntegration;
    using namespace std;
    
    #define MAX_FRAMES 100
    
    
    struct StatCounter
    {
        float data[MAX_FRAMES];
        float getMax() { return *max_element(data, data + MAX_FRAMES); }
        float getAvarge()
        {
            float average = 0.0f;
            int removeCounts = 0;
            for (int n = 0; n < IM_ARRAYSIZE(data); n++)
            {
                auto v = data[n];
                if (v < 0.0) {
                    v = 0.0;
                    removeCounts++;
                }
                average += v;
            }
            average /= (float)(MAX_FRAMES - removeCounts);
    
            return average;
        }
    };
    
    class ImDebugRenderer : public Object
    {
        shared_ptr<World> _world;
        StatCounter _fpsCounter;
        StatCounter _cullCounter;
        StatCounter _renderCounter;
        int _currentFrame = 0;
        bool _isOpen = true;
    
    public:
        ImDebugRenderer(shared_ptr<World> world) : _world(world)
        {
            _world->RecordStats(true);
        }
    
        void CollectStatistics()
        {
            _fpsCounter.data[_currentFrame] = _world->renderstats.framerate;
            _cullCounter.data[_currentFrame] = _world->renderstats.cullingtime;
            _renderCounter.data[_currentFrame] = _world->renderstats.rendertime;
        }
    
        void Render()
        {
            CollectStatistics();
    
            if (_isOpen)
            {
                ImGui::SetNextWindowPos(ImVec2(20, 20), ImGuiCond_Appearing);
                ImGui::SetNextWindowBgAlpha(0.5);
                //ImGui::SetNextWindowSize(ImVec2(350, 420), ImGuiCond_Appearing);
    
                ImGui::Begin("Debug", &_isOpen, ImGuiWindowFlags_AlwaysAutoResize);
    
                ImGui::PlotLines(("Framerate (" + String(_world->renderstats.framerate) + " fps )").c_str(), _fpsCounter.data, IM_ARRAYSIZE(_fpsCounter.data), _currentFrame, ("average : " + String(_fpsCounter.getAvarge())).c_str(), 0.0, _fpsCounter.getMax(), ImVec2(0, 80.0f));
                ImGui::PlotLines(("Culltime (" + String(_world->renderstats.cullingtime) + " ms)").c_str(), _cullCounter.data, IM_ARRAYSIZE(_cullCounter.data), _currentFrame, ("average : " + String(_cullCounter.getAvarge())).c_str(), 0.0, _cullCounter.getMax(), ImVec2(0, 80.0f));
                ImGui::PlotLines(("Rendertime (" + String(_world->renderstats.rendertime) + " ms)").c_str(), _renderCounter.data, IM_ARRAYSIZE(_renderCounter.data), _currentFrame, ("average : " + String(_renderCounter.getAvarge())).c_str(), 0.0, _renderCounter.getMax(), ImVec2(0, 80.0f));
    
                ImGui::Text("Cameras       : %d", _world->renderstats.cameras);
                ImGui::Text("Meshbatches   : %d", _world->renderstats.meshbatches);
                ImGui::Text("Pipelines     : %d", _world->renderstats.pipelines);
                ImGui::Text("Polygons      : %d", _world->renderstats.polygons);
                ImGui::Text("Vertices      : %d", _world->renderstats.vertices);
                ImGui::Text("Instances     : %d", _world->renderstats.instances);
                ImGui::Text("Shadows       : %d", _world->renderstats.shadows);
                ImGui::Text("Shadowpolygons: %d", _world->renderstats.shadowpolygons);
                ImGui::Text("VRam          : %d", _world->renderstats.vram);
    
                ImGui::End();
            }
    
            _currentFrame = (_currentFrame + 1) % MAX_FRAMES;
        }
    
        void Show() { _isOpen = true; };
    
        void Toggle() { _isOpen = !_isOpen; };
    };
    
    int main(int argc, const char* argv[])
    {
        auto plg = LoadPlugin("Plugins/FITextureLoader");
        auto plg2 = LoadPlugin("Plugins/KTX2TextureLoader");
        //Get the displays
        auto displays = GetDisplays();
    
        //Create a window
        auto window = CreateWindow("Ultra Engine", 0, 0, 1280, 720, displays[0], WINDOW_CENTER | WINDOW_TITLEBAR);
    
        //Create a world
        auto world = CreateWorld();
    
        //Create a framebuffer
        auto framebuffer = CreateFramebuffer(window);
    
        //Create a camera
        auto camera = CreateCamera(world);
        camera->SetPosition(0, 0, -1);
        camera->SetFov(70);
        camera->SetClearColor(0.125);
        camera->SetTessellation(4);
    
        //Create a light
        auto  light = CreateBoxLight(world);
        light->SetRange(-10, 10);
        light->SetRotation(35, 35, 0);
        light->SetColor(4);
    
    
        //Display material
        auto model = CreateCubeSphere(world, 0.5, 8, MESH_QUADS);
        auto mtl = LoadMaterial("Materials/rocks_ground_02.json");
        mtl->SetTessellation(true);
        mtl->SetDisplacement(0.075f);
        model->SetMaterial(mtl);
    
        //Entity component system
        auto component = model->AddComponent<Mover>();
        component->rotationspeed.y = 45;
    
        // Init the Manager
        auto imGuiManager = CreateImGuiManager(window, world);
        auto debugRenderer = make_shared<ImDebugRenderer>(world); // A custom class to render debug information for the world
    
        auto sampleTexture = mtl->GetTexture(0); // Get the diffuse Texture
        auto imGuiTexture = imGuiManager->RegisterTexture(sampleTexture); // Register the texture for usage with ImGui
    
        //Define some settings to control the camera and material settings
        bool camera_wireFrame = false;
        float tesselation_level = 4.0;
        Vec2 mtl_displacement = mtl->GetDisplacement();
    
        //Main loop
        while (window->Closed() == false and window->KeyDown(KEY_ESCAPE) == false)
        {
            //Begin the ui-definition
            imGuiManager->BeginFrame();
            debugRenderer->Render(); //render the debug panel (only when open)
    
            //Start rendering of the Settings
            ImGui::Begin("Settings", NULL, ImGuiWindowFlags_AlwaysAutoResize);
            ImGui::Checkbox("Wireframe", &camera_wireFrame);
            ImGui::SliderFloat("Tesselation-Level", &tesselation_level,0.0,32);
            ImGui::SliderFloat2("Displacement", &mtl_displacement[0], -1.0, 1.0);
            if (ImGui::Button("Toggle Debug-Window"))
            {
                debugRenderer->Toggle();
            }
            ImGui::End();
    
            //Render a window containing the UltraEngine texture
            ImGui::Begin("Texture", NULL, ImGuiWindowFlags_AlwaysAutoResize);
            ImGui::Image(imGuiTexture,ImVec2(128,128));
            ImGui::End();
    
            //Stop UI-Recording and update/create the models for UltraEngine
            imGuiManager->Sync();
    
            //Update the Settings modified by the UI
            camera->SetWireframe(camera_wireFrame);
            camera->SetTessellation(tesselation_level);
            mtl->SetDisplacement(mtl_displacement.x, mtl_displacement.y);
    
            world->Update();
            world->Render(framebuffer);
        }
        return 0;
    }

    imGui_ultra_engine_2.thumb.gif.8a8ee5ee11269a916fdf58fa9e3bbd4b.gif

     

    • Like 3
  13. I already posted a small screenshot in the Gallery showing an early proof of concept for integrating ImGui into UltraEngine.

    https://www.ultraengine.com/community/gallery/image/2676-imgui-integration-in-ultraengine/

    Now I have made some progress and finished a lot of the integration stuff.

    1. Ordered Rendering (not completely working, sometimes the order seems a bit off)
    2. Keyboard and MouseInput 
    3. Shader and Material stuff
    4. General-Rendering is working

    What is still to do:

    1.  Resolve some clipping errors
    2.  Make the whole integration more Ultra-like ;) not the c approach ImGui is normally using for initialisation
    3. Add multiple texture and font support (for images or framebuffer display)

     

    This small gif shows a small sample with the provided demo window and a custom debug window showing the renderstats:

    image.thumb.gif.2fcef9573096227c5db37ef0c75204a3.gif

    Here is a small code snippet showing the debug window code:

    #define MAX_FRAMES 100
    
    struct StatCounter
    {
        float data[MAX_FRAMES];
        float getMax() { return *max_element(data, data + MAX_FRAMES); }
        float getAvarge()  
        {
            float average = 0.0f;
            int removeCounts = 0;
            for (int n = 0; n < IM_ARRAYSIZE(data); n++)
            {
                auto v = data[n];
                if (v < 0.0) {
                    v = 0.0;
                    removeCounts++;
                }
                average += v;
            }
            average /= (float)(MAX_FRAMES - removeCounts);
    
            return average;
        }
    };
    
    class ImDebugRenderer : public Object
    {
        shared_ptr<World> _world;
        StatCounter _fpsCounter;
        StatCounter _cullCounter;
        StatCounter _renderCounter;
        int _currentFrame = 0;
        bool _isOpen = true;
    
    public:
        ImDebugRenderer(shared_ptr<World> world) : _world(world)
        {
            _world->RecordStats(true);
        }
    
        void CollectStatistics()
        {
            _fpsCounter.data[_currentFrame] = _world->renderstats.framerate;
            _cullCounter.data[_currentFrame] = _world->renderstats.cullingtime;
            _renderCounter.data[_currentFrame] = _world->renderstats.rendertime;
        }
    
        void Render()
        {
            CollectStatistics();
    
            if (_isOpen)
            {
                ImGui::SetNextWindowPos(ImVec2(20, 20), ImGuiCond_Appearing);
                ImGui::SetNextWindowBgAlpha(0.5);
                //ImGui::SetNextWindowSize(ImVec2(350, 420), ImGuiCond_Appearing);
    
                ImGui::Begin("Debug", &_isOpen, ImGuiWindowFlags_AlwaysAutoResize);
    
                ImGui::PlotLines(("Framerate (" + String(_world->renderstats.framerate) + " fps )").c_str(), _fpsCounter.data, IM_ARRAYSIZE(_fpsCounter.data), _currentFrame, ("average : " + String(_fpsCounter.getAvarge())).c_str(), 0.0, _fpsCounter.getMax(), ImVec2(0, 80.0f));
                ImGui::PlotLines(("Culltime (" + String(_world->renderstats.cullingtime) + " ms)").c_str(), _cullCounter.data, IM_ARRAYSIZE(_cullCounter.data), _currentFrame, ("average : " + String(_cullCounter.getAvarge())).c_str(), 0.0, _cullCounter.getMax(), ImVec2(0, 80.0f));
                ImGui::PlotLines(("Rendertime (" + String(_world->renderstats.rendertime) + " ms)").c_str(), _renderCounter.data, IM_ARRAYSIZE(_renderCounter.data), _currentFrame, ("average : " + String(_renderCounter.getAvarge())).c_str(), 0.0, _renderCounter.getMax(), ImVec2(0, 80.0f));
    
                ImGui::Text("Cameras       : %d", _world->renderstats.cameras);
                ImGui::Text("Meshbatches   : %d", _world->renderstats.meshbatches);
                ImGui::Text("Pipelines     : %d", _world->renderstats.pipelines);
                ImGui::Text("Polygons      : %d", _world->renderstats.polygons);
                ImGui::Text("Vertices      : %d", _world->renderstats.vertices);
                ImGui::Text("Instances     : %d", _world->renderstats.instances);
                ImGui::Text("Shadows       : %d", _world->renderstats.shadows);
                ImGui::Text("Shadowpolygons: %d", _world->renderstats.shadowpolygons);
                ImGui::Text("VRam          : %d", _world->renderstats.vram);
    
                ImGui::End();
            }
    
            _currentFrame = (_currentFrame + 1) % MAX_FRAMES;
        }
    
        void Show() { _isOpen = true; };
    };

    The setup is more or less the same as for the default GUI, you need an orthographic camera and a separate renderlayer. But then you can simply call:

     

    // This is how i imagine a more ultra-way of rendering the ImGui
    auto ui_manager = ImGuiUltraManager::Create(window, world, renderlayer); //Framebuffer will work as well
    
    void main()
    {
    	ui_manager->BeginFrame(); // Begins the frame-recording
    	debugWindow->Render(); // Add the commands to draw the debug window (code from above)
    	ui_manager->Render(); // Stops the frame-recording and prepares the models for rendering
    
    	world->Update();
    	world->Render(framebuffer,true);
    }

     

     

    • Like 2
    • Upvote 1
  14. I have the need to write to 2 colorattachments and i have set it up like this:

        "textures":
            [
                {
                    "size": [0.5, 0.5],
                    "format": 97
                },
                {
                    "size": [0.5, 0.5],
                    "format": 97
                }
            ],
            "subpasses":
            [
                {    
                    "samplers": ["DEPTH", "NORMAL", "PREVPASS"],
                    "colorAttachments": [0,1],
                    "shader":
                    {
                        "float32":
                        {
                            "fragment": "Shaders/Environment/environment_clouds.frag.spv"
                        }
                    }
                },

    in the shader file i have something like this (very simplfied):

    layout(location = 0) out vec4 outColor[2];
    
    void main()
    {
    	outColor[0] = vec4(1.0,0.0,0.0,1.0);
    	outColor[1] = vec4(0.0,1.0,0.0,1.0);
    }

    only the red value is stored to the texture, the second one is still unmodified. Ate multiple attachemnts currently not supported?

  15. Hi, 

    while i am working on my "Environement / Atmosphere / Weather" - system i'm working a lot with the terrain system and found some bugs and missing functionality from the API.

    Bugs:

    The layer system works only when you first assign a material to the terrain. In my opinion,  this should not be required and a default terrain material should be set until it is overridden by the user (later planetary implementation or streaming terrain).

    On further investigation this was just a one time bug for me, but i let it here for reference.

    #include "UltraEngine.h"
    #include "Components/CameraControls.hpp"
    
    using namespace UltraEngine;
    
    int main(int argc, const char* argv[])
    {
        //Get the display list
        auto displays = GetDisplays();
    
        //Create a window
        auto window = CreateWindow("Ultra Engine", 0, 0, 1280, 720, displays[0], WINDOW_CENTER | WINDOW_TITLEBAR);
    
        //Create a world
        auto world = CreateWorld();
        world->SetAmbientLight(0);
    
        //Create a framebuffer
        auto framebuffer = CreateFramebuffer(window);
    
        //Create a camera
        auto camera = CreateCamera(world);
        camera->SetFov(70);
        camera->SetPosition(0, 50, 0);
        camera->SetRotation(45, 0, 0);
        camera->SetClearColor(0.125);
    
        //Sunlight
        auto light = CreateDirectionalLight(world);
        light->SetRotation(45, 35, 0);
        light->SetColor(2);
    
        //Create terrain
        auto terrain = CreateTerrain(world, 512);
        terrain->LoadHeightmap("https://raw.githubusercontent.com/UltraEngine/Documentation/master/Assets/Terrain/512.r16");
        terrain->SetScale(1, 100, 1);
    
        //Create base material
        auto ground = CreateMaterial();
        auto diffusemap = LoadTexture("https://raw.githubusercontent.com/UltraEngine/Documentation/master/Assets/Materials/Ground/river_small_rocks_diff_4k.dds");
        auto normalmap = LoadTexture("https://raw.githubusercontent.com/UltraEngine/Documentation/master/Assets/Materials/Ground/river_small_rocks_nor_gl_4k.dds");
        ground->SetTexture(diffusemap, TEXTURE_DIFFUSE);
        ground->SetTexture(normalmap, TEXTURE_NORMAL);
    
        terrain->SetMaterial(ground); // Comment out this line and the whole terrain will be black
    
        //Create paint material
        auto rocks = CreateMaterial();
        diffusemap = LoadTexture("https://raw.githubusercontent.com/UltraEngine/Documentation/master/Assets/Materials/Ground/Rocks_Dirt_Ground_2k.dds");
        normalmap = LoadTexture("https://raw.githubusercontent.com/UltraEngine/Documentation/master/Assets/Materials/Ground/Rocks_Dirt_Ground_2k_dot3.dds");
        auto dispmap = LoadTexture("https://raw.githubusercontent.com/UltraEngine/Documentation/master/Assets/Materials/Ground/Rocks_Dirt_Ground_2k_disp.dds");
        rocks->SetTexture(diffusemap, TEXTURE_DIFFUSE);
        rocks->SetTexture(normalmap, TEXTURE_NORMAL);
        rocks->SetTexture(dispmap, TEXTURE_DISPLACEMENT);
    
        int layer = terrain->AddLayer(rocks);
        terrain->SetLayerTextureMapping(layer, TERRAINMAPPING_VERTICAL);
        //Apply material based on terrain slope
        for (int x = 0; x < terrain->resolution.x; ++x)
        {
            for (int y = 0; y < terrain->resolution.y; ++y)
            {
                float slope = terrain->GetSlope(x, y);
                if (slope > 15.0f)
                {
                    float wt = Min((slope - 15.0f) / 10.0f, 1.0f);
                    terrain->SetLayerWeight(layer,x, y, wt);
                }
            }
        }
    
        //Camera controls
        camera->AddComponent<CameraControls>();
    
        //Main loop
        while (window->Closed() == false and window->KeyDown(KEY_ESCAPE) == false)
        {
            world->Update();
            world->Render(framebuffer);
        }
        return 0;
    }

    Missing methods: 

    1. The Fill method is commented out in the header file and my should maybe renamed to FillLayer(int layerid)

    2. The methods to set and get the layer scaling are missing, or better: they are in the protected area.

    3 All get Methods for LayerProperties should be provided. Currently it is not possible to access things like the Slope / height constraints of a specific layer.

    • Like 1
    • Upvote 1
×
×
  • Create New...