Jump to content

Ultra Engine testing


Josh
 Share

Recommended Posts

I know, but for the scattering i might need a bit more ;) 

According to some papers i will split up the data into PushConstants and uniform buffers, where the push constants will contain the repeatedly changing data and the uniform buffer maybe the more static once.

UniformBuffer was just the beginning, i will try to integrate as much buffer types as possible, this will give a lot more freedom. 

  • Intel® Core™ i7-8550U @ 1.80 Ghz 
  • 16GB RAM 
  • INTEL UHD Graphics 620
  • Windows 10 Pro 64-Bit-Version
Link to comment
Share on other sites

 

Do you feel like this approach is more modular than what you did with Leadwerks? Does this allow more possibilities?

My job is to make tools you love, with the features you want, and performance you can't live without.

Link to comment
Share on other sites

Yes, to get the ComputeShaders (mainly for fft or the atmospheric scattering) was very tricky to implement in leadwerks, some missing things like native 3d textures. UltraEngine already has eveything we need, and the latest additions to hook into it the rendering pipeline and get easy access to some underlying types makes it much easier. 

On the other hand, you need to think a bit different than as in Leadwrks Engine. eg: Textures etc. are really only initialised once they are rendered. But thats nice, just needs a different thinking and makes it ultra fast.

  • Intel® Core™ i7-8550U @ 1.80 Ghz 
  • 16GB RAM 
  • INTEL UHD Graphics 620
  • Windows 10 Pro 64-Bit-Version
Link to comment
Share on other sites

1 hour ago, Josh said:

Another update:

  • Initializes using Vulkan 1.3
  • TextArea now working in 3D graphics
  • CreateInterface() overload added a font parameter

Nice, I don't expect that you got to fixing buttons, but if this also fixes text areas, I should be able to get a working developer console now.

Cyclone - Ultra Game System - Component PreprocessorTex2TGA - Darkness Awaits Template (Leadwerks)

If you like my work, consider supporting me on Patreon!

Link to comment
Share on other sites

I'm sure you'll figure it out. More interested in functionality right now.

Also, for hiding UI elements, should I be hiding the interface or the camera? I was hiding the camera to make the menu stop drawing which is probably why it's slow to update first time around.

Cyclone - Ultra Game System - Component PreprocessorTex2TGA - Darkness Awaits Template (Leadwerks)

If you like my work, consider supporting me on Patreon!

Link to comment
Share on other sites

I would hide the camera, unless there are other 2D elements you want to draw.

I think the UI might be responding to a bunch of piled up window paint events and is re-creating a lot of sprites at startup but I'm not sure yet.

  • Like 1

My job is to make tools you love, with the features you want, and performance you can't live without.

Link to comment
Share on other sites

Just updated and all the contents of the Include directory were gone - not sure about other directories.  This could be because I deleted tar.exe and am using the older default for W10?  There were also two updates available, one right after the other.  After uninstalling then reinstalling there is no Include folder now. :huh:

Link to comment
Share on other sites

Test program shows a problem with the slider:
 

#include "UltraEngine.h"

using namespace UltraEngine;

