Mittwoch, 15. Juli 2015

OpenGL Game GUI / Widgets with Source Code (C++)

The GUI is intended for PC and/or Console Games. 
Focus was mostly on keeping everything simple, easy to use, fast, and flexible (like having skin and ttf support).
The performance is ~700 fps for 20 windows and 100 fps for 100 windows on a Notebook PC (NVidia GTX 765 / Core i7 2.4). 

The source (MIT license) can be downloaded from GitHUB or here



Usage examples:

Creating and opening a window: 

gui.window["mywin"]=Gui::Window("Hello World",100,100,350,250);

Adding a button:

gui.window["mywin"].button["Ok1"]=Gui::Button("OK",20,100,60,20);

Adding a callback to close the window when pushing the button:

gui.window["mywin"].button["Ok1"].callback_pressed=
  [](Gui::Window *w,Gui::Button* b,int index) { w->close(); };


Here two screenshots with different skins
( VS2012 / OpenGL / GLUT based / MIT license )

Features:
  • Advanced skin scaling: define how the inner and borders of a texture shall be scaled ( simple scale, repeating inner pattern or constant outer frame, carried out by 3 Shader types)
  • UTF8 Support
  • TTF Support
  • Easy skinning support
  • Alpha support (RGBA), so you can have transparent windows/widgets etc
  • direct access to all controls and their members
  • Non-blocking visualization
  • Callback support
  • Different from most GUIs, Widgets are stored directly inside the GUI rather having local variables pointing to instances of an overwritten class.
  • Directory: Controls are stored and accessed by a tree structure that is rendered every time like a scene graph
  • No event loop, show function or update function
  • No need to have a timer that calls an update function
  • Lightweight: Only Depends on GL and libFreetype and DevIL
  • Can easily be used in SDL, Qt and other environments. Just plug in the keyboard and mouse events and call the gui's draw function from your main render loop.
  • Config file support for default variables (font,padding etc) and skin textures+scaling parameters
  • Only 2000 lines of code for the entire Gui class including all widgets. Therefore easy to modify and extend.
  • Comes with a simple file-browser (See screenshot1)
  • MIT license
  • Multi-screen support (same as multiple Desktops). You can have a title screen, loading screen, ingame screen etc. Simply create them at the beginning and the switch between by setting gui.active_screen=number;
  • Window manager
  • Context menu
  • Custom mouse pointer
  • Controls have support for custom user variables like control.var.string["myvar"]="hello"
Available classes for controls:
  • Window class
  • Label class
  • Button class
  • Combo class
  • Radio class
  • Tab class
  • Textedit class
  • Slider class
  • CheckBox class
  • Menu class
What the GUI is good for:
  • PC/Console Games
  • Simple menus
What the GUI might not be suited for
  • Complex GUIs that need tree-views , docking ,drag & drop etc
  • Mobile Devices ; perhaps too slow (needs testing) and also the render code needs to become OpenGL ES
Current Limitations (might be solved in the future)
  • Uses old OpenGL (glMatrix etc)
  • Usually one or two render calls per widget rather than one opengl render call for the entire gui.
  • 2D windows/widgets only. You could overwrite the render shader to get 3D, but you would have to map 2D mouse coordinates to 3D then by yourself. 
  • No animations (you could create them yourself by updating the textures accordingly or modifying the shader that render the widgets)
  • Only one font size and one font type
  • The GUI class is a singleton
  • The TextEdit does not support arbitrary cursor position or ctrl+c/v/x operations
  • Drag and Drop is not supported
  • No automatic window layout
  • No docking
  • No Ressource files (yet)
  • No treeview class
  • No caching of rendered labels, geometry, etc. Therefore every frame, the entire tree is traversed and every single character of a rendered text is processed for rendering.
The usage is also described in the previous post.

