-
-
Notifications
You must be signed in to change notification settings - Fork 152
Description
There are some limitations of the Model API that would be nice to improve:
- There isn't a very easy way to manually render the individual nodes/meshes of a model. This can be useful if you want to use different shaders, materials, or render states when rendering some nodes, or just do the rendering yourself. Technically it's possible. You can get all the meshes in a model, you can walk the node tree and use
ModelData:getNodeMeshes(node)
to see which meshes to render. There are some downsides:- It's a little more cumbersome than I'd like.
- It does not work with animations.
- Using your own materials with a Model is pretty hard. People often have a simple OBJ model with 1 node and mesh, and want to load the texture manually and use it with the Model. It seems like this should work with
Pass:setMaterial(texture)
, but the model doesn't use the pass's material -- it uses its own material (which is usually just plain white). There is a workaround where you can load the model with{ material = false }
, but it shouldn't be necessary. - It isn't easy to create a MeshShape or ConvexShape from a "piece" of a model. There isn't an existing Model/ModelData API that can give you the triangles of a single node or mesh, and even if you get the full vertex/index buffer, you have to get the mesh draw range (or ranges, for a node) and extract that subset of vertices yourself.
- There are lots of ways to solve this. MeshShape constructor could take a Model plus a node/mesh, or a vertex range, but these feel somehow wrong. Alternatively there could be a way to query different sets of triangles from the Model.
- Also,
Model:getMesh
doesn't work because the Mesh itself references the model's entire vertex buffer and uses the draw range to reference a slice from that buffer, which newMeshShape isn't able to understand.
- Relatedly, it's also hard to just get the vertices from a Model.
ModelData
's API to return vertices is able to support arbitrary vertex formats, which makes it really difficult to "just get the UVs from the mesh" or whatever. You have to search through all the attributes, find the UV attribute, and then hope it's in a format that works for you.- This is because glTF supports arbitrary vertex formats. This might be overkill, I've been thinking LÖVR's ModelData object could convert all the vertices into a standard format on import (Model already does this and no one's complained, maybe we can just move that logic to ModelData).
- We currently do not support what glTF calls "instantiated meshes" (animated meshes referenced by multiple nodes, each potentially with a different skin or set of blend shape weights). It would be nice to support this.
- As discussed in Multiple Animated Poses Per Model #701, models can only have a single animated state right now.
Model:clone
solves this for now, but we should keep this in mind while redesigning Model-related APIs.
Here are some ideas to improve the API and solve some of these problems:
Improved Node Walking API
Model:getNodeChildren
is bad because it creates garbage. It also requires recursion to iterate?- Consider the following alternative APIs:
Model(Data):getNodeChild
--> returns index of a node's first childModel(Data):getNodeSibling
--> returns index of a node's next siblingfor node in Model(Data):nodes([mode])
--> returns a Lua iterator that iterates over all node indicesmode
could let you iterate in DFS/BFS order.- There could be other parameters like "only visible nodes" or "root node to start at" or "recurse".
- I think this could be a stateless iterator to avoid garbage.
This would let you do something like the following to walk over nodes:
for node in model:nodes() do
for mesh in model:meshes(node) do -- assume some way to get meshes for node
pass:draw(mesh, model:getNodeTransform(node))
end
end
This makes it a lot easier to walk the node graph.
Improved Mesh API
- Suggestion: Convert Model vertices immediately when importing, in ModelData, rather than in Model.
- LÖVR will have a standard vertex format for model vertices.
- ModelData accessors can be simplified, and return data in a known format.
- There could be a function like
ModelData:encode
that returns a Blob with LÖVR-optimized binary model data, which would make models very quick to load.
- Keep
Model:getMesh
. Maybe it could use something other than draw range to refer to a subsection of the Model's vertex buffer, not sure yet. - Make
Model(Data):getTriangles
more flexible:- You should be able to pass in a mesh index and get local vertices for just that mesh.
- There is a notion of "local vertices" vs. "full vertices" (name TBD):
- local vertices are the raw contents of the vertex buffer. The vertices are not duplicated for every node that references them, and they are not transformed by their node transform(s). This is basically the contents of the Model's vertex buffer, or what you'd want if you're creating a MeshCollider from a specific node in the model.
- "full" vertices are the full set of vertices in the model: they are duplicated and transformed by each node.
- Replace ModelData's
:getMeshVertex
and:getMeshIndex
with:getVertices
and:getIndices
(or:getVertex
/:getIndex
) methods.- These should have the same flexibility mentioned above for
:getTriangles
, so you can get the vertices for the entire model, or just a single mesh. - Additionally, they'll return data in a single, known format.
- These should have the same flexibility mentioned above for
- Replace ModelData's
:getMeshDraWMode
and:getMeshMaterial
withModelData:getMesh
- Consider
mode, material, start, count, base = ModelData:getMesh(i)
- Consider
- Add
for i, mesh in Model:meshes(node)
iterator that lets you quickly iterate over Mesh objects that belong to a node.- Unclear if this one can be stateless.
All of this should make it easier to grab vertices/indices/triangles out of a model.
First Class Vertex Animation
Currently Model animation is "magical". LÖVR doesn't provide built in tools to do skeletal animation or blend shapes, it's baked into the Model
object. It would be nice if this functionality was exposed on Mesh objects as well, so people can easily do animation even if they aren't using Model.
Mesh
can have skinning info (VertexJoints
andVertexWeights
attributes).- Add a
Skeleton
object. Contains a tree of joints, each with an inverse bind matrix and local/global transforms. - A
Skeleton
can be attached to aMesh
, either withMesh:setSkeleton
orPass:draw(Mesh, Skeleton, ...transform)
. Mesh vertices will be skinned to the joints in the skeleton. Model
creates aSkeleton
for every skin in the glTF.- When you
:animate
a model or move its nodes manually, the Model will also update any affected skeletons. - Drawing an animated model just draws its meshes with whatever skeletons are assigned to those nodes. No magic.
- Skeletons are included when iterating meshes manually:
for node, mesh, skeleton in model:meshes() do
pass:draw(mesh, skeleton, model:getNodeTransform(node))
end
Similarly, Meshes can have blend shapes and blend weights. Adding blend shapes to the mesh and setting its blend weights will morph the vertices with a compute shader before rendering.