2023-12-23

Project Raygine #5

It feels like a small victory when things are coming together, so here's a short update;

New inspector view showing a selected scene asset with some content: the scene's object, pointing to the root entity which has a child. Each scene entity has a button to add new children to that entity.

On the screenshot, you can see the inspector view when the new scene asset is selected. As can be seen, each entity here is a separate object in the scene asset. When the scene is being deserialized, an optional "on_deserialized" method on the object is called to allow the object to do some initialization handling. This is also called when the asset is created and in case of a scene asset, it will create the root entity.

I don't intend to use this view on the scene asset to edit the scene, but it's a good way to see how the asset is structured at the moment, as the scene view and hierarchy is not switched yet to the new serialization system. It's also a testing ground for the features of the new serialization system: One feature I want is the customizability of the inspector view. In this example, the scene entity type definition declares a custom GUI function that is called when the entity is being displayed in the inspector. The code looks like this:

---@class scene_entity : object
---@field parent scene_entity|nil
local scene_entity = {}
scene_entity._mt = { __index = scene_entity }

type_registry:define(T.raygine.scene_entity)
    :set_metatable(scene_entity._mt)
    :add_attribute("name", T.string, "New entity")
    :add_attribute("parent", T.raygine.scene_entity, nil, true)
    :add_inspector_callback(function(obj, path_id)
        local width = imgui.GetContentRegionAvail()
        if imgui.Button("Add child entity##" .. path_id, width - 10) then
            local asset = assetdb:load(obj)
            if asset then
                local child = asset:add_object(T.raygine.scene_entity)
                child.parent = obj
                return true
            end
        end
        return false
    end)

The type_registry is a global registry that holds all type definitions. The type_registry:define() function returns a type_definition object that can be used to define the type.

There's a Lua specific pattern that's being used; In a regular OOP language, you would need to have a factory method to produce an object of said type. In Lua, this isn't necessary: A table can be created and have a metatable assigned to it. The metatable is practically the class. This is quite reverse to how it works in most other languages, but I believe it makes sense here.

The add_attribute functions should be fairly self explanatory. The last parameter is a boolean that indicates if the attribute is a reference. This is used to determine if the attribute should be serialized as a reference or as a value. I am not so happy with this approach, I only realized late into the design that a type can be a pointer... I will probably add a specific method to the type_definition to add a reference attribute.

Another special case here is the "add_inspector_callback" function. It adds a callback to the attributes that is called when the attribute is being displayed in the inspector. This allows to customize the inspector view for specific types, as in this case, to add a button to add a child entity to the entity.

If you are used to unity, you would probably expect that this UI specific code is stripped out of the game because any reference to editor specific methods would break compilation; Since Lua is a scripting language without any kind of typing, this isn't the case though. The function is compiled, but never executed in a runtime build. It is a little bit annoying that this code is still in the build, but I think it's a fair tradeoff for the flexibility it gives - and maybe it could be stripped through some build logic in the future.

The next things I need to make work before reworking the scene view is the reference system; I need to show a popup when linking a reference to another asset. There needs to be some filtering and I want to also have a setting for references, that they can only make "local" references - so references only to the same asset. For example, scene entities shouldn't have children and components that are located in different assets.

So still much left to do on the serialization rework before I can start working on the scene and runtime mode again - and then there's also the build system 🫠

🍪