The code used to create window + widgets + menu in screenshot2  is the following:

 gui.init( Gui::Flags::CONTEXT_MENU | Gui::Flags::CUSTOM_MOUSE ,
    "../data/gui_global.txt" , 
    "../data/gui_skin.txt");

 // Main Window

 Gui::Window w=Gui::Window("Hello World",100,100,350,250); 

 // Add Simple Label to Window

 w.label["l"]=Gui::Label("Label",20,70,100,20);

 // Add Simple Button to Window

 w.button["Ok1"]=Gui::Button("OK",20,100,60,20);
 w.button["Ok1"].callback_pressed=
  [](Gui::Window *window,Gui::Button* control,int index)
  {
   window->close();
  };

 w.button["Ok2"]=Gui::Button("",220,100,100,100);
 w.button["Ok2"].skin=Skin( "../data/smiley.png",
        "../data/smileybw.png",
        "../data/smiley2.png");

 // Add Simple Combo to Window

 w.combo ["cb"]=Gui::Combo(120,100,60,20);
 w.combo ["cb"].add_item("test");
 w.combo ["cb"].add_item(L"東京");
 w.combo ["cb"].callback_selected=
  [](Gui::Window *w,Gui::Button* control,int index) // text entered callback example
  {
   Gui::Combo &c=*(Gui::Combo*)control;
   w->label["l"].text=Gui::String( c.selected ) + "  selected";
   w->label["l"].textcolor=vec4f(1,0,0,1);
  };

 // Add Text Edit to Window

 w.textedit["txt"]=Gui::TextEdit(10,"text",20,150,160,20);
 w.textedit["txt"].callback_text_entered=  
  [](Gui::Window *w,Gui::Button* control,int index) // text entered callback example
  {
   Gui::TextEdit &t=*(Gui::TextEdit*)control;
   w->title.text=t.text;
  };

 // Add Radio

 w.radio["rad"]=Gui::Radio(20,190,20,20); // first button
 w.radio["rad"].add_item(50,190);   // second button
 w.radio["rad"].add_item(80,190);   // third button
 w.radio["rad"].callback_pressed=
  [](Gui::Window *w,Gui::Button* control,int index)
  { 
   w->x+=10;
  };

 // Add Slider

 w.slider["s1"]=Gui::Slider( 0,200,100,   /* min,max,default*/ 
        20,220,160,20); /* window x,y,sx,sy */ 
 w.slider["s1"].callback_pressed=
 [](Gui::Window *w,Gui::Button* control,int index)
 {
  Gui::Slider &b=*((Gui::Slider*) control); 
  w->button["Ok1"].text=Gui::String(int(b.val)); 
 };

 w.slider["s2"]=Gui::Slider( 0,200,100,   /* min,max,default*/ 
        350,100,20,160, /* window x,y,sx,sy */
        Gui::Slider::VERTICAL );

 // Add File Menu to Window

 Gui::Menu m=Gui::Menu("File",/* x,y,sx,sy */ 9,39,50,20, /* menuwidth */ 100);
 m.add_item("Load",
  [](Gui::Window *window,Gui::Button* control,int index) // menu button callback
  {
   gui.screen[0].window["filebrowser"]=  // open file dialog
    gui_file_dialog( "Load SaveGame" , "Load" , "Cancel" ,
     /*get_current_dir()*/ "..\\data\\win8",".png", 100,100,

     // file dialog ok button callback
     [](Gui::Window *w,Gui::Button* b,int index) 
     {  
      MessageBoxA(0, 
       w->textedit["Filename"].text.c_str() , 
       w->label["dir"].text.c_str() ,0);  
      w->close();    
     }
   );
  });

 m.add_item("Close",
  [](Gui::Window *w,Gui::Button* control,int index) // menu button callback
  {
   w->parent->close(); // close window
  });

 m.add_menu("submenu");
 m.window.menu[0].add_item("test1");
 m.window.menu[0].add_item("test2");

 w.menu["menu"]=m;

 // Add new Window to Screen 0 (default)

 gui.screen[0].window["hello1"]=w; // finally put window on the screen (1st copy)

 // Modify and Add new Window to Screen 0 (default)

 w.move(500,100);
 w.resize(400,300);
 w.minsize(150,150);

 gui.screen[0].window["hello2"]=w; // finally put window on the screen (2nd copy)

 // -------------------------------------------------------------------------

 // also use our previous menu as context menu and file menu
 // Note : "context_menu" is reserved for the context menu
 //    all other id names are common menus on the background

 gui.screen[0].menu["context_menu"]=m;

 gui.screen[0].menu["file"]=m;
 gui.screen[0].menu["file"].y=5;

 // -------------------------------------------------------------------------

 // Add Button to Background

 gui.dialog["sample"]=w; // store for later use in the callback

 gui.screen[0].button["more"]=Gui::Button("More Windows Plz!!",50,50,200,20);
 gui.screen[0].button["more"].callback_pressed=
  [](Gui::Window *w,Gui::Button* control,int index) // menu button callback
  {
   int id=gui.screen[0].window.add(gui.dialog["sample"]);
   gui.screen[0].window[id].x= timeGetTime() % (int)gui.screen_resolution_x;
   gui.screen[0].window[id].y= timeGetTime() % (int)gui.screen_resolution_y;
  };

 // Add Tabbed Window to Background

 Gui::Tab t=Gui::Tab("Win1",350,150,300,200,50,20);;
 t.add_tab("Win2");
 t.add_tab("Win3");
 t.add_tab("Win4");
 loopi(0,4)
 {
  t.window[i].button.add(Gui::Button("OK",20+i*10,20,60));
  t.window[i].label["lab"] =Gui::Label("some text",20+i*20,90,100);
  t.window[i].button["test2"]=Gui::Button("OK",100+i*10,20,60);
  t.window[i].button["test3"]=Gui::Button("OK",50+i*10,50,60);
 }
 t.flags=Gui::Tab::MOVABLE; // Make it movable
 gui.screen[0].tab["mytab"]=t;



Main reason for the development of the GUI was, that after trying several gui's more or less suited for games (LibRocket, Qt, NVWidgets, CEgui, etc), I found that none of them was really light weight, easy to use and fully skinable at the same time. So the solution was to create one which is exactly so. 




Mittwoch, 1. Juli 2015

OpenGL Game GUI & Widget Development (Alpha Demo)

Today I am going to share some progress on my GUI project.

The Design Goals were:
  • Simplicity : Create a control in one line
  • Easy Skinning support
  • TTF and UTF8 support
  • Directory : Controls can be accessed by a tree structure
  • Direct access of all data members like if a button is pressed e.g.
  • No Event Loop
  • Static non-blocking visualization
  • Dialog Templates from ressource text file
  • Callback support 

Some simple examples:
Creating a window: gui.window["idname"]=Gui::Window("Title",100,100,250,350);
Closing a window: gui.window.erase("idname");
Adding a button : gui.window["idname"].button["load"]=Gui::Button("Load",20,20,50,30);

Change the skin of a window or other control:
gui.window["idname"].skin=Skin("normal.png","hover.png","selected.png");

There is no need to create a variable for most cases. The GUI handles that for you. It will cover basic functions that a required in a game. Its not optimized for speed, yet fast enough in most cases.

You can fetch an alpha demo of the gui here : [ DOWNLOAD ]

I will post more details soon.




Mittwoch, 27. Mai 2015

Sparse Voxel Octree Raytracing based Occlusion Culling (Theory)

I will share some thoughts on how the next generation of block based sandbox games rendering (triangle based) and creators pipelines might look like. In current games, overdraw is the most limiting factor when drawing high detailed scenes - that means, occlusions are not determined 100% accurate. Further, it is necessary to traverse some data structures on the CPU to create a list of objects to be rendered on the screen.

To improve on this, here the proposal for a completely GPU based solution that uses octree raycasting to determine the visibility.


The first step is to raycast a low res, lets say 128x128 pixel, ID buffer, which takes about 1ms from experiments. This is fast and only used to determine the visibility. In a second step, we create a list of visible objects and further merge with the list of objects that were visible in the past few frames. The list is generated as input buffer for glmultidrawelementsindirect. The steps until now are performed on the GPU using multiple CL or GL compute kernels. The final step is to draw all object with one GL call. The raycasting and grouping of duplicate IDs on the screen can be performed best in opengl compute or opencl. To not miss out distant objects only occupying a few pixels, it is necessary to add jittering to the rays casted. The proposed technology will allow the use of high resolution blocks for sandbox games while the performance is high enough to run on notebooks at high frame rates.

