Cgltf loader extension

This Dev Dairy is so I can track the development of this little extension.

The primary goals are:

  • Runtime gltf and glb loader
  • Ability to create mesh objects from the gltf/glbs
  • Use the animations in the files

This can already be achieved for prebuilt gltf/glbs, but this is specifically for runtime capabilities. Additionally, this loader will then be improved or added to a blockbench bbmodel loader, so that raw bbmodels can be utilized as-is.

The cgltf loader should be fairly quick to build. The repo will be here:

11 Likes

Ok. Initial loader pass is in. Will start on loading buffers into meshes.

@britzl - couple quick q’s if you have time:

  1. I have tried in the past to generate a gameobject with a component on the fly from C++ side using some of the API systems without much success. Is this possible?

  2. Alternatively, I used a gameobject factory as a “gameobject pool” to generate components from a gltf file (each gltf node) and I know its not great performance would there be an alternative to this?

Thanks in advance.
A possible solution was to make a structured buffer, so that all the mesh nodes (primitives) could packed with their transforms and the shader has a couple of inputs (offset, etc) to render the mesh. This is very custom, but it might be reasonably easy to implement.

1 Like

You can spawn game objects from factories in C++ using dmGameSystem ::CompFactorySpawn().

You can also create empty game objects using C++ using dmGameObject::New() but I don’t think we currently have a way to create components and assign them to game objects. @JCash ?

1 Like

Yeah, thats what I was attempting to do, I think I might have asked this previously.
I had a method like this:

static dmGameObject::HInstance SpawnMesh(dmVMath::Point3 position, dmVMath::Quat rotation, dmVMath::Vector3 scale)
{
    if (m_Meshes.Full())
    {
        dmLogWarning("Meshes buffer is full, skipping spawn of new mesh.");
        return 0;
    }
    uint32_t index = dmGameObject::AcquireInstanceIndex(m_MainCollection);
    if (index == dmGameObject::INVALID_INSTANCE_POOL_INDEX)
    {
        dmLogError("Gameobject buffer is full. See \`collection.max_instances\` in   game.project");
        return 0;
    }
    dmhash_t meshid = dmGameObject::ConstructInstanceId(index);
    dmGameObject::HPropertyContainer properties = 0;
    dmGameObject::HInstance instance = dmGameSystem::CompFactorySpawn(m_FactoryWorld, m_MeshFactory, m_MainCollection,
                                   index, meshid, position, rotation, scale, properties);
    m_Meshes.Push(instance);
    return instance;
}

Note: this is non-working code so readers please dont attempt to use!! :slight_smile:

I think if the gameobject I spawned was a template that had a mesh, it might work? Dunno. Will tinker. Any ideas would be great :slight_smile: … Im prob hacking into the void a little.. so soz if this is outside general use.

My alternate solution might be far simpler and probably more robust over time.

Thanks.

You shouldn’t call dmGameObject::AcquireInstanceIndex(m_MainCollection);.

Instead call dmhash_t meshid = dmGameObject::CreateInstanceId(); and dmGameSystem::CompFactorySpawn(..., meshid, ...).

1 Like

I also just did the update (1.12.0) for this in our C++ version (wip) of the side scroller tutorial, where we spawn game objects (from a factory):

4 Likes

Ok dang! Awesome @Mathias_Westerdahl - I’ll give it a whirl. Thanks!

Just so Im on the right page - I wont really need the proxy collection setup right?

Ive got a factory that can instance a gameobject that has a mesh (dummy) on it. So I can instance that, populate its mesh buffer and the go transform, and that should work as a node quite well (setting parents and children etc).

So from my understanding (as Im hacking it out atm) if I remove most of the LoadProxyCollection code, and just focus on factory + starmesh instancing, this should be gtg?

No, there’s no need for a collection proxy.

Yeah, then you need to do something like this:

