Dienstag, 28. Juli 2015

Github Code

I shifted some of my code to GITHUB. You can find it here https://github.com/sp4cerat

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.