The creation pipeline is as usual:


First, sculpting / creating of the mesh as hi-res, then creating LODs+Normal maps using mesh simplification and transferring the details to a normal map.

Actually, there is already a similar method out there for a while called instant OC. It is not that effeciently integrated however. Its a script addon to Unity - yet, is uses raycasts to determine the visibility (1000-2000 have been the demo settings). 

Donnerstag, 21. Mai 2015

Sparse Voxel Octree (SVO) Reprojection Raycast Tech-DEMO

I decided to release a tech demo of the sparse voxel octree raytracing re-projection algorithm. Feel free to download and try it out. It uses OpenCL and should run on NVIDIA in any case. For ATI and INTEL HD I cannot guarantee. You might further need VS2012 redistributables 32 bit.

Usage:

Mouse : Look around
w,s,a,d,q,e : Move around
Space : See the re-projected pixels (lower image)

The algorithm raycasts only a fraction of pixels for each screen. Most are re-used from the previous frame exploiting frame-to-frame coherency, which allows a speed up of up to 4x. The algorithm is explained in more detail in a previous post.





Sonntag, 12. April 2015

Work in Progress on Outstar (SSAO / Bugfixes)

After a year, I finally found time to continue the development. Here a shot from a castle I built in about 1 day, including sculpting of some new blocks. Note that there are also rooms behind windows where you can walk around inside The new version also has a lot of bugfixes and now is getting closer to be ready for a demo release. SSAO (the lowermost image) is in progress, but not yet to my satisfaction. The complete castle's octree ist stored in 2.1MB on Hard disk. Title of the game will be Outstar. You can soon check out http://www.outstar.net . (The 20 fps in the screenshots is because I captured them on my notebook rather desktop PC)










Donnerstag, 22. Mai 2014

Voxel Engine Update - 18 Level Octree & Copy Paste in Action

Copy and Paste works well now. Areas of voxels can be grouped to new entities for placing them in the scene. The size of the entire voxel data of the screenshot below on disk is just 1.3MB, which is quite OK. Thanks to my new graphics card, voxel raycasting and terrain rendering also runs smoothly at 60+ fps at full HD resolution now. Also worth a note: The raycasting method is updated to allow octrees greater than 16 levels - the scene below is created in an 18 level octree. Now, problems with precision begin to arise, which need to be solved.




Dienstag, 13. Mai 2014

Quadric Mesh Simplification with Source Code

In the past days I have written a quadric based mesh simplification program. After searching the internet I couldnt find any code that was free to use, not unnecessarily bloated, fast and memory efficient, even the quadric based method is soon 20 years old. I therefore decided to write one myself.

Features / Summary:
  • Threshold based, therefore faster than sorting based methods
  • Since the Quadric Matrices are symmetric, only 10 elements are stored & computed per Matrix instead of 16
  • Non-closed meshes are supported by extra treating mesh borders
  • Simplifies 2.000.000 triangles to 20.000 triangles in 3 seconds on a Core i7
  • MIT License
  • MS Visual Studio 2012 , C++
Update Sept.20th 2014 : improved quality of reduced borders

The result is short and easy to use in case you need to adopt it to your project. You can fetch the C++ Project with source here: (about 300 lines for the main part, contained in Simplify.h )

Download Source and Data

The code is about 4x-7x faster than Meshlab, which is already fast. Using multi-core programming, it could even be faster.

Here a comparison along Meshlab, QSlim and this method:


Program output (left) and Meshlab (right). Note that Meshlab produces floating teeth and looses details around the eyes, nose and the mouth region. Reduction was 85k -> 3k Triangles.


Original (left) this code (middle) and meshlab (right)


Here another comparison: Program output (left) and Meshlab (right).