Opened 12 years ago

Closed 12 years ago

Last modified 12 years ago

#1012 closed defect (fixed)

[PATCH] COLLADA: Support special vertex weight index on skinned models

Reported by: historic_bruno Owned by: historic_bruno
Priority: Should Have Milestone: Alpha 9
Component: Core engine Keywords: blender, collada, skinning
Cc: Patch:

Description (last modified by historic_bruno)

For skinned models, the COLLADA 1.4.1 spec allows a special vertex weight index of -1, which indicates the weight applies to the bind-shape matrix* instead of a particular bone, as seen in this example:

<skin>
  <source id="joints"/>
  <source id="weights"/>
  <vertex_weights count="4">
    <input semantic="JOINT" source="#joints"/>
    <input semantic="WEIGHT" source="#weights"/>
    <vcount>3 2 2 3</vcount>
    <v>
      -1 0 0 1 1 2
      -1 3 1 4
      -1 3 2 4
      -1 0 3 1 2 2
    </v>
  </vertex_weights>
</skin>

(*The bind-shape matrix represents the transform of the bind-shape prior to skinning, it transforms the bind-shape from object space to bind-space. It's specified by a <bind_shape_matrix> element in the <skin> or assumed to be an identity transform. See pages 4.6-4.8 of the COLLADA spec for more notes.)

Currently if you try to add such a model to the game, it will fail to import since the PMD converter thinks there are more than 256 bones (the -1 index is typecast to an unsigned int). Apparently Blender 2.60a likes to export such models and seems to have made general improvements to animation exporting, so we should find a way to implement this.

As a workaround, it may be possible to export animations with Blender 2.6 and the corresponding models with an older version.

Attachments (4)

ram_blender26.dae (334.9 KB ) - added by historic_bruno 12 years ago.
6-wheeled ram exported from Blender 2.60a
ram_blender25.dae (335.5 KB ) - added by historic_bruno 12 years ago.
6-wheeled ram exported from Blender 2.59
collada_import_fixes-12092011.patch (8.2 KB ) - added by historic_bruno 12 years ago.
collada_alternate_fix-01222012.patch (12.5 KB ) - added by historic_bruno 12 years ago.
alternative solution - extra bone added by the DAE converters

Download all attachments as: .zip

Change History (19)

by historic_bruno, 12 years ago

Attachment: ram_blender26.dae added

6-wheeled ram exported from Blender 2.60a

comment:1 by historic_bruno, 12 years ago

Description: modified (diff)

by historic_bruno, 12 years ago

Attachment: ram_blender25.dae added

6-wheeled ram exported from Blender 2.59

comment:2 by historic_bruno, 12 years ago

Owner: set to historic_bruno
Status: newassigned

comment:3 by historic_bruno, 12 years ago

Description: modified (diff)

updated description with more info

comment:4 by historic_bruno, 12 years ago

Here's how my current solution looks:

  • In the special case of this ticket, a vertex can be influenced by the bind-shape matrix instead of a bone, what this means in the common case of only one influence per vertex is the vertex is not affected by the pose (i.e. it's not animated). To indicate this when converting the model, I set the influence's bone ID equal to the total number of bones, because this value is constant and serves as a useful index. CModel::ValidatePosition is called on the resulting model prior to skinning, which updates prop points, the array of posed bone matrices (CModel::m_BoneMatrices), etc. These bone matrices include the various transforms necessary for skinning in world space.
  • I reserve one "pseudo" bone matrix in the array, which contains only the model's world space transform at the time of skinning. In this way, the skinning functions don't change to handle the special case, they treat the influence ID as if it's referring to a bone. I allocate one extra bone matrix on model loading and offset the indices by one when loading the PMD in CModelDef::Load and when computing the weighted blend matrices in CModelDef::BlendBoneMatrices.
  • The resulting array of bone matrices is indexed like this:
    0, 1, ... numBones-1, *numBones* , numBones+1, ... numBones+numBlends+1
    
    where the first numBones entries are real bones, followed by the pseudo bone (model world transform), and finally numBlends distinct weighted blends.
  • There's one other "special" case, which you notice in the attached files. Blender sometimes exports skinned models with influences like this:
    <vertex_weights count="2148">
      <input semantic="JOINT" source=.../>
      <input semantic="WEIGHT" source=.../>
      <vcount>0 0 0 0 ... 1 1 1 ... 0 0 0 ... </vcount>
      ...
    
    where some vertices have no influences at all (an entry of 0 in <vcount>)! I've found no details in the COLLADA spec about this case. Because these influences end up as default values during the PMD conversion, we catch that if it occurs on the first vertex and throw an error (without skinning the model). Maybe we should treat these models as errors if that's undefined behavior, but I've found that treating the no-influence vertices the same as the special case works: giving them a single bind-shape influence with weight 1.0.

in reply to:  4 comment:5 by historic_bruno, 12 years ago

Replying to historic_bruno:

but I've found that treating the no-influence vertices the same as the special case works: giving them a single bind-shape influence with weight 1.0.

I should clarify that this is handled during the model import and is transparent to the loading and skinning processes.

comment:6 by historic_bruno, 12 years ago

Keywords: review added
Milestone: BacklogAlpha 8
Summary: COLLADA: Support special vertex weight index on skinned models[PATCH] COLLADA: Support special vertex weight index on skinned models

Added patch which does just that

comment:7 by historic_bruno, 12 years ago

I haven't bumped the PMD converter version because this shouldn't affect existing models (they would have failed to import in this case).

by historic_bruno, 12 years ago

comment:8 by historic_bruno, 12 years ago

Milestone: Alpha 8Alpha 9

comment:9 by Philip Taylor, 12 years ago

Since the normal bones are computed in CModel::ValidatePosition as the animated matrix (which is rotation * translation) multiplied by world-space transform, isn't the new special-case world-space-transform-only bone equivalent to a standard bone with 0 rotation and 0 translation? If so, it might be cleaner to have the Collada converter explicitly create a bone with 0 rotation/translation (in both the PSA and PMD files, I think) so that we don't need the special case in the file format or in the engine code.

If not:

"numBones + numBlends + 1" and "mdef->m_NumBones + j + 1" could perhaps put the +1 in the middle instead of the end, to mirror the array layout better.

PMD_File_Format should be updated with the file format changes.

in reply to:  9 comment:10 by historic_bruno, 12 years ago

Replying to Philip:

Since the normal bones are computed in CModel::ValidatePosition as the animated matrix (which is rotation * translation) multiplied by world-space transform, isn't the new special-case world-space-transform-only bone equivalent to a standard bone with 0 rotation and 0 translation? If so, it might be cleaner to have the Collada converter explicitly create a bone with 0 rotation/translation (in both the PSA and PMD files, I think) so that we don't need the special case in the file format or in the engine code.

That solution works with my test models, I think it still requires special casing in the engine, because we have animations and models that are in PSA/PMD format and so can't be converted now (for instance a new COLLADA model might reference an old PSA animation and vice versa - the bones counts have to match). I'd have to bump the PSA/PMD version numbers and check for older versions, manually add the extra bone, in addition to bumping the converter version. Unless I'm missing something or overcomplicating things.

comment:11 by historic_bruno, 12 years ago

After discussing this with Philip, I realized the advantage of handling this in the PSA/PMD converters: it keeps the formats easy to describe and handle, as all the bone data gets encoded directly instead of requiring special run-time handling by the engine. The disadvantage is that it breaks backwards compatibility with old PSA/PMD files, because we have to allow for someone creating a COLLADA model that reuses old animations. This means adding more code to CModelDef::Load and CSkeletonAnimDef::Load to ensure we always have the correct number of bones, but the silver lining is that if we ever do replace those old files, we can remove the version compatibility code.

comment:12 by historic_bruno, 12 years ago

Keywords: review removed

Postponing this a bit, until I know if we can get rid of all the remaining PSA/PMDs.

comment:13 by Kieran P, 12 years ago

Milestone: Alpha 9Alpha 10

comment:14 by ben, 12 years ago

Resolution: fixed
Status: assignedclosed

In 11242:

Implements COLLADA -1 vertex weight support, using simpler initial solution. Fixes #1012.

comment:15 by historic_bruno, 12 years ago

Milestone: Alpha 10Alpha 9

Went ahead and fixed this with the initial solution, since old PMD/PSAs are not likely to be replaced soon (e.g. there are issues exporting from the source .max files and the animation import process is still not very intuitive).

I will also attach a patch for the alternative solution, but it complicated the PMD/PSA loading process unnecessarily IMO. All old models would have been required to have an extra bone added explicitly to maintain compatibility, but this way the change is transparent and only used for models with -1 vertex weights. If we do replace all the old PMD/PSAs, then the alternative solution would be nice for reasons mentioned above, but still not a significant improvement.

by historic_bruno, 12 years ago

alternative solution - extra bone added by the DAE converters

Note: See TracTickets for help on using tickets.