int main(int argc, const char* argv[])
{
    //Get the displays
    auto displays = GetDisplays();

    //Create a window
    auto window = CreateWindow("Ultra Engine", 0, 0, 800, 600, 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->SetClearColor(0.125);
    camera->SetRotation(45, 0, 0);
    camera->Move(0, 0, -10);
    camera->SetFOV(70);

    auto uicam = CreateCamera(world, PROJECTION_ORTHOGRAPHIC);
    uicam->SetRenderLayers(RENDERLAYER_1);
    uicam->SetPosition(framebuffer->size.x / 2, framebuffer->size.y / 2);
    auto ui = CreateInterface(world, LoadFont("Fonts/arial.ttf"), framebuffer->size);
    ui->SetRenderLayers(RENDERLAYER_1);

    auto sz = ui->root->ClientSize();
    auto textarea = CreateTextArea(10, 10, sz.x - 20, sz.y - 20, ui->root, TEXTAREA_WORDWRAP);

    WString s;

    for (int n = 0; n < 300; ++n)
    {
        s += String(n) + "\n";
    }

    textarea->SetLayout(1, 1, 1, 1);

    //ui->SetScale(displays[0]->scale);
    textarea->SetText(s);

    //Main loop
    while (window->Closed() == false and window->KeyDown(KEY_ESCAPE) == false)
    {
        while (PeekEvent())
        {
            ui->ProcessEvent(WaitEvent());
        }

        world->Update();
        world->Render(framebuffer);
    }
    return 0;
}

 

My job is to make tools you love, with the features you want, and performance you can't live without.

Link to comment
Share on other sites

Same problem in a simpler example:

#include "UltraEngine.h"

using namespace UltraEngine;

int main(int argc, const char* argv[])
{
    //Get the displays
    auto displays = GetDisplays();

    //Create a window
    auto window = CreateWindow("Ultra Engine", 0, 0, 800, 600, displays[0]);

    //Create User Interface
    auto ui = CreateInterface(window);

    //Create widget
    auto sz = ui->root->ClientSize();
    auto slider = CreateSlider(10, 10, 200, 30, ui->root, SLIDER_SCROLLBAR);

    while (true)
    {
        const Event ev = WaitEvent();
        switch (ev.id)
        {
        case EVENT_WIDGETACTION:
            Print("Widget action: " + String(ev.data));
            break;
        case EVENT_WINDOWCLOSE:
            return 0;
            break;
        }
    }
    return 0;
}

 

My job is to make tools you love, with the features you want, and performance you can't live without.

Link to comment
Share on other sites

New update:

  • GUI is now 100% working correctly in Vulkan, as far as I know
  • Fixed terrain paint example
  • Sprites are now using the PBR shader family by default. If you want to use the unlit shader family, you need to create a material and assign the shader family to it.

My list of things to do:

  • Directional light shadows
  • GI for dynamic objects
  • Motion blur
  • VR integration
  • Like 2

My job is to make tools you love, with the features you want, and performance you can't live without.

Link to comment
Share on other sites

If you click the "run install scripts manually" options in the settings you can run the bat file yourself and see any info it prints out.

My job is to make tools you love, with the features you want, and performance you can't live without.

Link to comment
Share on other sites

Still getting the clipping issue in the 3D render but the first draw call seems to be better. 

FYI, unless I'm not doing this correctly, adding in a 3D viewport seems to slow everything down. Also, I can't seem to resize the viewport after the window size has been changed.

#include "UltraEngine.h"
#include "ComponentSystem.h"

using namespace UltraEngine;
const int TOOLBARHEIGHT = 48;
const int STATUSBARHEIGHT = 32;
const int SIDEPANELWIDTH = 300;
const int CONSOLEHEIGHT = 120;

int main(int argc, const char* argv[])
{
    //Get displays
    auto displays = GetDisplays();

    //Create window
    auto mainwindow = CreateWindow("Ultra App Kit", 0, 0, 1024, 768, displays[0], WINDOW_CENTER | WINDOW_TITLEBAR | WINDOW_RESIZABLE);
    mainwindow->SetMinSize(800, 600);

    //Create user interface
    auto ui = CreateInterface(mainwindow);
    iVec2 sz = ui->root->ClientSize();

    //-------------------------------------------------------
    // Create main menu
    //-------------------------------------------------------

    auto mainmenu = CreateMenu("", ui->root);

    //File menu
    auto menu_file = CreateMenu("File", mainmenu);
    CreateMenu("New", menu_file);
    CreateMenu("", menu_file);
    auto menu_open = CreateMenu("Open", menu_file);
    auto menu_save = CreateMenu("Save", menu_file);
    auto menu_saveas = CreateMenu("Save as...", menu_file);
    CreateMenu("", menu_file);
    auto menu_recentfiles = CreateMenu("Recent files", menu_file);
    array<shared_ptr<Widget>, 10> menu_recentfile;
    for (int n = 0; n < menu_recentfile.size(); ++n)
    {
        menu_recentfile[n] = CreateMenu("Recent file " + String(n + 1), menu_recentfiles);
    }
    CreateMenu("", menu_file);
    auto menu_exit = CreateMenu("Exit", menu_file);

    //Edit menu
    auto menu_edit = CreateMenu("Edit", mainmenu);
    CreateMenu("Undo", menu_edit);
    CreateMenu("Redo", menu_edit);
    CreateMenu("", menu_edit);
    CreateMenu("Cut", menu_edit);
    CreateMenu("Copy", menu_edit);
    CreateMenu("Past", menu_edit);
    CreateMenu("", menu_edit);
    CreateMenu("Select all", menu_edit);
    CreateMenu("Select none", menu_edit);
    CreateMenu("Invert selection", menu_edit);

    //View menu
    auto menu_view = CreateMenu("View", mainmenu);
    auto menu_perspective = CreateMenu("Perspective", menu_view);
    auto menu_top = CreateMenu("XZ - Top", menu_view);
    auto menu_side = CreateMenu("XZ - Side", menu_view);
    auto menu_front = CreateMenu("XY - Front", menu_view);
    menu_perspective->SetState(true);

    //Tools menu
    auto menu_tools = CreateMenu("Tools", mainmenu);
    auto menu_options = CreateMenu("Options", menu_tools);

    //Help menu
    auto menu_help = CreateMenu("Help", mainmenu);
    auto menu_helpcontents = CreateMenu("Help Contents", menu_help);
    auto menu_about = CreateMenu("About", menu_help);

    //-------------------------------------------------------
    // Create toolbar
    //-------------------------------------------------------

    auto toolbar = CreatePanel(0, mainmenu->size.y, sz.x, TOOLBARHEIGHT, ui->root);
    toolbar->SetLayout(1, 1, 1, 0);
    int x = 4, y = 4;

    auto toolbarbutton_open = CreateButton("", x, y, TOOLBARHEIGHT - 8, TOOLBARHEIGHT - 8, toolbar, BUTTON_TOOLBAR);
    toolbarbutton_open->SetFontScale(2);
    toolbarbutton_open->SetIcon(LoadIcon("https://raw.githubusercontent.com/Leadwerks/Documentation/private/Assets/Icons/open.svg"));
    x += TOOLBARHEIGHT;

    auto toolbarbutton_save = CreateButton("", x, y, TOOLBARHEIGHT - 8, TOOLBARHEIGHT - 8, toolbar, BUTTON_TOOLBAR);
    toolbarbutton_save->SetFontScale(2);
    toolbarbutton_save->SetIcon(LoadIcon("https://raw.githubusercontent.com/Leadwerks/Documentation/private/Assets/Icons/save.svg"));
    x += TOOLBARHEIGHT;

    auto toolbarbutton_options = CreateButton("", x, y, TOOLBARHEIGHT - 8, TOOLBARHEIGHT - 8, toolbar, BUTTON_TOOLBAR);
    toolbarbutton_options->SetFontScale(2);
    toolbarbutton_options->SetIcon(LoadIcon("https://raw.githubusercontent.com/Leadwerks/Documentation/private/Assets/Icons/settings.svg"));
    x += TOOLBARHEIGHT;

    auto toolbarbutton_help = CreateButton("", x, y, TOOLBARHEIGHT - 8, TOOLBARHEIGHT - 8, toolbar, BUTTON_TOOLBAR);
    toolbarbutton_help->SetIcon(LoadIcon("https://raw.githubusercontent.com/Leadwerks/Documentation/private/Assets/Icons/help.svg"));
    toolbarbutton_help->SetFontScale(2);

    //-------------------------------------------------------
    // Create status bar
    //-------------------------------------------------------

    auto statusbar = CreatePanel(0, sz.y - STATUSBARHEIGHT, sz.x, STATUSBARHEIGHT, ui->root);
    statusbar->SetLayout(1, 1, 0, 1);
    auto statusbarlabel_view = CreateLabel("Perspective", 4, 0, 300, statusbar->size.y, statusbar, LABEL_LEFT | LABEL_MIDDLE);

    //-------------------------------------------------------
    // Create main panel
    //-------------------------------------------------------

    auto mainpanel = CreatePanel(0, toolbar->position.y + toolbar->size.y, sz.x, sz.y - toolbar->size.y - toolbar->position.y - statusbar->size.y, ui->root);
    mainpanel->SetLayout(1, 1, 1, 1);
    sz = mainpanel->ClientSize();

    //Create console
    auto console = CreateTextArea(4, mainpanel->size.y - CONSOLEHEIGHT, mainpanel->size.x - SIDEPANELWIDTH - 8, CONSOLEHEIGHT - 28 - 4, mainpanel);
    console->SetLayout(1, 1, 0, 1);
    auto widget_input = CreateTextField(4, mainpanel->size.y - 28, mainpanel->size.x - SIDEPANELWIDTH - 8, 28, mainpanel, TEXTFIELD_ENTERKEYACTIONEVENT);
    widget_input->SetLayout(1, 1, 0, 1);

    //Main viewport
    auto mainviewport = CreatePanel(4, 4, mainpanel->size.x - SIDEPANELWIDTH - 8, mainpanel->size.y - 8 - CONSOLEHEIGHT, mainpanel, PANEL_BORDER);
    mainviewport->SetLayout(1, 1, 1, 1);
    mainviewport->SetColor(0, 0, 0);

    auto viewport = CreateWindow("", mainviewport->GetPosition(true).x, mainviewport->GetPosition(true).y, mainviewport->GetSize().x, mainviewport->GetSize().y, mainwindow, WINDOW_CHILD | WINDOW_CLIENTCOORDS);

    //Create a world
    auto world = CreateWorld();

    //Create a framebuffer
    auto framebuffer = CreateFramebuffer(viewport);

    //Create a camera
    auto camera = CreateCamera(world);
    camera->SetClearColor(0.125);
    camera->SetFOV(70);
    camera->SetPosition(0, 0, -3);

    //Create a light
    auto light = CreateLight(world, LIGHT_DIRECTIONAL);
    light->SetRotation(35, 45, 0);

    //Create a box
    auto box = CreateBox(world);


    //-------------------------------------------------------
    // Create side panel
    //-------------------------------------------------------   

    auto sidepanel = CreatePanel(sz.x - SIDEPANELWIDTH, 0, SIDEPANELWIDTH, sz.y, mainpanel);
    sidepanel->SetLayout(0, 1, 1, 1);
    auto tabber = CreateTabber(0, 0, SIDEPANELWIDTH, sz.y, sidepanel);
    tabber->SetLayout(1, 1, 1, 1);
    tabber->AddItem("Objects", true);
    tabber->AddItem("Scene");

    //Object panel
    sz = tabber->ClientSize();
    auto objectpanel = CreatePanel(0, 0, sz.x, sz.y, tabber);
    objectpanel->SetLayout(1, 1, 1, 1);
    //tabber->items[0].extra = objectpanel;

    //Scene panel
    auto scenepanel = CreatePanel(0, 0, sz.x, sz.y, tabber);
    scenepanel->SetHidden(true);
    scenepanel->SetLayout(1, 1, 1, 1);
    //tabber->items[1].extra = scenepanel;

    x = 8;
    y = 12;

    CreateLabel("Category:", x, y, 200, 30, objectpanel);
    y += 24;
    auto objectcategorybox = CreateComboBox(x, y, sz.x - x * 2, 30, objectpanel);
    objectcategorybox->SetLayout(1, 1, 1, 0);
    objectcategorybox->AddItem("Primitives", true);
    objectcategorybox->AddItem("Extended primitives");
    objectcategorybox->AddItem("Cameras");
    objectcategorybox->AddItem("Lights");
    objectcategorybox->AddItem("Splines");
    y += 44;

    CreateLabel("Object:", x, y, 200, 30, objectpanel);
    y += 24;
    auto objectbox = CreateComboBox(x, y, sz.x - x * 2, 30, objectpanel);
    objectbox->SetLayout(1, 1, 1, 0);
    objectbox->AddItem("Box", true);
    objectbox->AddItem("Wedge");
    objectbox->AddItem("Cylinder");
    objectbox->AddItem("Sphere");
    y += 44;

    x = 80;
    CreateButton("Create", x, y, sz.x - 2 * x, 28, objectpanel);

    x = 8;
    y = 12;
    auto scenebrowser = CreateTreeView(x, y, sz.x - 2 * x, 400 - y, scenepanel);
    scenebrowser->SetLayout(1, 1, 1, 1);
    auto node = scenebrowser->root->AddNode("Scene");
    node->Expand();
    node->AddNode("Box 1");
    node->AddNode("Box 2");
    node->AddNode("Box 3");
    y += scenebrowser->size.y + x;

    auto propertiespanel = CreatePanel(x, y, sz.x, sz.y - y, scenepanel);
    propertiespanel->SetLayout(1, 1, 0, 1);

    y = 8;
    CreateLabel("Name:", x, y + 4, 60, 30, propertiespanel);
    auto widget_name = CreateTextField(x * 2 + 60, y, sz.x - 4 * x - 60, 30, propertiespanel);
    widget_name->SetText("Box 1");
    y += 40;

    CreateLabel("Value:", x, y + 4, 60, 30, propertiespanel);
    CreateSlider(x * 2 + 60, y, sz.x - 4 * x - 60, 30, propertiespanel, SLIDER_HORIZONTAL | SLIDER_TRACKBAR);
    y += 40;

    //-------------------------------------------------------
    // Options window
    //-------------------------------------------------------

    auto optionswindow = CreateWindow("Options", 0, 0, 400, 500, mainwindow, WINDOW_HIDDEN | WINDOW_TITLEBAR | WINDOW_CENTER);
    auto optionsui = CreateInterface(optionswindow);
    sz = optionsui->root->ClientSize();

    auto button_option1 = CreateButton("Option 1", 12, 12, 300, 30, optionsui->root, BUTTON_CHECKBOX);
    button_option1->SetState(WIDGETSTATE_SELECTED);
    auto button_option2 = CreateButton("Option 2", 12, 12 + 32, 300, 30, optionsui->root, BUTTON_RADIO);
    button_option2->SetState(WIDGETSTATE_SELECTED);
    auto button_option3 = CreateButton("Option 3", 12, 12 + 32 * 2, 300, 30, optionsui->root, BUTTON_RADIO);

    auto button_applyoptions = CreateButton("OK", sz.x - 2 * (8 + 80), sz.y - 8 - 30, 80, 30, optionsui->root, BUTTON_OK);
    auto button_closeoptions = CreateButton("Cancel", sz.x - 8 - 80, sz.y - 8 - 30, 80, 30, optionsui->root, BUTTON_CANCEL);

    //-------------------------------------------------------
    // Main loop
    //-------------------------------------------------------

    while (true)
    {
        const Event event = WaitEvent();
        switch (event.id)
        {
        case EVENT_WINDOWSIZE:
            if (event.source == mainwindow)
            {
                // If the window resize event is captured
                auto window = event.source->As<UltraEngine::Window>();

                // Get the new size of the applications window
                UltraEngine::iVec2 sz = mainwindow->ClientSize();

                // Set the position and size of the viewport window
                //viewport->SetShape(mainviewport->GetPosition(true).x, mainviewport->GetPosition(true).y, mainviewport->GetSize().x, mainviewport->GetSize().y);
            }
            break;

        case EVENT_WIDGETACTION:
            if (event.source == menu_exit)
            {
                EmitEvent(EVENT_WINDOWCLOSE, mainwindow);
            }
            else if (event.source == menu_open)
            {
                RequestFile("Open File");
            }
            else if (event.source == menu_save or event.source == menu_saveas)
            {
                RequestFile("Save File", "", "All Files", 0, true);
            }
            else if (event.source == menu_helpcontents)
            {
                RunFile("https://www.ultraengine.com/learn");
            }
            else if (event.source == menu_about)
            {
                Notify("Ultra App Kit");
            }
            else if (event.source == menu_perspective or event.source == menu_top or event.source == menu_side or event.source == menu_front)
            {
                menu_perspective->SetState(WIDGETSTATE_UNSELECTED);
                menu_top->SetState(WIDGETSTATE_UNSELECTED);
                menu_side->SetState(WIDGETSTATE_UNSELECTED);
                menu_front->SetState(WIDGETSTATE_UNSELECTED);
                auto menuitem = event.source->As<Widget>();
                menuitem->SetState(WIDGETSTATE_SELECTED);
                statusbarlabel_view->SetText(menuitem->text);
            }
            else if (event.source == toolbarbutton_open)
            {
                EmitEvent(EVENT_WIDGETACTION, menu_open);
            }
            else if (event.source == toolbarbutton_save)
            {
                EmitEvent(EVENT_WIDGETACTION, menu_save);
            }
            else if (event.source == toolbarbutton_options)
            {
                EmitEvent(EVENT_WIDGETACTION, menu_options);
            }
            else if (event.source == toolbarbutton_help)
            {
                EmitEvent(EVENT_WIDGETACTION, menu_helpcontents);
            }
            else if (event.source == widget_input)
            {
                if (!widget_input->text.empty())
                {
                    console->AddText("\n" + widget_input->text);
                    widget_input->SetText("");
                }
                widget_input->Activate();
            }
            else if (event.source == menu_options)
            {
                optionswindow->SetHidden(false);
                optionswindow->Activate();
                mainwindow->Disable();
            }
            else if (event.source == button_applyoptions or event.source == button_closeoptions)
            {
                EmitEvent(EVENT_WINDOWCLOSE, optionswindow);
            }
            break;

        case EVENT_WIDGETSELECT:
            if (event.source == tabber)
            {
                for (int n = 0; n < tabber->items.size(); ++n)
                {
                    if (n == event.data)
                    {
                        objectpanel->SetHidden(true);
                        scenepanel->SetHidden(false);
                    }
                    else
                    {
                        objectpanel->SetHidden(false);
                        scenepanel->SetHidden(true);
                    }
                }
            }
            break;

        case EVENT_WINDOWCLOSE:
            if (event.source == mainwindow)
            {
                if (Confirm("Are you sure you want to quit?"))
                {
                    return 0;
                }
            }
            else if (event.source == optionswindow)
            {
                mainwindow->Enable();
                mainwindow->Activate();
                optionswindow->SetHidden(true);
            }

        case EVENT_PRINT:
            console->AddText(event.text + "\n");
            break;
        }

       world->Update();
       world->Render(framebuffer);
    }
    return 0;
}

 

Cyclone - Ultra Game System - Component PreprocessorTex2TGA - Darkness Awaits Template (Leadwerks)

If you like my work, consider supporting me on Patreon!

Link to comment
Share on other sites

2 hours ago, reepblue said:

Still getting the clipping issue in the 3D render but the first draw call seems to be better. 

Update your project to get the new shaders.

2 hours ago, reepblue said:

FYI, unless I'm not doing this correctly, adding in a 3D viewport seems to slow everything down. Also, I can't seem to resize the viewport after the window size has been changed.

Okay, a few things...first, your application seems to be a real-time app, but you probably want an event-driven application that only renders when the window is painted. If that is the case, read on, otherwise my solution would be slightly different.

So the code below changes the event look to stop until an event occurs, and only render when a WINDOWPAINT event occurs.

In either case, for viewports to be resizable, you cannot have multithreaded rendering enabled. To turn this off call ASyncRender(false) before the engine is initialized. This will run all the rendering and culling code on the same thread as the logic thread, in order, the same way Leadwerks works. So it's much slower like this (but also more responsive because there's less latency.)