The ctx->m_LevelCollection should be the collection where the game object is spawned. You can grab the collection from the calling script (assuming you are running your code from a Lua function):

        dmGameObject::HInstance sender_instance = dmScript::CheckGOInstance(L);
        dmGameObject::HCollection collection = dmGameObject::GetCollection(sender_instance);
2 Likes

Thanks @britzl and @Mathias_Westerdahl I think I have the mesh game object instancing correctly.
The gos are spawned and when I add a model with a box, it appears!! nice.
Now. Im creating buffers to store the verts, uvs and colors using CreateBuffer (on C++ api side), and now I effectively want to do the equivalent of this lua in C++:

local res_path = go.get("#mesh", "vertices")

resource.set(res_path, buffer)

So that I can assign the newly created buffer on the c++ side.
There are some methods (like GetPath) and such, but Im not quite certain how this would be done.

1 Like

Note that the “res_path” is a hash, i.e. the resource path hash, so changing that with “resource.set()” will change the actual resource (the buffer). So, it’s not changing the buffer only for the “#mesh” component, but for all components using that specific buffer resource.

In my example project, I create new buffer resources from scratch:

and then set this resource, to my mesh component:

Note, that api is not available, but in 1.12.1, we’ll add a proper dmGameObject::SetPropertyXXX() which will allow you to manipulate properties such as this.

5 Likes

Ok. So then is there a way to just change the mesh buffer of a factory instanced go?

Ideally if there is an api already. If not, I can push this back out to lua and use that api, but I suspect that wont be great to do so.

Im currently using:
dmBuffer::Create(stream_size, streams_standard_decl, stream_count, &buffer);

To make a buffer, then I fill it. I really just want to replace the instanced mesh buffer with these.

1 Like

What I did before I had these functions (including the upcoming dmGameObject::SetProperty()), was to set them via Lua resource.create_buffer().
It takes a buffer handle, so you should be able to use dmScript::PushBuffer(), to pass it from C to Lua.
Or perhaps do vice versa, create it from Lua, and pass it to C.

Note that when pushing buffers from C to Lua, you need to specify who owns it:

dmScript::LuaHBuffer luabuf(buffer, dmScript::OWNER_LUA);
dmScript::PushBuffer(L, luabuf);

But, if you give it another day, I think @britzl is done with the dmGameObject::SetProperty() PR and it all becomes much much simpler :slight_smile:

2 Likes

Ok. cool. Hrm… I might restructure things a little. I could add callbacks during node and mesh creation which can then do the building in lua which shouldnt be too bad.

Actually prob the easiest is to get the whole cgltf data into a lua table, then process on lua side - one of my old gltf loaders does this, so I can prob lift a bunch of code.

Once the api is sorted, I can then port it across - shouldnt be too hard. I think this is the easiest route for the time being - Ive also done many mesh generation bits in lua so its less work overall.

Ok. Cool.. thanks for the info. Should have something soon.


[ Update ]

Ok.. its going to be a bit different than a complete Lua. You see in my Dim editor I made a luajit cgltf loader that is nice and fast, but it uses sokol-luajit for all the rendering and so on (my own little engine). Because that loader works with many many models, I am going to bring that in, but it means some changes - so cgltf extension will become basically a pass-thru to raw cgltf calls (thus mimmicking my original dll interface - thus I can reuse alot of my loader code in lua).

This should mean, I should have something working tonight (next few hours fingers crossed). I need to map gameobjects, meshes, textures and shaders.. but thats shouldnt (fingers crossed) be too complex. Will post anything here once its up and running, or if I hit some more hurdles.

The big benefit of this method, is that new C++ api integration will be simpler (just put it in the extension where the loader calls are).

2 Likes

Ok first pass is in - Lua interface, and cgltf general loading and parsing.

Next: Make some game objects from a factory, set their buffers (pos, uvs and colors) and shazzam. Basic gltf runtime loader sorted.

1 Like

Ok finally have some complex gltf vert data loading.

Im building materials, so I will try to add in the textures next.
Note: Dont try animated or large models. Still many things needing attention :slight_smile:

2 Likes