KhronosGroup / glTF

glTF – Runtime 3D Asset Delivery
Other
7.18k stars 1.14k forks source link

node.meshes vs mesh.primitives #821

Closed lexaknyazev closed 7 years ago

lexaknyazev commented 7 years ago

While re-editing skinning part of specification, I've found that spec requires that node can contain only one skinned mesh, however number of mesh.primitives isn't limited.

So the question is, why exactly do we have two ways of specifying the same geometry layout?

node
|----meshes
     |------meshA
            |-----primitives
                  |----------primitiveA
                  |----------primitiveB

and

node
|----meshes
     |------meshA
            |-----primitives
                  |----------primitiveA
     |------meshB
            |-----primitives
                  |----------primitiveB

CC @pjcozzi @tparisi @javagl

lexaknyazev commented 7 years ago

Related: #744, CC @mre4ce

lexaknyazev commented 7 years ago

From skinning perspective, this means that all primitives of the same mesh share the same joints/bones scope (because skin is defined per-node). On the other hand, each primitive could have different set of attributes, material and even use different program. Was such usage intended?

javagl commented 7 years ago

It's hard to tell "THE" reason for certain decisions. Particularly for me, because I've not been involved in many of the early discussions. So to be taken with a grain of salt:

From looking at the git history, the mesh.primitives have been there since the beginning of time (~2012). I can't imagine that they have never been questioned before. But during my search, I did not find a clear reason of why they exist at all. They might stem from a time where the mesh basically contained attribute objects, and each primitive combined some of these attributes. But this is just a guess based on the examples in https://github.com/KhronosGroup/glTF/issues/71 . (Obviously, the attribute objects have been moved up and became accessor objects)

There are some issues and comments related to the interplay of mesh/ mesh.primitives, and skin. These mainly seem to be about how to cope with the given structures, and less about why they exist (e.g. https://github.com/KhronosGroup/glTF/issues/442). However, one of these issues mentions a few points in https://github.com/KhronosGroup/glTF/issues/100#issuecomment-28130224 that might be relevant here - mainly, the case where a single mesh had to be split into multiple parts due to the 64k indices limit.

So I can't give a profound answer here. I think that at least the 64k-splitting-case may be a justification to have the primitives. Beyond that, I don't see why they should strictly be necessary, but it's easy to overlook some border case or application pattern that may be a justification for the current state.

lexaknyazev commented 7 years ago

the case where a single mesh had to be split into multiple parts due to the 64k indices limit

This sounds reasonable, but I'm sure it doesn't apply anymore. Right now all desktop and almost all mobile WebGL 1.0 implementations support 32-bit indices. I think that mesh.primitives originates from the lack of universal 32-bit indices support at the time when collada2gltf was written.

What bothers me most is that such layout inevitably complicates state management, in worst case: different attributes layout of primitives of the same mesh - skinning/morphing issues, different drawing modes from different index buffers, different materials - possible program switching, etc.

Of course, runtime could workaround most of that stuff (reorder objects, build caches, etc) but, maybe, we could sacrifice 5% of use cases in favor of simplifying other 95%?

lexaknyazev commented 7 years ago

If the only purpose of having several mesh.primitives is to allow mesh "splitting" (to workaround indices limit), wouldn't it be better to move material out of array and require that all primitives must have the same set of attributes (so skinning/morphing would be more predictable)? Otherwise, I can't see any reason why same mesh parts couldn't be represented as separate "meshes".

javagl commented 7 years ago

Maybe an @fabrobinet may be appropriate, in view of https://github.com/KhronosGroup/glTF/issues/161 , where the concept of accessor objects was introduced. Again, that was before I started tracking glTF more actively, so just the question: Could it be that the main justifications for having the mesh.primitives became obsolete when the attribute objects have been replaced with accessor objects, but this potential simplification remained unnoticed?

Otherwise, the role of the mesh now only is that it is used for "grouping" several mesh.primitives. Considering the questions about skinning that arise from that, a simplification or additional constraints may be reasonable, but I'm still not sure whether I'm overlooking something.

pjcozzi commented 7 years ago

mesh.primitives is there to allow converters and exporters to preserve their semantic hierarchy of objects in the scene. I definitely questioned it years ago myself and I'm pretty sure it is required if, for example, you want to have a clean conversions from COLLADA.

xelatihy commented 7 years ago

My 2cents.

  1. Mesh primitives may help with the semantic of some scenes, but combined with the use of accessors require quite a bit of indirections to render
  2. Most graphics file format have a concept of per-vertex or per-face attributes (OBJ, Blender, Renderman, all subdivision surface stuff) . Mesh primitives add complexity but disallow any kind of per-face attribute.
  3. In my opinion, and this is just an opinion, all application will likely convert a glTf model to their own data layout anyway, so the added complexity is unclear.
  4. I also considered using Mesh to enable instancing for raytracing/GL. But in their current form, the instancing would neither by flexible nor optimal.

In light of these observation, I would suggest the following things.

a. Either remove mesh entirely or maybe add a "groupname" property for nodes. b. If there is interest, create an extension that preserve better topolgy information and per-face attributes to help data exchange for editing application. This would allow lossless conversion between glTF and OBJ, which in turn might help a lot with adoption. Not the conversion OBJ->glTF change entirely mesh topolgy. One way to do this is to look at the RIB API and OpenSubdiv. I plan to support one for our research efforts in yocto_gltf.

This is just me, but since glTF 2 is backward incompatible, trying to simplify some of its indirections might be helpful for adoption.

pjcozzi commented 7 years ago

Looking into this more with @lasalvavida, it is likely that node.meshes could go from an array to just one property: node.mesh, or maybe even make it an array of Primitives (and rename Primitive to Mesh, and remove the current Mesh). Let me confirm with a few more users.

@xelatihy the extension could have potential once someone has the bandwidth to investigate.

xelatihy commented 7 years ago

Sounds good. I think if Mesh is kept, than an additional extension cab certainly by done to add in topology preseving meshes and subdivs.

lexaknyazev commented 7 years ago

From offline WG discussions: