User interface

edited February 2015 in General discussions
Hi,
just want to ask what UI libraries are you using (if any) in your orx project?
I was searching for some and found for instance libRocket (https://github.com/libRocket/libRocket) which should be easily integrated. Anyone has experienced with it? Or with some other libraries?

Thanks

Comments

  • edited February 2015
    I think it'd be great if we had a public Orx-SomeUIFramework integration project. The integration does not necessarily have to be deeply rooted into the engine and its config system, even the ability to display UI elements on top of the Orx context would be a nice start.
  • edited February 2015
    I've already integrated libRocket with orx

    https://bitbucket.org/volcanomobile/orxrocket

    but it's a bit of an overkill, and there is currently no way to animate Rocket objects (needs a deeper integration with orxOBJECT)

    I'm using a very light home made UI lib for my games which trigger some orx TRACK to do stuffs.

    Philippe
  • edited February 2015
    Nice :)

    Do you have a licence in mind for this piece of code?
  • edited February 2015
    license is : I don't care 4.0
  • edited February 2015
    4.0 is a bit too intrusive, don't you think? It requires your users to not care that you don't care.

    In all seriousness, though, I tend to use the What The Fuck Public License for most of my projects, for this purpose.
  • edited February 2015
    Thanks for the replies,
    I would welcome some UI in orx, but I think it isn't so straightforward to implement it. Because you know, everyone need something different, someone need only basic UI, some other needs some very-custom-specialized controls. The easiest way is to use some UI lib and only write some binding.

    Anyway, I was looking to libRocket and to your binding to orx, lydesik. I would like to know your opinion on the library. What's wrong with animation problem? How extensible the library is (that is, if I want to make some custom control, is it possible with little effort or some huge amount of work is needed)?
    I've also found TurboBadger (https://github.com/fruxo/turbobadger) which looks also interesting.
    And also found NoesisGUI (http://www.noesisengine.com/noesis_gui_features.htm) which proprietary. But I would rather use OSS library than a proprietary one.

    From skimming through some documentation in libRocket and the TurboBadger (as I haven't the time to try them) I like the TurboBadger better. Have someone use it?

    As I written in the first post, if someone has some experience with some other framework, just throw your opinion here.
  • edited February 2015
    enobayram wrote:
    4.0 is a bit too intrusive, don't you think? It requires your users to not care that you don't care.

    In all seriousness, though, I tend to use the What The Fuck Public License for most of my projects, for this purpose.

    what The Fuck v1 or v2?

    On topic: you can use normal objects, just reparent them to the camera and set them to a group that will be rendered last.
  • edited February 2015
    In the past someone started a CeGUI integration, but even if we dug enough and found it, it's probably now too old to be of any real value.
    In addition to Lydesik's libRocket integration, some other users claimed to have had an ImGui approach (myself I was looking maybe into using blendish/oui for another iteration of ScrollEd, so for the editor, not for in-game UI).

    I personally have a similar approach than Knolan and Lydesik: I'm using regular objects that are children of cameras for UI. My UI code is about 15-20 lines that will do picking and trigger tracks on the picked objects that are associated with them in config. That's about it. All the logic happens in those track and I use it for buttons, labels, scrolling texts, etc etc. I never had any need for typical UI "widgets", but I guess it's all contextual and depends heavily and one's game's needs.

    It looks something like this, using Scroll:
    void Game::UpdateInteraction(const orxCLOCK_INFO &_rstInfo)
    {
      orxVECTOR     vMousePos, vPickPos;
      orxU64        u64PickedID = 0;
      ScrollObject *poPickedObject = orxNULL, *poInteractionObject;
    
      // Gets world mouse position
      if(orxRender_GetWorldPosition(orxMouse_GetPosition(&vMousePos), orxNULL, &vMousePos) != orxNULL)
      {
        // Stores it
        orxVector_Set(&mvMousePosition, vMousePos.fX, vMousePos.fY, orxFLOAT_0);
      }
    
      // Gets picking position
      orxVector_Set(&vPickPos, mvMousePosition.fX, mvMousePosition.fY, -orxFLOAT_1);
    
      // For all pickable groups
      for(orxU32 i = 0, u32Number = orxConfig_GetListCounter("PickGroupList"); i < u32Number; i++)
      {
        // Picks object in it
        poPickedObject = PickObject(vPickPos, orxString_GetID(orxConfig_GetListString("PickGroupList", i)));
    
        // Found?
        if(poPickedObject)
        {
          // Updates picked ID
          u64PickedID = poPickedObject->GetGUID();
    
          // Stops
          break;
        }
      }
    
      // Gets current interaction object
      poInteractionObject = (mu64InteractionID != 0) ? GetObject(mu64InteractionID) : orxNULL;
    
      // New picking?
      if(u64PickedID != mu64InteractionID)
      {
        // Has current interaction object?
        if(poInteractionObject)
        {
          // Adds contextual track
          poInteractionObject->AddConditionalTrack("OnLeaveTrack");
        }
    
        // Picked new object?
        if(poPickedObject)
        {
          // Adds conditional track
          poPickedObject->AddConditionalTrack(orxInput_IsActive("Action") ? "OnClickTrack" : "OnEnterTrack");
        }
    
        // Stores new interaction ID
        mu64InteractionID = u64PickedID;
      }
      else
      {
        // Has interaction object?
        if(poInteractionObject)
        {
          // Change of action?
          if(orxInput_HasNewStatus("Action"))
          {
            // Adds conditional track
            poInteractionObject->AddConditionalTrack(orxInput_IsActive("Action") ? "OnClickTrack" : "OnReleaseTrack");
          }
        }
      }
    }
    

    A few details:
    - mvMousePosition and mu64InteractionID are member variables of my Game class which derives from Scroll
    - "Action" is the input I listen for UI interaction in this case (usually left click in both menus and game as well as left click + enter in menus, as I also support keyboard menu navigation that moves the mouse cursor between interactable entries)

    And here's a generic base class for my menu entries:
    [O-MainMenuEntry@ScrollObject]
    IsEntry       = true
    ParentCamera  = MainCamera
    UseParentSpace= none
    Text          = @
    Color         = (0, 0, 0)
    OnEnterTrack  = T-ScaleIn
    OnLeaveTrack  = T-ScaleOut
    

    As well as a few actual menu entries:
    [O-ContinueGameEntry@O-MainMenuEntry]
    Position      = (0, -60, 0.5)
    String        = Continue
    
    [O-NewGameEntry@O-MainMenuEntry]
    Position      = (0, 0, 0.5)
    String        = New Game
    OnReleaseTrack= T-Start
    
    [O-SetUserEntry@O-MainMenuEntry]
    Position      = (0, 60, 0.5)
    String        = Set User Name
    OnReleaseTrack= T-SetUser
    
    [O-ExitEntry@O-MainMenuEntry]
    Position      = (0, 120, 0.5)
    String        = Exit
    OnReleaseTrack= T-Exit
    

    Thanks for the links about TurboBadger and NoesisGUI, I'll have a look into them, just out of curiosity.
  • edited February 2015
    Thanks for the replies,
    was also looking for the CEGUI and it looks very good. I don't think that approach "using only orxObjects" would be sufficient for me. I would need also some more complicated dialogs, widgets, etc. and don't want to implement all from the scratch. So for now will look at CEGUI. Do you think that it would be possible to use CEGUI's OpenGL rendered or I would need to implement the custom one on the top of the orx? (I'm talking about the in game GUI here)
    For the editor I was looking for the ImGui and I like it. Also oui-blendish looks nice.

    Thanks for the all opinions.
  • edited February 2015
    You should be able to plug CEGUI's rendering on one of the render hooks, probably orxRENDER_EVENT_STOP.
    And yes, some games have more advanced UI needs than basic buttons/dialog boxes, howver I guess I could provide an extra tutorial with a few common UI widgets. Lemme know if anyone is interested.
  • edited February 2015
    Ok, so I've tried integrating the CEGUI but got some asserts in orx. For now, I'm only bootstrapping the CEUGI's OpenGL3Renderer and not drawing any GUI. But in the orxDisplay.c in orxDisplay_GLFW_PrepareBitmap() (around the line 1494), I've got the GL error 0x502 assert on the line
    glUNIFORM(1iARB, sstDisplay.pstDefaultShader->iTextureLocation, sstDisplay.s32ActiveTextureUnit);
    

    Error:
    [10:10:19] [ASSERT] [orxDisplay.c:orxDisplay_GLFW_PrepareBitmap():1494] [ASSERT] : <eError == GL_NO_ERROR && "OpenGL error code: 0x502">
    
    I've read the OpenGL doc about glUniform (https://www.opengl.org/sdk/docs/man/html/glUniform.xhtml) but this error could happens in multiply cases. As I'm not OGL expert, I really don't know where could be the problem.

    Do you know where could be the problem?

    Thanks
  • edited February 2015
    So I think I solved it. We need to set the actual shader. Adding
    glUseProgramObjectARB(sstDisplay.pstDefaultShader->hProgram);
    glASSERT();
    
    before using glUNIFORM(...) solve the problem.

    But as I'm not the expert I don't know if it is right way to solve it.
  • edited February 2015
    I guess it depends where (as in between which steps in the frame sequence) you bootstrapped CEGUI's render code. :)

    Something you can try is, when CEGUI's rendering is done, to call orxDisplay_SetVideoMode(orxNULL). This will not modify the video mode per se but will reinit all rendering internals, including the activation of the default shader.
  • edited February 2015
    I've bootstrap it at the application init.

    This error happens before rendering any CEGUI content. Why do you think that adding glUseProgramObjectARB() before setting the shader params isn't appropriate fix? :) Because according to the https://www.opengl.org/wiki/GLSL_:_common_mistakes#glUniform_doesn.27t_work you should always set the active shader. Trying to call orxDisplay_SetVideoMode(orxNULL) in every instance where some shaders could be modified doesn't sound right to me. Not mentioning the size/execution time of the orxDisplay_SetVideoMode() vs glUseProgramObjectARB().

    edit: But from the other side, why should someone pay for glUseProgramObjectARB() if he isn't using other shaders? The question is how slow the glUseProgramObjectARB() is? Maybe only some debug check could be added that will test the current acctive program? Or call glUseProgramObjectARB() before rendering the frame?

    edit2: After more thinking, I agree with your previous answer, but would like to have some debug check as mentioned above (that the default shader used is the right one).

    Thank You
  • edited February 2015
    Ah I see, I didn't know you had to call an init function for CEGUI, I thought only a Render() call was necessary.

    As for the way it works, orx setups the render once in orxDisplay_Init() (which is called before you bootstrap CEGUI), and doesn't expect anyone to change anything with that has been setup. Or more precisely, it expects to have good citizen: whichever is changed by something else is expected to be put back as it was when that something is done executing, ie. that the default shader has been put back the way it was setup in the first place.

    The orxDisplay_SetVideoMode(orxNULL) is only there to help reset things when some custom render call isn't a good citizen and doesn't restore everything as it was.
    In addition to this, it also has better performances than querying OpenGL about all the current states to back them up locally as usually all the glGet* calls imply a CPU->GPU sync point, whereas orx caches those data on the CPU side when setting them up in the first place.
  • edited February 2015
    I guess debug checks could be an option, but having them happening all the time might really hinder performances due to the same CPU->GPU sync points, so I'm not totally convinced.
  • edited February 2015
    The orxDisplay_SetVideoMode(orxNULL) is ok for now. Yes, the debug check could slow down the system but it could spot the bugs which would be hard to find.

    I think that we may need to modify orxDisplay_SetVideoMode(orxNULL) (null parameter means the path in this function where null is handled) to reset some more parameters to be able to use CEGUI OpenGL renderer (or maybe not if the custom renderer would be provided, see below). And there we could set the default shader and no debug checks are needed then.

    Anyway, I was trying to make the CEGUI working but I can't. I have just root window (which is empty, it's logical root concept) and it crashed in nvidia OpenGL driver. When using integrated GFX the the crash isn't there, but only black screen is shown. I have logged the OpenGL calls when rendering the GUI and when not. But I don't know which could cause this. I'm attaching the log maybe some could spot something.
    ...
    glGetError()=GL_NO_ERROR 
    glDrawElements(GL_TRIANGLE_STRIP,6,GL_UNSIGNED_SHORT,00000000) GLSL=1  Textures[ (0,3) (1,5) (2,4) (3,11) (4,6) (5,8) ] 
    glGetError()=GL_NO_ERROR 
    glFlush()
    glGetError()=GL_NO_ERROR
    
    -- CEGUI calls
    
    glEnable(GL_SCISSOR_TEST)
    glEnable(GL_BLEND)
    glBlendFuncSeparate(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA,GL_ONE_MINUS_DST_ALPHA,GL_ONE)
    glUseProgram(9)
    glBindBuffer(GL_ARRAY_BUFFER,3)
    glBufferSubData(GL_ARRAY_BUFFER,0,0,00000000)
    glViewport(0,0,800,600)
    glScissor(0,0,800,600)
    glUniformMatrix4fv(23724032,1,false,[2.799038,0.000000,0.000000,0.000000,0.000000,-3.732051,0.000000,0.000000,0.000000,0.000000,1.666667,1.000000,-1119.615234,1119.615112,373.204834,1119.615112])
    glBindVertexArray(2)
    glUseProgram(0)
    
    -- CEGUI end
    
    glCheckFramebufferStatusEXT(GL_FRAMEBUFFER)=GL_FRAMEBUFFER_COMPLETE 
    glGetError()=GL_NO_ERROR 
    glActiveTextureARB(GL_TEXTURE6)
    glGetError()=GL_NO_ERROR 
    glBindTexture(GL_TEXTURE_2D,2)
    glGetError()=GL_NO_ERROR 
    glUseProgramObjectARB(1)
    glGetError()=GL_NO_ERROR 
    glUniform1iARB(23724032,6)
    glGetError()=GL_NO_ERROR 
    ...
    

    The lines between "-- CEGUI logs" and "-- CEGUI end" are the calls made by CEGUI. If I don't render the CEGUI, these lines aren't there and anything is ok.

    Maybe I should make a custom CEGUI renderer for the orx. That was also my plan at the beginning but want to give a shot for the OpenGL renderer. Apparently it isn't working (or I'm doing it wrong :) ).
  • edited February 2015
    for reference, here is the code I call before rendering libRocket
            GLint box[4];
            GLint elementBufferArray;
    
            // force to draw pending geometry
            orxDisplay_SetVideoMode(orxNULL);
    
            glGetIntegerv(GL_SCISSOR_BOX, box);
            glASSERT();
            glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, &elementBufferArray);
            glASSERT();
    
            glEnable(GL_BLEND);
            glASSERT();
            glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
            glASSERT();
    
            glMatrixMode(GL_PROJECTION);
            glASSERT();
            glPushClientAttrib(GL_CLIENT_VERTEX_ARRAY_BIT);
            glASSERT();
            glBindBuffer(GL_ELEMENT_ARRAY_BUFFER_ARB, 0);
    
            spContext->Render(); // draw libRocket
    
            glPopClientAttrib();
            glASSERT();
    
            // reset internals
            orxDisplay_SetVideoMode(orxNULL);
    
            glEnable(GL_SCISSOR_TEST);
            glASSERT();
            glScissor(box[0], box[1], box[2], box[3]);
            glASSERT();
            glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, elementBufferArray);
            glASSERT();
    

    maybe it can help
  • edited February 2015
    Hi,
    thanks for the reply.

    I've tried your code but without the luck. I would just go with implementing the custom renderer then and see how it goes.
  • edited February 2015
    Adding some notes to the log:
    ...
    glGetError()=GL_NO_ERROR 
    glDrawElements(GL_TRIANGLE_STRIP,6,GL_UNSIGNED_SHORT,00000000) GLSL=1  Textures[ (0,3) (1,5) (2,4) (3,11) (4,6) (5,8) ] 
    glGetError()=GL_NO_ERROR 
    glFlush()
    glGetError()=GL_NO_ERROR
    
    -- CEGUI calls
    
    glEnable(GL_SCISSOR_TEST)
    glEnable(GL_BLEND)
    glBlendFuncSeparate(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA,GL_ONE_MINUS_DST_ALPHA,GL_ONE)
    
    *** CEGUI sets its own shader ***
    
    glUseProgram(9)
    glBindBuffer(GL_ARRAY_BUFFER,3)
    glBufferSubData(GL_ARRAY_BUFFER,0,0,00000000)
    glViewport(0,0,800,600)
    glScissor(0,0,800,600)
    glUniformMatrix4fv(23724032,1,false,[2.799038,0.000000,0.000000,0.000000,0.000000,-3.732051,0.000000,0.000000,0.000000,0.000000,1.666667,1.000000,-1119.615234,1119.615112,373.204834,1119.615112])
    glBindVertexArray(2)
    
    *** CEGUI deactivates shaders (back to fixed pipeline) ****
    
    glUseProgram(0)
    
    -- CEGUI end
    
    *** orxDisplay_SetVideoMode(orxNULL) should be called here ***
    
    
    *** This below is the start of a new render pass, ie. new frame or new viewport ***
    
    glCheckFramebufferStatusEXT(GL_FRAMEBUFFER)=GL_FRAMEBUFFER_COMPLETE 
    glGetError()=GL_NO_ERROR 
    glActiveTextureARB(GL_TEXTURE6)
    glGetError()=GL_NO_ERROR 
    glBindTexture(GL_TEXTURE_2D,2)
    glGetError()=GL_NO_ERROR 
    glUseProgramObjectARB(1)
    glGetError()=GL_NO_ERROR 
    glUniform1iARB(23724032,6)
    glGetError()=GL_NO_ERROR 
    ...
    

    I have to admit I'm still a bit fuzzy on how the CEGUI code gets injected in this sequence, but things are not cleaned after it gets executed (back to the fixed pipeline and the internal matrices are not reset either).
Sign In or Register to comment.