So I made a styling language for OpenGL Apps

Started by
2 comments, last by CosmicMeatloaf 6 years, 3 months ago

The title is vague, so I'll explain.

I was frustrated with UI dev (in general), and even more-so when working with a application that needed OpenGL with embedded UI. What if I wanted to make a full application with OpenGL? (Custom game engine anyone?)

Well I did want to. And I'm working on it right now. But it started me onto making what I think is a great idea of a styling language;

I present KSS (pron. Kiss) The multitudes more programmable version of CSS.... (only for desktop dev)


/*
It has all the 'normal' styling stuff, like you'd expect.
*/

elementName {
	/*This is a styling field.*/
    font-color: rgb(0,0,0),
    font: "Calibri",
    font-size: 12px
}

.idName {
   color: rgba(255,255,255,0)	
}

uiName : onMouse1Click{
   color: vec3(0,0,0)
}

BUT 

It also has some cool things. I've taken the liberty to add variables, templates (style inheritance), hierarchy-selection, events (as objects), native function calls, in-file functions.


var defaultColor: rgb(0,0,0)
var number : 1.0

/*Types include: rgb, rgba, vec2, vec3, vec4, number, string, true, false, none (null), this*/

fun styleSomeStuff{
  .buttons{
    color: defaultColor,
    text-color: rgb(number,255,number)
  }
}

template buttonStyle{
	color: rgb(255,255,0)
}

.buttons{
    use: buttonStyle, otherTemplateName /*copies the templates styling field*/
    color: defaultColor;
}

.buttons : onMouse1Click{ /* events not assigned as a value are initialized when read from file*/
	styleSomeStuff();*/* call the in-file function*/
  	*nativeFunctionCall();/*call a native function that's binded*/
  
  	var a : 2; /*assign a variable, even if not initially defined.*/
}

/*storing an event in a 'value' will allow you to operate on the event itself. It is only ran when 'connected', below.*/
val ON_CLICK2 = .buttons : onMouse2Click{
	use: templateName
}

connect ON_CLICK2;
disconnect ON_CLICK2; /*you can make a function to do the same.*/

fun connectStuff{
   connect ON_CLICK2;
}

 

But wait, you ask... what If I need to select items from a hierarchy? Surely using element names and id's couldn't be robust enough!

Well:


/*We use the > to indicate the item next element is a 'child' to itemA in the hierarchy*/

itemA > itemAChild : onMouse1Click{

}

.itemId > .itemAChild > itemAChildsChild{

}

/*want to get all children of the element at hand?*/
elementName > .any{
	/*this will style all the elements children*/
}

/*What about if we want to use conditional styling? Like if a variable or tag inside the
element is or isnt something?*/

var hello : false;
var goodbye : true;
itemA [var hello = false, var goodbye != false] { /*passes*/
}

itemA [@tagName = something]{ /*passes is the tag is equal to whatever the value u asked.*/
}

The last things to note are event-pointers, how tagging works, 'this', and general workflow.

Tagging works (basically) the same as Unity, you say @tagName : tagValue inside a styling field to assign that element a tag that's referable in the application.

You have the ability to refer to any variable you assign within the styling sheet, from say.. source-code. The backend. As such, being able to set a variable to 'this' (the element being styled) allows you to operate with buttons who are currently in focus, or set the parent of an item to another element.

All elements are available to use as variables inside and outside the styling sheet, so an event can effectively parent an element or group of elements to a UI you specify. Ill show that in a figure below,

Event pointers are so that you can trigger an event with a UI component, but affect another, below-


/*We use the -> to point to a new element, or group of elements to style.*/
.buttons : onMouse1Click -> .buttons > childName{
    visible : false
    parent: uiName;
}
/*
In this case, we style something with the name "childName"
whos parented to anything with the id 'buttons'.
Likewise, we if there was a element with the name 'uiName', 
it would parent the childName element to it.
*/

 

Lastly: The results/workflow.

I'm in the process of building my first (serious) game engine after learning OpenGL, and I'm building it with Kotlin and Python. I can bind both Kotlin and Python functions for use inside the styling sheet, and although I didn't show the layout-language (cause its honestly not good), this is the result after a day of work so far, while using these two UI languages I've made (In the attachments.)

It's also important to note that while it does adopt the CSS Box-model, it is not a Cascading layout.

That's all I had to show. Essentially, right now Its a Kotlin -backed creation, but can be made for Java specifically, C++, C#, etc. I'm planning on adding more into the language to make it even more robust. What's more, it doesn't need to be OpenGL backed- it can use Java paint classes, SFML, etc etc. I just need to standardize the API for it. I guess what I'm wondering is, if I put it out there- would anyone want to use it for desktop application dev? 

P.S. Of course the syntax is subject to change, via suggestion.

P.S.[2] The image in the middle of the editor is static, but wont be when I put 3D scenes in the scene-view.

unknown (1).png

Kotlin, Java, Lua, Python

Advertisement

To be fair the situation of UI libs for Java that can be used in games is pretty annoying right now.

You have stuff like TWL, which sounds cool and all but it has its own renderer, based on GL 1.1 of all things, and you'd have to code one for it if you ever want to use it on say, a core GL context or Vulkan. Of course, if you use core GL or Vulkan, it is going to be a total pain because the whole lib has the ideas of vertex3f, color3f and friends very ingrained in it, which doesn't maps at all to a modern solution. 

There is NiftyGUI, which is, again, pretty cool and all but again, it insists on rendering itself, which causes problems integrating it into an existing pipeline, since all state tracking you make goes out of the window when you pass the wheel to NiftyGUI. On the good side, it does has a core GL backend.

But as you can see the whole concept of "UI rendering itself" is not good. You have various backends in NiftyGUI: Compat ones, core ones, LWJGL ones, JOGL ones, etc. And thats the rendering part, there is also input handling, which it is also a pain to incorporate into an existing pipeline (and IIRC it tries to do sound too, it's big and messy).

LWJGL did, in my opinion, the most sane thing and incorporated bindings for Nuklear, which it can be quite a pain in the ass since it is an immediate UI lib (which gets pretty annoying when doing more complex UIs) and the only documentation is in the source code, and it flat out stops after you scrolled through 1/4 of it.

On the other hand, the big, big plus of Nuklear is that it knows jack shit of what you're running on. You can use GL, Vulkan, D3D, some abstraction layer like GLFW or SDL for input, or just polling win32 events, it doesn't matters. It provides a tiny API in which you pass input events to it, and it generates memory chunks with vertices to draw for you, that you send to your rendering pipeline however and whenever you want. Integration becomes **super** easy this way. You can draw right away immediate style, or VAO/VBO core style, or deffer for later, or whatever. Your call.

So in my opinion, if you offer something that can be easily integrated like Nuklear, and and the same time being easier to use (since it'd be entirely Java side), you could have an audience for this stuff.

EDIT: As a side note, using any of the standard libs like Swing or JavaFX with a GL app is also a pain in the butt because the implementations usually depend on big ass non portable hacks to make it work, like the render target hijacking method in JavaFX, or the mandatory compat profile GLCanvas for SWT. And performance just plain sucks in these cases.

"I AM ZE EMPRAH OPENGL 3.3 THE CORE, I DEMAND FROM THEE ZE SHADERZ AND MATRIXEZ"

My journals: dustArtemis ECS framework and Making a Terrain Generator

That's quite the insightful reply;

The way I see it- this early in the project I have the choice of creating API assisted interfaces so that ( at least in Java or Kotlin, idk about C++ as much) Users can either use currently made interfaces (say, programmed by me) or implement their own interface for the sake of embedding easier. Which is to say it could have the positive qualities of Nuklear if so desired- while maintaining official techniques of rendering; this and guidelines (API Docs and some tutorials) for implementing your own interfaces, or using your own datas (say a custom Vec3 class, or Vector3f in the case of LWJGL).

The reason I think this language can be "the go-to" is that it doesn't dictate rendering technique- it's mostly intended to be a means of dictating property changes within a UI class that you define. Of course I can make (and link to in documentation) the actual UI libraries, and rendering source if someone wants to use those items; but as I see it now- the KSS lib itself should probably be able to work with different/definable data-classes, and I should probably create some interface classes so that people can either "willy nilly" implement how the language affects their UI, or use a stricter interface implementation

Does that all sound kosher?

EDIT: You're definitely right, its the UI library situation for the JVM is Super XXL Annoying 

Kotlin, Java, Lua, Python

This topic is closed to new replies.

Advertisement