Posts
Search
Contact
Cookies
About
RSS

G3N introducing the Application more 3d with G3N and golang

Added 1 Jun 2018, 9:54 p.m. edited 18 Jun 2023, 7:31 p.m.
First off G3N has a shiny new website, (and yes it does rock!) hopefully in future it will evolve into a great source of tutorials and demo code... As we left things last time while we quickly had our shiny textured doughnut up and running, there were some issues meaning we were using less than idiomatic Go, fortunately G3N has this all figured out for us, with its Application structure.
main() {
    anApp, err := application.Create(application.Options{
        Title: "Tut2",
        Width: 800,
        Height: 600,
        Fullscreen: false,
        LogPrefix: "TUT2",
        LogLevel: logger.DEBUG,
        TargetFPS: 60,
        EnableFlags: true,
    })

    if err != nil {
        panic(err)
    }
    
    err = anApp.Run()
    if err != nil {
        panic(err)
    }

    fmt.Printf("app was running for %f \n", application.Get().RunSeconds())
}
This is very straight forward, we create the Application with some basic options, and G3N takes care of all the boiler plate stuff, aggregating all the related fields together. Not only have we got rid of a load of boiler plate but look at the last line, we're getting information about the application but without using the anApp variable. While my initial somewhat naive attempt was to hack a user data field into Application, just to make sure I could keep track of my own data, this would basically be abusing an empty interface as if it were a void pointer, and thinking about it, this anti pattern is something that should be used sparingly... By making our own structure and embedding the Application we have a place holder for our data, which we can also use as a receiver pointer to the custom data, this isn't obvious at first glance as it could be, so lets take a closer look.
type myApp struct {
    application.Application
    shape [3] *graphic.Mesh
    x, y, z float32
}
So we've simply embedded the Application in our own structure, but how do we access our data from a callback ...
a, err := application.Create( ....
    ...
var anApp myApp
anApp.Application = *a
//setupStuff(&anApp)
anApp.setupStuff() // could be a method of myApp or a regular function of the main package
anApp.Window().Subscribe(window.OnKeyDown, anApp.onKeyDown)
anApp.Subscribe(application.OnBeforeRender, anApp.onRender)
First after creating an Application, we assign it to the appropriate field of our own application structure. Our custom structure now has all the fields of the created Application plus our extra fields. If we subscribe via this custom structure, we can set the subscriptions to our structures functions.
func (a *myApp) onRender(evname string, ev interface{}) {

    a.x = a.x + 0.016    // x is a field of our myApp instance
    ...
Because we've bound our callbacks to our custom application, we have access to our custom application without need of some empty interface abuse... this is one of those things that easier to just do than describe. I've gone over board and split the various functions for our extra data over two source files, this is just to make the point that its no problem to have for example initialisation functions in one source file and call backs in another, even if they are all part of the same structure. You should notice that there is very little in our main function, and all our various functionality can be separated out in to easy to find and navigate source files, not an issue for a simple example, but later down the line with a more complex example it could defiantly make life easier... You can grab a copy of the code here.