The reason this is required is because you can't have the window resizing on one thread while Vulkan is rendering to it on another. No way this can be allowed to happen, and the code to use a mutex to prevent that would be extremely complicated and probably cancel out the benefits of multithreading. This is probably why you don't see a lot of resizable game windows anymore.

The code below is how our editor is structured, and it does have a resizable viewport. A Vulkan validation error is occuring the second time the viewport is resized, but it's probably a fairly minor issue that will be easy for me to fix.

In order for a window to be resizable at all, it must have the WINDOW_RESIZABLE flag. You cannot resize a window that does not have this flag. You also cannot create a framebuffer on a resizable window if ASyncRender(false) has not been called, for the reason explained above (Vulkan trying to render to a framebuffer as another thread is resizing it).

#include "UltraEngine.h"

using namespace UltraEngine;
const int TOOLBARHEIGHT = 48;
const int STATUSBARHEIGHT = 32;
const int SIDEPANELWIDTH = 300;
const int CONSOLEHEIGHT = 120;

int main(int argc, const char* argv[])
{
    ASyncRender(false);

    //Get displays
    auto displays = GetDisplays();

    //Create window
    auto mainwindow = CreateWindow("Ultra App Kit", 0, 0, 1024, 768, displays[0], WINDOW_CENTER | WINDOW_TITLEBAR | WINDOW_RESIZABLE);
    mainwindow->SetMinSize(800, 600);

    //Create user interface
    auto ui = CreateInterface(mainwindow);
    iVec2 sz = ui->root->ClientSize();

    //-------------------------------------------------------
    // Create main menu
    //-------------------------------------------------------

    auto mainmenu = CreateMenu("", ui->root);

    //File menu
    auto menu_file = CreateMenu("File", mainmenu);
    CreateMenu("New", menu_file);
    CreateMenu("", menu_file);
    auto menu_open = CreateMenu("Open", menu_file);
    auto menu_save = CreateMenu("Save", menu_file);
    auto menu_saveas = CreateMenu("Save as...", menu_file);
    CreateMenu("", menu_file);
    auto menu_recentfiles = CreateMenu("Recent files", menu_file);
    std::array<shared_ptr<Widget>, 10> menu_recentfile;
    for (int n = 0; n < menu_recentfile.size(); ++n)
    {
        menu_recentfile[n] = CreateMenu("Recent file " + String(n + 1), menu_recentfiles);
    }
    CreateMenu("", menu_file);
    auto menu_exit = CreateMenu("Exit", menu_file);

    //Edit menu
    auto menu_edit = CreateMenu("Edit", mainmenu);
    CreateMenu("Undo", menu_edit);
    CreateMenu("Redo", menu_edit);
    CreateMenu("", menu_edit);
    CreateMenu("Cut", menu_edit);
    CreateMenu("Copy", menu_edit);
    CreateMenu("Past", menu_edit);
    CreateMenu("", menu_edit);
    CreateMenu("Select all", menu_edit);
    CreateMenu("Select none", menu_edit);
    CreateMenu("Invert selection", menu_edit);

    //View menu
    auto menu_view = CreateMenu("View", mainmenu);
    auto menu_perspective = CreateMenu("Perspective", menu_view);
    auto menu_top = CreateMenu("XZ - Top", menu_view);
    auto menu_side = CreateMenu("XZ - Side", menu_view);
    auto menu_front = CreateMenu("XY - Front", menu_view);
    menu_perspective->SetState(true);

    //Tools menu
    auto menu_tools = CreateMenu("Tools", mainmenu);
    auto menu_options = CreateMenu("Options", menu_tools);

    //Help menu
    auto menu_help = CreateMenu("Help", mainmenu);
    auto menu_helpcontents = CreateMenu("Help Contents", menu_help);
    auto menu_about = CreateMenu("About", menu_help);

    //-------------------------------------------------------
    // Create toolbar
    //-------------------------------------------------------

    auto toolbar = CreatePanel(0, mainmenu->size.y, sz.x, TOOLBARHEIGHT, ui->root);
    toolbar->SetLayout(1, 1, 1, 0);
    int x = 4, y = 4;

    auto toolbarbutton_open = CreateButton("", x, y, TOOLBARHEIGHT - 8, TOOLBARHEIGHT - 8, toolbar, BUTTON_TOOLBAR);
    toolbarbutton_open->SetFontScale(2);
    toolbarbutton_open->SetIcon(LoadIcon("https://raw.githubusercontent.com/Leadwerks/Documentation/private/Assets/Icons/open.svg"));
    x += TOOLBARHEIGHT;

    auto toolbarbutton_save = CreateButton("", x, y, TOOLBARHEIGHT - 8, TOOLBARHEIGHT - 8, toolbar, BUTTON_TOOLBAR);
    toolbarbutton_save->SetFontScale(2);
    toolbarbutton_save->SetIcon(LoadIcon("https://raw.githubusercontent.com/Leadwerks/Documentation/private/Assets/Icons/save.svg"));
    x += TOOLBARHEIGHT;

    auto toolbarbutton_options = CreateButton("", x, y, TOOLBARHEIGHT - 8, TOOLBARHEIGHT - 8, toolbar, BUTTON_TOOLBAR);
    toolbarbutton_options->SetFontScale(2);
    toolbarbutton_options->SetIcon(LoadIcon("https://raw.githubusercontent.com/Leadwerks/Documentation/private/Assets/Icons/settings.svg"));
    x += TOOLBARHEIGHT;

    auto toolbarbutton_help = CreateButton("", x, y, TOOLBARHEIGHT - 8, TOOLBARHEIGHT - 8, toolbar, BUTTON_TOOLBAR);
    toolbarbutton_help->SetIcon(LoadIcon("https://raw.githubusercontent.com/Leadwerks/Documentation/private/Assets/Icons/help.svg"));
    toolbarbutton_help->SetFontScale(2);

    //-------------------------------------------------------
    // Create status bar
    //-------------------------------------------------------

    auto statusbar = CreatePanel(0, sz.y - STATUSBARHEIGHT, sz.x, STATUSBARHEIGHT, ui->root);
    statusbar->SetLayout(1, 1, 0, 1);
    auto statusbarlabel_view = CreateLabel("Perspective", 4, 0, 300, statusbar->size.y, statusbar, LABEL_LEFT | LABEL_MIDDLE);

    //-------------------------------------------------------
    // Create main panel
    //-------------------------------------------------------

    auto mainpanel = CreatePanel(0, toolbar->position.y + toolbar->size.y, sz.x, sz.y - toolbar->size.y - toolbar->position.y - statusbar->size.y, ui->root);
    mainpanel->SetLayout(1, 1, 1, 1);
    sz = mainpanel->ClientSize();

    //Create console
    auto console = CreateTextArea(4, mainpanel->size.y - CONSOLEHEIGHT, mainpanel->size.x - SIDEPANELWIDTH - 8, CONSOLEHEIGHT - 28 - 4, mainpanel);
    console->SetLayout(1, 1, 0, 1);
    auto widget_input = CreateTextField(4, mainpanel->size.y - 28, mainpanel->size.x - SIDEPANELWIDTH - 8, 28, mainpanel, TEXTFIELD_ENTERKEYACTIONEVENT);
    widget_input->SetLayout(1, 1, 0, 1);

    //Main viewport
    auto mainviewport = CreatePanel(4, 4, mainpanel->size.x - SIDEPANELWIDTH - 8, mainpanel->size.y - 8 - CONSOLEHEIGHT, mainpanel, PANEL_BORDER);
    mainviewport->SetLayout(1, 1, 1, 1);
    mainviewport->SetColor(0, 0, 0);

    auto viewport = CreateWindow("", mainviewport->GetPosition(true).x, mainviewport->GetPosition(true).y, mainviewport->GetSize().x, mainviewport->GetSize().y, mainwindow, WINDOW_CHILD | WINDOW_CLIENTCOORDS | WINDOW_RESIZABLE);

    //Create a world
    auto world = CreateWorld();

    //Create a framebuffer
    auto framebuffer = CreateFramebuffer(viewport);

    //Create a camera
    auto camera = CreateCamera(world);
    camera->SetClearColor(0.125);
    camera->SetFOV(70);
    camera->SetPosition(0, 0, -3);

    //Create a light
    auto light = CreateLight(world, LIGHT_DIRECTIONAL);
    light->SetRotation(35, 45, 0);

    //Create a box
    auto box = CreateBox(world);


    //-------------------------------------------------------
    // Create side panel
    //-------------------------------------------------------   

    auto sidepanel = CreatePanel(sz.x - SIDEPANELWIDTH, 0, SIDEPANELWIDTH, sz.y, mainpanel);
    sidepanel->SetLayout(0, 1, 1, 1);
    auto tabber = CreateTabber(0, 0, SIDEPANELWIDTH, sz.y, sidepanel);
    tabber->SetLayout(1, 1, 1, 1);
    tabber->AddItem("Objects", true);
    tabber->AddItem("Scene");

    //Object panel
    sz = tabber->ClientSize();
    auto objectpanel = CreatePanel(0, 0, sz.x, sz.y, tabber);
    objectpanel->SetLayout(1, 1, 1, 1);
    //tabber->items[0].extra = objectpanel;

    //Scene panel
    auto scenepanel = CreatePanel(0, 0, sz.x, sz.y, tabber);
    scenepanel->SetHidden(true);
    scenepanel->SetLayout(1, 1, 1, 1);
    //tabber->items[1].extra = scenepanel;

    x = 8;
    y = 12;

    CreateLabel("Category:", x, y, 200, 30, objectpanel);
    y += 24;
    auto objectcategorybox = CreateComboBox(x, y, sz.x - x * 2, 30, objectpanel);
    objectcategorybox->SetLayout(1, 1, 1, 0);
    objectcategorybox->AddItem("Primitives", true);
    objectcategorybox->AddItem("Extended primitives");
    objectcategorybox->AddItem("Cameras");
    objectcategorybox->AddItem("Lights");
    objectcategorybox->AddItem("Splines");
    y += 44;

    CreateLabel("Object:", x, y, 200, 30, objectpanel);
    y += 24;
    auto objectbox = CreateComboBox(x, y, sz.x - x * 2, 30, objectpanel);
    objectbox->SetLayout(1, 1, 1, 0);
    objectbox->AddItem("Box", true);
    objectbox->AddItem("Wedge");
    objectbox->AddItem("Cylinder");
    objectbox->AddItem("Sphere");
    y += 44;

    x = 80;
    CreateButton("Create", x, y, sz.x - 2 * x, 28, objectpanel);

    x = 8;
    y = 12;
    auto scenebrowser = CreateTreeView(x, y, sz.x - 2 * x, 400 - y, scenepanel);
    scenebrowser->SetLayout(1, 1, 1, 1);
    auto node = scenebrowser->root->AddNode("Scene");
    node->Expand();
    node->AddNode("Box 1");
    node->AddNode("Box 2");
    node->AddNode("Box 3");
    y += scenebrowser->size.y + x;

    auto propertiespanel = CreatePanel(x, y, sz.x, sz.y - y, scenepanel);
    propertiespanel->SetLayout(1, 1, 0, 1);

    y = 8;
    CreateLabel("Name:", x, y + 4, 60, 30, propertiespanel);
    auto widget_name = CreateTextField(x * 2 + 60, y, sz.x - 4 * x - 60, 30, propertiespanel);
    widget_name->SetText("Box 1");
    y += 40;

    CreateLabel("Value:", x, y + 4, 60, 30, propertiespanel);
    CreateSlider(x * 2 + 60, y, sz.x - 4 * x - 60, 30, propertiespanel, SLIDER_HORIZONTAL | SLIDER_TRACKBAR);
    y += 40;

    //-------------------------------------------------------
    // Options window
    //-------------------------------------------------------

    auto optionswindow = CreateWindow("Options", 0, 0, 400, 500, mainwindow, WINDOW_HIDDEN | WINDOW_TITLEBAR | WINDOW_CENTER);
    auto optionsui = CreateInterface(optionswindow);
    sz = optionsui->root->ClientSize();

    auto button_option1 = CreateButton("Option 1", 12, 12, 300, 30, optionsui->root, BUTTON_CHECKBOX);
    button_option1->SetState(WIDGETSTATE_SELECTED);
    auto button_option2 = CreateButton("Option 2", 12, 12 + 32, 300, 30, optionsui->root, BUTTON_RADIO);
    button_option2->SetState(WIDGETSTATE_SELECTED);
    auto button_option3 = CreateButton("Option 3", 12, 12 + 32 * 2, 300, 30, optionsui->root, BUTTON_RADIO);

    auto button_applyoptions = CreateButton("OK", sz.x - 2 * (8 + 80), sz.y - 8 - 30, 80, 30, optionsui->root, BUTTON_OK);
    auto button_closeoptions = CreateButton("Cancel", sz.x - 8 - 80, sz.y - 8 - 30, 80, 30, optionsui->root, BUTTON_CANCEL);

    //-------------------------------------------------------
    // Main loop
    //-------------------------------------------------------

    while (true)
    {
        const Event event = WaitEvent();
        switch (event.id)
        {
        case EVENT_WINDOWSIZE:
            if (event.source == mainwindow)
            {
                // If the window resize event is captured
                auto window = event.source->As<UltraEngine::Window>();

                // Get the new size of the applications window
                UltraEngine::iVec2 sz = mainwindow->ClientSize();

                // Set the position and size of the viewport window
                viewport->SetShape(mainviewport->GetPosition(true).x, mainviewport->GetPosition(true).y, mainviewport->GetSize().x, mainviewport->GetSize().y);
            }
            break;

        case EVENT_WIDGETACTION:
            if (event.source == menu_exit)
            {
                EmitEvent(EVENT_WINDOWCLOSE, mainwindow);
            }
            else if (event.source == menu_open)
            {
                RequestFile("Open File");
            }
            else if (event.source == menu_save or event.source == menu_saveas)
            {
                RequestFile("Save File", "", "All Files", 0, true);
            }
            else if (event.source == menu_helpcontents)
            {
                RunFile("https://www.ultraengine.com/learn");
            }
            else if (event.source == menu_about)
            {
                Notify("Ultra App Kit");
            }
            else if (event.source == menu_perspective or event.source == menu_top or event.source == menu_side or event.source == menu_front)
            {
                menu_perspective->SetState(WIDGETSTATE_UNSELECTED);
                menu_top->SetState(WIDGETSTATE_UNSELECTED);
                menu_side->SetState(WIDGETSTATE_UNSELECTED);
                menu_front->SetState(WIDGETSTATE_UNSELECTED);
                auto menuitem = event.source->As<Widget>();
                menuitem->SetState(WIDGETSTATE_SELECTED);
                statusbarlabel_view->SetText(menuitem->text);
            }
            else if (event.source == toolbarbutton_open)
            {
                EmitEvent(EVENT_WIDGETACTION, menu_open);
            }
            else if (event.source == toolbarbutton_save)
            {
                EmitEvent(EVENT_WIDGETACTION, menu_save);
            }
            else if (event.source == toolbarbutton_options)
            {
                EmitEvent(EVENT_WIDGETACTION, menu_options);
            }
            else if (event.source == toolbarbutton_help)
            {
                EmitEvent(EVENT_WIDGETACTION, menu_helpcontents);
            }
            else if (event.source == widget_input)
            {
                if (!widget_input->text.empty())
                {
                    console->AddText("\n" + widget_input->text);
                    widget_input->SetText("");
                }
                widget_input->Activate();
            }
            else if (event.source == menu_options)
            {
                optionswindow->SetHidden(false);
                optionswindow->Activate();
                mainwindow->Disable();
            }
            else if (event.source == button_applyoptions or event.source == button_closeoptions)
            {
                EmitEvent(EVENT_WINDOWCLOSE, optionswindow);
            }
            break;

        case EVENT_WIDGETSELECT:
            if (event.source == tabber)
            {
                for (int n = 0; n < tabber->items.size(); ++n)
                {
                    if (n == event.data)
                    {
                        objectpanel->SetHidden(true);
                        scenepanel->SetHidden(false);
                    }
                    else
                    {
                        objectpanel->SetHidden(false);
                        scenepanel->SetHidden(true);
                    }
                }
            }
            break;

        case EVENT_WINDOWCLOSE:
            if (event.source == mainwindow)
            {
                if (Confirm("Are you sure you want to quit?"))
                {
                    return 0;
                }
            }
            else if (event.source == optionswindow)
            {
                mainwindow->Enable();
                mainwindow->Activate();
                optionswindow->SetHidden(true);
            }
            break;

        case EVENT_WINDOWPAINT:
            if (event.source == viewport) world->Render(framebuffer);
            break;
        }
    }
    return 0;
}

My job is to make tools you love, with the features you want, and performance you can't live without.

Link to comment
Share on other sites

Now if you wanted to make a real-time application with a resizable viewport, we just wrap the event loop up in a while statement, remove the WINDOWPAINT event handling, and add the world Update() and Render() to the end. This would be appropriate for something like a particle editor that is continuously rendering. However, since this is using ASyncRender(false) the renderer will be much slower than multithreaded rendering (probably about the same speed as Leadwerks). I still get a validation error upon resizing the window, but it's probably easy to fix that. This will also make the GUI not as snappy as it normally would be, because we are intermittently spending a lot of time performing some fairly heavy computational tasks, and the world Update method also pauses the thread if it is running faster than 60 hz.

#include "UltraEngine.h"

using namespace UltraEngine;
const int TOOLBARHEIGHT = 48;
const int STATUSBARHEIGHT = 32;
const int SIDEPANELWIDTH = 300;
const int CONSOLEHEIGHT = 120;

int main(int argc, const char* argv[])
{
    ASyncRender(false);

    //Get displays
    auto displays = GetDisplays();

    //Create window
    auto mainwindow = CreateWindow("Ultra App Kit", 0, 0, 1024, 768, displays[0], WINDOW_CENTER | WINDOW_TITLEBAR | WINDOW_RESIZABLE);
    mainwindow->SetMinSize(800, 600);

    //Create user interface
    auto ui = CreateInterface(mainwindow);
    iVec2 sz = ui->root->ClientSize();

    //-------------------------------------------------------
    // Create main menu
    //-------------------------------------------------------

    auto mainmenu = CreateMenu("", ui->root);

    //File menu
    auto menu_file = CreateMenu("File", mainmenu);
    CreateMenu("New", menu_file);
    CreateMenu("", menu_file);
    auto menu_open = CreateMenu("Open", menu_file);
    auto menu_save = CreateMenu("Save", menu_file);
    auto menu_saveas = CreateMenu("Save as...", menu_file);
    CreateMenu("", menu_file);
    auto menu_recentfiles = CreateMenu("Recent files", menu_file);
    std::array<shared_ptr<Widget>, 10> menu_recentfile;
    for (int n = 0; n < menu_recentfile.size(); ++n)
    {
        menu_recentfile[n] = CreateMenu("Recent file " + String(n + 1), menu_recentfiles);
    }
    CreateMenu("", menu_file);
    auto menu_exit = CreateMenu("Exit", menu_file);

    //Edit menu
    auto menu_edit = CreateMenu("Edit", mainmenu);
    CreateMenu("Undo", menu_edit);
    CreateMenu("Redo", menu_edit);
    CreateMenu("", menu_edit);
    CreateMenu("Cut", menu_edit);
    CreateMenu("Copy", menu_edit);
    CreateMenu("Past", menu_edit);
    CreateMenu("", menu_edit);
    CreateMenu("Select all", menu_edit);
    CreateMenu("Select none", menu_edit);
    CreateMenu("Invert selection", menu_edit);

    //View menu
    auto menu_view = CreateMenu("View", mainmenu);
    auto menu_perspective = CreateMenu("Perspective", menu_view);
    auto menu_top = CreateMenu("XZ - Top", menu_view);
    auto menu_side = CreateMenu("XZ - Side", menu_view);
    auto menu_front = CreateMenu("XY - Front", menu_view);
    menu_perspective->SetState(true);

    //Tools menu
    auto menu_tools = CreateMenu("Tools", mainmenu);
    auto menu_options = CreateMenu("Options", menu_tools);

    //Help menu
    auto menu_help = CreateMenu("Help", mainmenu);
    auto menu_helpcontents = CreateMenu("Help Contents", menu_help);
    auto menu_about = CreateMenu("About", menu_help);

    //-------------------------------------------------------
    // Create toolbar
    //-------------------------------------------------------

    auto toolbar = CreatePanel(0, mainmenu->size.y, sz.x, TOOLBARHEIGHT, ui->root);
    toolbar->SetLayout(1, 1, 1, 0);
    int x = 4, y = 4;

    auto toolbarbutton_open = CreateButton("", x, y, TOOLBARHEIGHT - 8, TOOLBARHEIGHT - 8, toolbar, BUTTON_TOOLBAR);
    toolbarbutton_open->SetFontScale(2);
    toolbarbutton_open->SetIcon(LoadIcon("https://raw.githubusercontent.com/Leadwerks/Documentation/private/Assets/Icons/open.svg"));
    x += TOOLBARHEIGHT;

    auto toolbarbutton_save = CreateButton("", x, y, TOOLBARHEIGHT - 8, TOOLBARHEIGHT - 8, toolbar, BUTTON_TOOLBAR);
    toolbarbutton_save->SetFontScale(2);
    toolbarbutton_save->SetIcon(LoadIcon("https://raw.githubusercontent.com/Leadwerks/Documentation/private/Assets/Icons/save.svg"));
    x += TOOLBARHEIGHT;

    auto toolbarbutton_options = CreateButton("", x, y, TOOLBARHEIGHT - 8, TOOLBARHEIGHT - 8, toolbar, BUTTON_TOOLBAR);
    toolbarbutton_options->SetFontScale(2);
    toolbarbutton_options->SetIcon(LoadIcon("https://raw.githubusercontent.com/Leadwerks/Documentation/private/Assets/Icons/settings.svg"));
    x += TOOLBARHEIGHT;

    auto toolbarbutton_help = CreateButton("", x, y, TOOLBARHEIGHT - 8, TOOLBARHEIGHT - 8, toolbar, BUTTON_TOOLBAR);
    toolbarbutton_help->SetIcon(LoadIcon("https://raw.githubusercontent.com/Leadwerks/Documentation/private/Assets/Icons/help.svg"));
    toolbarbutton_help->SetFontScale(2);

    //-------------------------------------------------------
    // Create status bar
    //-------------------------------------------------------

    auto statusbar = CreatePanel(0, sz.y - STATUSBARHEIGHT, sz.x, STATUSBARHEIGHT, ui->root);
    statusbar->SetLayout(1, 1, 0, 1);
    auto statusbarlabel_view = CreateLabel("Perspective", 4, 0, 300, statusbar->size.y, statusbar, LABEL_LEFT | LABEL_MIDDLE);

    //-------------------------------------------------------
    // Create main panel
    //-------------------------------------------------------

    auto mainpanel = CreatePanel(0, toolbar->position.y + toolbar->size.y, sz.x, sz.y - toolbar->size.y - toolbar->position.y - statusbar->size.y, ui->root);
    mainpanel->SetLayout(1, 1, 1, 1);
    sz = mainpanel->ClientSize();

    //Create console
    auto console = CreateTextArea(4, mainpanel->size.y - CONSOLEHEIGHT, mainpanel->size.x - SIDEPANELWIDTH - 8, CONSOLEHEIGHT - 28 - 4, mainpanel);
    console->SetLayout(1, 1, 0, 1);
    auto widget_input = CreateTextField(4, mainpanel->size.y - 28, mainpanel->size.x - SIDEPANELWIDTH - 8, 28, mainpanel, TEXTFIELD_ENTERKEYACTIONEVENT);
    widget_input->SetLayout(1, 1, 0, 1);

    //Main viewport
    auto mainviewport = CreatePanel(4, 4, mainpanel->size.x - SIDEPANELWIDTH - 8, mainpanel->size.y - 8 - CONSOLEHEIGHT, mainpanel, PANEL_BORDER);
    mainviewport->SetLayout(1, 1, 1, 1);
    mainviewport->SetColor(0, 0, 0);

    auto viewport = CreateWindow("", mainviewport->GetPosition(true).x, mainviewport->GetPosition(true).y, mainviewport->GetSize().x, mainviewport->GetSize().y, mainwindow, WINDOW_CHILD | WINDOW_CLIENTCOORDS | WINDOW_RESIZABLE);

    //Create a world
    auto world = CreateWorld();

    //Create a framebuffer
    auto framebuffer = CreateFramebuffer(viewport);

    //Create a camera
    auto camera = CreateCamera(world);
    camera->SetClearColor(0.125);
    camera->SetFOV(70);
    camera->SetPosition(0, 0, -3);

    //Create a light
    auto light = CreateLight(world, LIGHT_DIRECTIONAL);
    light->SetRotation(35, 45, 0);

    //Create a box
    auto box = CreateBox(world);


    //-------------------------------------------------------
    // Create side panel
    //-------------------------------------------------------   

    auto sidepanel = CreatePanel(sz.x - SIDEPANELWIDTH, 0, SIDEPANELWIDTH, sz.y, mainpanel);
    sidepanel->SetLayout(0, 1, 1, 1);
    auto tabber = CreateTabber(0, 0, SIDEPANELWIDTH, sz.y, sidepanel);
    tabber->SetLayout(1, 1, 1, 1);
    tabber->AddItem("Objects", true);
    tabber->AddItem("Scene");

    //Object panel
    sz = tabber->ClientSize();
    auto objectpanel = CreatePanel(0, 0, sz.x, sz.y, tabber);
    objectpanel->SetLayout(1, 1, 1, 1);
    //tabber->items[0].extra = objectpanel;

    //Scene panel
    auto scenepanel = CreatePanel(0, 0, sz.x, sz.y, tabber);
    scenepanel->SetHidden(true);
    scenepanel->SetLayout(1, 1, 1, 1);
    //tabber->items[1].extra = scenepanel;

    x = 8;
    y = 12;

    CreateLabel("Category:", x, y, 200, 30, objectpanel);
    y += 24;
    auto objectcategorybox = CreateComboBox(x, y, sz.x - x * 2, 30, objectpanel);
    objectcategorybox->SetLayout(1, 1, 1, 0);
    objectcategorybox->AddItem("Primitives", true);
    objectcategorybox->AddItem("Extended primitives");
    objectcategorybox->AddItem("Cameras");
    objectcategorybox->AddItem("Lights");
    objectcategorybox->AddItem("Splines");
    y += 44;

    CreateLabel("Object:", x, y, 200, 30, objectpanel);
    y += 24;
    auto objectbox = CreateComboBox(x, y, sz.x - x * 2, 30, objectpanel);
    objectbox->SetLayout(1, 1, 1, 0);
    objectbox->AddItem("Box", true);
    objectbox->AddItem("Wedge");
    objectbox->AddItem("Cylinder");
    objectbox->AddItem("Sphere");
    y += 44;

    x = 80;
    CreateButton("Create", x, y, sz.x - 2 * x, 28, objectpanel);

    x = 8;
    y = 12;
    auto scenebrowser = CreateTreeView(x, y, sz.x - 2 * x, 400 - y, scenepanel);
    scenebrowser->SetLayout(1, 1, 1, 1);
    auto node = scenebrowser->root->AddNode("Scene");
    node->Expand();
    node->AddNode("Box 1");
    node->AddNode("Box 2");
    node->AddNode("Box 3");
    y += scenebrowser->size.y + x;

    auto propertiespanel = CreatePanel(x, y, sz.x, sz.y - y, scenepanel);
    propertiespanel->SetLayout(1, 1, 0, 1);

    y = 8;
    CreateLabel("Name:", x, y + 4, 60, 30, propertiespanel);
    auto widget_name = CreateTextField(x * 2 + 60, y, sz.x - 4 * x - 60, 30, propertiespanel);
    widget_name->SetText("Box 1");
    y += 40;

    CreateLabel("Value:", x, y + 4, 60, 30, propertiespanel);
    CreateSlider(x * 2 + 60, y, sz.x - 4 * x - 60, 30, propertiespanel, SLIDER_HORIZONTAL | SLIDER_TRACKBAR);
    y += 40;

    //-------------------------------------------------------
    // Options window
    //-------------------------------------------------------

    auto optionswindow = CreateWindow("Options", 0, 0, 400, 500, mainwindow, WINDOW_HIDDEN | WINDOW_TITLEBAR | WINDOW_CENTER);
    auto optionsui = CreateInterface(optionswindow);
    sz = optionsui->root->ClientSize();

    auto button_option1 = CreateButton("Option 1", 12, 12, 300, 30, optionsui->root, BUTTON_CHECKBOX);
    button_option1->SetState(WIDGETSTATE_SELECTED);
    auto button_option2 = CreateButton("Option 2", 12, 12 + 32, 300, 30, optionsui->root, BUTTON_RADIO);
    button_option2->SetState(WIDGETSTATE_SELECTED);
    auto button_option3 = CreateButton("Option 3", 12, 12 + 32 * 2, 300, 30, optionsui->root, BUTTON_RADIO);

    auto button_applyoptions = CreateButton("OK", sz.x - 2 * (8 + 80), sz.y - 8 - 30, 80, 30, optionsui->root, BUTTON_OK);
    auto button_closeoptions = CreateButton("Cancel", sz.x - 8 - 80, sz.y - 8 - 30, 80, 30, optionsui->root, BUTTON_CANCEL);

    //-------------------------------------------------------
    // Main loop
    //-------------------------------------------------------

    while (true)
    {
        while (PeekEvent())
            {
            const Event event = WaitEvent();
            switch (event.id)
            {
            case EVENT_WINDOWSIZE:
                if (event.source == mainwindow)
                {
                    // If the window resize event is captured
                    auto window = event.source->As<UltraEngine::Window>();

                    // Get the new size of the applications window
                    UltraEngine::iVec2 sz = mainwindow->ClientSize();

                    // Set the position and size of the viewport window
                    viewport->SetShape(mainviewport->GetPosition(true).x, mainviewport->GetPosition(true).y, mainviewport->GetSize().x, mainviewport->GetSize().y);
                }
                break;

            case EVENT_WIDGETACTION:
                if (event.source == menu_exit)
                {
                    EmitEvent(EVENT_WINDOWCLOSE, mainwindow);
                }
                else if (event.source == menu_open)
                {
                    RequestFile("Open File");
                }
                else if (event.source == menu_save or event.source == menu_saveas)
                {
                    RequestFile("Save File", "", "All Files", 0, true);
                }
                else if (event.source == menu_helpcontents)
                {
                    RunFile("https://www.ultraengine.com/learn");
                }
                else if (event.source == menu_about)
                {
                    Notify("Ultra App Kit");
                }
                else if (event.source == menu_perspective or event.source == menu_top or event.source == menu_side or event.source == menu_front)
                {
                    menu_perspective->SetState(WIDGETSTATE_UNSELECTED);
                    menu_top->SetState(WIDGETSTATE_UNSELECTED);
                    menu_side->SetState(WIDGETSTATE_UNSELECTED);
                    menu_front->SetState(WIDGETSTATE_UNSELECTED);
                    auto menuitem = event.source->As<Widget>();
                    menuitem->SetState(WIDGETSTATE_SELECTED);
                    statusbarlabel_view->SetText(menuitem->text);
                }
                else if (event.source == toolbarbutton_open)
                {
                    EmitEvent(EVENT_WIDGETACTION, menu_open);
                }
                else if (event.source == toolbarbutton_save)
                {
                    EmitEvent(EVENT_WIDGETACTION, menu_save);
                }
                else if (event.source == toolbarbutton_options)
                {
                    EmitEvent(EVENT_WIDGETACTION, menu_options);
                }
                else if (event.source == toolbarbutton_help)
                {
                    EmitEvent(EVENT_WIDGETACTION, menu_helpcontents);
                }
                else if (event.source == widget_input)
                {
                    if (!widget_input->text.empty())
                    {
                        console->AddText("\n" + widget_input->text);
                        widget_input->SetText("");
                    }
                    widget_input->Activate();
                }
                else if (event.source == menu_options)
                {
                    optionswindow->SetHidden(false);
                    optionswindow->Activate();
                    mainwindow->Disable();
                }
                else if (event.source == button_applyoptions or event.source == button_closeoptions)
                {
                    EmitEvent(EVENT_WINDOWCLOSE, optionswindow);
                }
                break;

            case EVENT_WIDGETSELECT:
                if (event.source == tabber)
                {
                    for (int n = 0; n < tabber->items.size(); ++n)
                    {
                        if (n == event.data)
                        {
                            objectpanel->SetHidden(true);
                            scenepanel->SetHidden(false);
                        }
                        else
                        {
                            objectpanel->SetHidden(false);
                            scenepanel->SetHidden(true);
                        }
                    }
                }
                break;

            case EVENT_WINDOWCLOSE:
                if (event.source == mainwindow)
                {
                    if (Confirm("Are you sure you want to quit?"))
                    {
                        return 0;
                    }
                }
                else if (event.source == optionswindow)
                {
                    mainwindow->Enable();
                    mainwindow->Activate();
                    optionswindow->SetHidden(true);
                }
            }
        }
        box->Turn(0, 1, 0);
        world->Update();
        world->Render(framebuffer);
    }
    return 0;
}

 

My job is to make tools you love, with the features you want, and performance you can't live without.

Link to comment
Share on other sites

That may seem confusing at first, but it's a pretty straightforward way to handle some very complicated issues. It took quite a few iterations to figure all this stuff out but the solution I've got gives you a lot of power to do whatever you want with minimal fuss. And it turns out that slower speed with lower latency is actually ideal for a GUI app, because you are more concerned with responsiveness than the speed at which it can render over and over.

My job is to make tools you love, with the features you want, and performance you can't live without.

Link to comment
Share on other sites

16 hours ago, Josh said:

If you click the "run install scripts manually" options in the settings you can run the bat file yourself and see any info it prints out.

Downloaded client9. Installed fine manually, automatically however still had missing files in the Library folder in the client app directory.  No errors in manually or automatic install.  Perhaps now that tar.exe and it's two dll's are gone its working ok?  Running patch.bat as admin after an auto install put the .lib files into the Library folder.

 

Both debug and release build fine even though both show these errors.  The errors are across a few files.

WindowErrors.thumb.png.f9f1c633945f7141c7c21b80baace753.png

 

Release runs fine but debug still has the error;

Validation Error: [ VUID-VkDeviceCreateInfo-pNext-pNext ] Object 0: VK_NULL_HANDLE, type = VK_OBJECT_TYPE_DEVICE; | MessageID = 0x901f59ec | vkCreateDevice: pCreateInfo->pNext chain includes a structure with unknown VkStructureType (1000314007); Allowed structures are [VkDeviceDiagnosticsConfigCreateInfoNV, VkDeviceGroupDeviceCreateInfo, VkDeviceMemoryOverallocationCreateInfoAMD, VkDevicePrivateDataCreateInfoEXT, VkPhysicalDevice16BitStorageFeatures, VkPhysicalDevice4444FormatsFeaturesEXT, VkPhysicalDevice8BitStorageFeatures, VkPhysicalDeviceASTCDecodeFeaturesEXT, VkPhysicalDeviceBlendOperationAdvancedFeaturesEXT, VkPhysicalDeviceBufferDeviceAddressFeatures, VkPhysicalDeviceBufferDeviceAddressFeaturesEXT, VkPhysicalDeviceCoherentMemoryFeaturesAMD, VkPhysicalDeviceComputeShaderDerivativesFeaturesNV, VkPhysicalDeviceConditionalRenderingFeaturesEXT, VkPhysicalDeviceCooperativeMatrixFeaturesNV, VkPhysicalDeviceCornerSampledImageFeaturesNV, VkPhysicalDeviceCoverageReductionModeFeaturesNV, VkPhysicalDeviceCustomBorderColorFeaturesEXT, VkPhysicalDeviceDedicatedAllocationImageAliasingFeaturesNV, VkPhysicalDeviceDepthClipEnableFeaturesEXT, VkPhysicalDeviceDescriptorIndexingFeatures, VkPhysicalDeviceDeviceGeneratedCommandsFeaturesNV, VkPhysicalDeviceDiagnosticsConfigFeaturesNV, VkPhysicalDeviceExclusiveScissorFeaturesNV, VkPhysicalDeviceExtendedDynamicStateFeaturesEXT, VkPhysicalDeviceFeatures2, VkPhysicalDeviceFragmentDensityMap2FeaturesEXT, VkPhysicalDeviceFragmentDensityMapFeaturesEXT, VkPhysicalDeviceFragmentShaderBarycentricFeaturesNV, VkPhysicalDeviceFragmentShaderInterlockFeaturesEXT, VkPhysicalDeviceHostQueryResetFeatures, VkPhysicalDeviceImageRobustnessFeaturesEXT, VkPhysicalDeviceImagelessFramebufferFeatures, VkPhysicalDeviceIndexTypeUint8FeaturesEXT, VkPhysicalDeviceInlineUniformBlockFeaturesEXT, VkPhysicalDeviceLineRasterizationFeaturesEXT, VkPhysicalDeviceMemoryPriorityFeaturesEXT, VkPhysicalDeviceMeshShaderFeaturesNV, VkPhysicalDeviceMultiviewFeatures, VkPhysicalDevicePerformanceQueryFeaturesKHR, VkPhysicalDevicePipelineCreationCacheControlFeaturesEXT, VkPhysicalDevicePipelineExecutablePropertiesFeaturesKHR, VkPhysicalDevicePortabilitySubsetFeaturesKHR, VkPhysicalDevicePrivateDataFeaturesEXT, VkPhysicalDeviceProtectedMemoryFeatures, VkPhysicalDeviceRayTracingFeaturesKHR, VkPhysicalDeviceRepresentativeFragmentTestFeaturesNV, VkPhysicalDeviceRobustness2FeaturesEXT, VkPhysicalDeviceSamplerYcbcrConversionFeatures, VkPhysicalDeviceScalarBlockLayoutFeatures, VkPhysicalDeviceSeparateDepthStencilLayoutsFeatures, VkPhysicalDeviceShaderAtomicFloatFeaturesEXT, VkPhysicalDeviceShaderAtomicInt64Features, VkPhysicalDeviceShaderClockFeaturesKHR, VkPhysicalDeviceShaderDemoteToHelperInvocationFeaturesEXT, VkPhysicalDeviceShaderDrawParametersFeatures, VkPhysicalDeviceShaderFloat16Int8Features, VkPhysicalDeviceShaderImageFootprintFeaturesNV, VkPhysicalDeviceShaderIntegerFunctions2FeaturesINTEL, VkPhysicalDeviceShaderSMBuiltinsFeaturesNV, VkPhysicalDeviceShaderSubgroupExtendedTypesFeatures, VkPhysicalDeviceShadingRateImageFeaturesNV, VkPhysicalDeviceSubgroupSizeControlFeaturesEXT, VkPhysicalDeviceTexelBufferAlignmentFeaturesEXT, VkPhysicalDeviceTextureCompressionASTCHDRFeaturesEXT, VkPhysicalDeviceTimelineSemaphoreFeatures, VkPhysicalDeviceTransformFeedbackFeaturesEXT, VkPhysicalDeviceUniformBufferStandardLayoutFeatures, VkPhysicalDeviceVariablePointersFeatures, VkPhysicalDeviceVertexAttributeDivisorFeaturesEXT, VkPhysicalDeviceVulkan11Features, VkPhysicalDeviceVulkan12Features, VkPhysicalDeviceVulkanMemoryModelFeatures, VkPhysicalDeviceYcbcrImageArraysFeaturesEXT]. This error is based on the Valid Usage documentation for version 154 of the Vulkan header.  It is possible that you are using a struct from a private extension or an extension that was added to a later version of the Vulkan header, in which case the use of pCreateInfo->pNext is undefined and may not work correctly with validation enabled The Vulkan spec states: Each pNext member of any structure (including this one) in the pNext chain must be either NULL or a pointer to a valid instance of VkDeviceDiagnosticsConfigCreateInfoNV, VkDeviceGroupDeviceCreateInfo, VkDeviceMemoryOverallocationCreateInfoAMD, VkDevicePrivateDataCreateInfoEXT, VkPhysicalDevice16BitStorageFeatures, VkPhysicalDevice4444FormatsFeaturesEXT, VkPhysicalDevice8BitStorageFeatures, VkPhysicalDeviceASTCDecodeFeaturesEXT, VkPhysicalDeviceBlendOperationAdvancedFeaturesEXT, VkPhysicalDeviceBufferDeviceAddressFeatures, VkPhysicalDeviceBufferDeviceAddressFeaturesEXT, VkPhysicalDeviceCoherentMemoryFeaturesAMD, VkPhysicalDeviceComputeShaderDerivativesFeaturesNV, VkPhysicalDeviceConditionalRenderingFeaturesEXT, VkPhysicalDeviceCooperativeMatrixFeaturesNV, VkPhysicalDeviceCornerSampledImageFeaturesNV, VkPhysicalDeviceCoverageReductionModeFeaturesNV, VkPhysicalDeviceCustomBorderColorFeaturesEXT, VkPhysicalDeviceDedicatedAllocationImageAliasingFeaturesNV, VkPhysicalDeviceDepthClipEnableFeaturesEXT, VkPhysicalDeviceDescriptorIndexingFeatures, VkPhysicalDeviceDeviceGeneratedCommandsFeaturesNV, VkPhysicalDeviceDiagnosticsConfigFeaturesNV, VkPhysicalDeviceExclusiveScissorFeaturesNV, VkPhysicalDeviceExtendedDynamicStateFeaturesEXT, VkPhysicalDeviceFeatures2, VkPhysicalDeviceFragmentDensityMap2FeaturesEXT, VkPhysicalDeviceFragmentDensityMapFeaturesEXT, VkPhysicalDeviceFragmentShaderBarycentricFeaturesNV, VkPhysicalDeviceFragmentShaderInterlockFeaturesEXT, VkPhysicalDeviceHostQueryResetFeatures, VkPhysicalDeviceImageRobustnessFeaturesEXT, VkPhysicalDeviceImagelessFramebufferFeatures, VkPhysicalDeviceIndexTypeUint8FeaturesEXT, VkPhysicalDeviceInlineUniformBlockFeaturesEXT, VkPhysicalDeviceLineRasterizationFeaturesEXT, VkPhysicalDeviceMemoryPriorityFeaturesEXT, VkPhysicalDeviceMeshShaderFeaturesNV, VkPhysicalDeviceMultiviewFeatures, VkPhysicalDevicePerformanceQueryFeaturesKHR, VkPhysicalDevicePipelineCreationCacheControlFeaturesEXT, VkPhysicalDevicePipelineExecutablePropertiesFeaturesKHR, VkPhysicalDevicePortabilitySubsetFeaturesKHR, VkPhysicalDevicePrivateDataFeaturesEXT, VkPhysicalDeviceProtectedMemoryFeatures, VkPhysicalDeviceRayTracingFeaturesKHR, VkPhysicalDeviceRepresentativeFragmentTestFeaturesNV, VkPhysicalDeviceRobustness2FeaturesEXT, VkPhysicalDeviceSamplerYcbcrConversionFeatures, VkPhysicalDeviceScalarBlockLayoutFeatures, VkPhysicalDeviceSeparateDepthStencilLayoutsFeatures, VkPhysicalDeviceShaderAtomicFloatFeaturesEXT, VkPhysicalDeviceShaderAtomicInt64Features, VkPhysicalDeviceShaderClockFeaturesKHR, VkPhysicalDeviceShaderDemoteToHelperInvocationFeaturesEXT, VkPhysicalDeviceShaderDrawParametersFeatures, VkPhysicalDeviceShaderFloat16Int8Features, VkPhysicalDeviceShaderImageFootprintFeaturesNV, VkPhysicalDeviceShaderIntegerFunctions2FeaturesINTEL, VkPhysicalDeviceShaderSMBuiltinsFeaturesNV, VkPhysicalDeviceShaderSubgroupExtendedTypesFeatures, VkPhysicalDeviceShadingRateImageFeaturesNV, VkPhysicalDeviceSubgroupSizeControlFeaturesEXT, VkPhysicalDeviceTexelBufferAlignmentFeaturesEXT, VkPhysicalDeviceTextureCompressionASTCHDRFeaturesEXT, VkPhysicalDeviceTimelineSemaphoreFeatures, VkPhysicalDeviceTransformFeedbackFeaturesEXT, VkPhysicalDeviceUniformBufferStandardLayoutFeatures, VkPhysicalDeviceVariablePointersFeatures, VkPhysicalDeviceVertexAttributeDivisorFeaturesEXT, VkPhysicalDeviceVulkan11Features, VkPhysicalDeviceVulkan12Features, VkPhysicalDeviceVulkanMemoryModelFeatures, or VkPhysicalDeviceYcbcrImageArraysFeaturesEXT (https://vulkan.lunarg.com/doc/view/1.2.154.1/windows/1.2-extensions/vkspec.html#VUID-VkDeviceCreateInfo-pNext-pNext)

Do I need to update Vulkan or something?  How can I see what version I have installed?

 

Importing a project created with client8 works well.  I get a small error icon which I assumed means some files aren't up to date.  Didn't know what to do so I clicked the icon and guessed that I press OK which seemed to work.  Some more info displayed on this process would be a good idea.

Upon opening the updated imported project it can't find UltraEngine.h.  Perhaps the paths aren't updating to the new client app directory?  Looking in the projects properties that seems to be the case.

Link to comment
Share on other sites

The client app is updated again. It did not actually have any checks to make sure a file downloaded successfully. Now it will re-try the download if it fails a few times, and then stop with an error message if it can't get the file.

10 hours ago, SpiderPig said:

Upon opening the updated imported project it can't find UltraEngine.h.  Perhaps the paths aren't updating to the new client app directory?  Looking in the projects properties that seems to be the case.

The application does not currently attempt to modify imported projects but maybe I will add that.

10 hours ago, SpiderPig said:

Do I need to update Vulkan or something?  How can I see what version I have installed?

You might need to update your graphics driver, but if it is initializing Vulkan 1.3 then that structure should be recognized.

  • Thanks 1

My job is to make tools you love, with the features you want, and performance you can't live without.

Link to comment
Share on other sites

  • Josh changed the title to Ultra Engine testing
  • Josh locked this topic
Guest
This topic is now closed to further replies.
 Share

×
×
  • Create New...