Issue with SkinController and _sceneTransformDirty (FP10)

Software: Away3D 3.x

Mr Andersson, Newbie
Posted: 29 February 2012 11:53 AM   Total Posts: 16

I have an issue with a collada model with bones that isn’t skinned properly. Some vertices aren’t updated. I’m working with an old code base that used to work, but not with newer revisions.

I’ve spent hours upon hours upon hours trying to track down the problem. It came with the changes made in revision 2407 (http://code.google.com/p/away3d/source/detail?r=2407) “fix to animating collada bones manually”. My code worked in 2394, but is otherwise broken inbetween 2395-2406.

In SkinController::update(), there is this condition:

if (!joint || _sceneTransformDirty)
 return; 

Removing the _sceneTransformDirty will make it apparently work, because outherwise some bones are just updated the first time but never more, because for them this variable is true.

But I can’t understand the logic in that line. The function otherwise will update child controllers and set the position dirty of the vertices, but won’t unless the sceneTransformDirty is false. SceneTransformDirty is only set to false if the controller’s sceneTransform property is read, which seems to me to be a bit like catch-22.

It’s probably good to have some kind of “guard” there, because otherwise it will be updated unnecessarily, but should that maybe not be a seperate thing from _sceneTransformDirty?

 

   

Mr Andersson, Newbie
Posted: 29 February 2012 12:54 PM   Total Posts: 16   [ # 1 ]

Another possible issue I found, is that each SkinController seem to have their own copy of skinVertices, which contain the entire set of vertices, and which it, in update(), loops over and set _positionDirty on.

I see in the Collada loader that all SkinControllers gets initialized with all vertices.

In my case I have a total of 180 vertices and 32 controllers, so that’s ~5500 unnecessary iterations and settings each update…

Before the 2407 revision, the BonesAnimator had the list of skinVertices and did always set _postionDirty on them when updating.

   

Mr Andersson, Newbie
Posted: 29 February 2012 01:47 PM   Total Posts: 16   [ # 2 ]

Ok, think I understand the problem now.

The idea behind the _sceneTransformDirty guard I guess is that the update() step is separated from the rendering (and vertex update), and thus it counts on the vertices to “reset” the controller once they are rendered.

However, my model have “intermediate” bones/SkinControllers, that aren’t used by any vertices, thus their sceneTransform is never read, but they do have children. The children will thus never have their update() function called, and their sceneTransform will remain invalid.

   

Mr Andersson, Newbie
Posted: 29 February 2012 02:21 PM   Total Posts: 16   [ # 3 ]

I think I may have solved the problem for my part, with these modifications:

(NOTE: I’m working with r2407, but the newest fp10 has the same problem and almost the same code)

SkinController, old code:

public function update():void
{
    
if (!joint || _sceneTransformDirty)
        return;
        
    
_sceneTransformDirty true;

    var 
child:Bone;
    for 
each (child in joint.children)
        if (
child && child.controller)
            
child.controller.update();
    
    var 
skinVertex:SkinVertex;
    for 
each (skinVertex in skinVertices)
        
skinVertex.skinnedVertex._positionDirty true;

New code:

public function update():void
{
    
if (!joint)
        return;
        
    var 
child:Bone;
    for 
each (child in joint.children)
        if (
child && child.controller)
            
child.controller.update();

    if(
_sceneTransformDirty)
        return;
    
    
_sceneTransformDirty true;

    var 
skinVertex:SkinVertex;
    for 
each (skinVertex in skinVertices)
        
skinVertex.skinnedVertex._positionDirty true;

Basically allowing it to propagate regardless of the dirty state.

I also tried to modify the Collada loader, so that the SkinController only have a list of *its own* vertices. I don’t understand why all should have the entire mesh, so I might have messed up something else:

Collada, old code in parseGeometryData():

...
    
skinVertex = new SkinVertex(geometryData.vertices[i]);
    
geometryData.vertices[i].skinVertex skinVertex;
    
geometryData.skinVertices.push(skinVertex);
       
    for 
each (skinController in geometryData.skinControllers)
        
skinController.skinVertices.push(skinVertex);

    
j=0;
    while (
c)
    
{
        skinVertex
.controllers.push(geometryData.skinControllers[int(v[count])]);
        
count++;
        
skinVertex.weights.push(Number(weights[int(v[count])]));
        
count++;
        ++
j;
    
}
    
... 

New code:

...
    
skinVertex = new SkinVertex(geometryData.vertices[i]);
    
geometryData.vertices[i].skinVertex skinVertex;
    
geometryData.skinVertices.push(skinVertex);

    
j=0;
    while (
c)
    
{
        skinVertex
.controllers.push(geometryData.skinControllers[int(v[count])]);
        
geometryData.skinControllers[int(v[count])].skinVertices.push(skinVertex);
        
count++;
        
skinVertex.weights.push(Number(weights[int(v[count])]));
        
count++;
        ++
j;
    
}
    
... 

That is, when adding the controller to the vertex, also add the vertex to the controller.

Would be glad if someone (Rob?) who actually understands this code could have a look, because I cannot see all ends of this, and mayhaps fix it or give me the thumbs up to do so.

   

Mr Andersson, Newbie
Posted: 01 March 2012 12:19 PM   Total Posts: 16   [ # 4 ]

I’m sorry I keep rambling like this, and I suppose the devs consider 3.6/fp10 long dead, and so on, but I hope maybe someone will find it useful. I should maybe file bug reports instead, and I might do so once I get some feedback/options on the matter.

I’m also sorry that the code i’m talking about is so old. There were so many issues at once when I tried to upgrade to the latest, and also extreme performance hits, so I decided I would upgrade one revision at a time, so I could understand (and possibly fix) the problems. For the most parts, I’ve checked the latest code and determined that the problems are still there.

——-

Ok, next step was trying to clone the damn thing, including getting it to animate separately. This proved hard work as well grin Remember, I compare this to 3.4-ish (<r2395), which I had working.

Cloning, using ObjectContainer3D::cloneAll(), now requires me to set the target myself:

for each(var animationData:AnimationData in clonedObject.animationLibrary{
    animationData
.animator.target clonedObject;

Another issue with Animator when cloning is that the animation will lose its length. I fixed this by adding this line to Animator::clone():

anim._totalFrames _totalFrames

But of course, the cloned object wasn’t moving… Main reason is the “new” way, where the animator doesn’t explicitly update the vertices, but just indirectly by making them dirty. The “new” Vertex, has a reference to its SkinVertex, but when cloning this was not set.

I had to make a few modifications to Geometry to fix this. I know it has been totally rewritten since, removing all dealings with skinvertices, so this is possibly only of historical interest. I did these changes in Geometry::clonseSkinVertex():

if (result == null{
    result 
= new SkinVertex(cloneVertex(skinVertex.skinnedVertex));
    
// ADDED: the vertex must have a back-reference to the skinvertex
    
result.skinnedVertex.skinVertex result;
    
result.weights skinVertex.weights.concat();
    var 
_skinVertex_controllers:Array = skinVertex.controllers;
    var 
_skinController:SkinController;
    for 
each (var skinController:SkinController in _skinVertex_controllers{
        _skinController 
cloneSkinController(skinController);
        
// ADDED: controller must know its vertices
        
_skinController.skinVertices.push(result);
        
result.controllers.push(_skinController);
    
}
    clonedskinvertices[skinVertex] 
result;

Also, the new bones didn’t have a SkinController attached to them, so I had to change ObjectContainer3D:cloneBones()

for each (skinController in skinControllers{
    
var bone:Bone root.getBoneByName(skinController.name);
    if (
bone{
        skinController
.joint bone.joint;
        
// ADDED: actually associate the bone with its controller
        
bone.controller skinController;
        ... 

I found this weird since the comment above it all says “wire up new bones to new skincontrollers”, but I couldn’t see that being done anywhere.

Alright, now it was roughly working. However, close to my original issue with animating the original; all “extremity vertices” (fingers, toes) were fixed.

The problem was again that some SkinControllers weren’t cloned; those that were intermediates and didn’t have any vertices of their own. Again apologizing, this is obsolete code now, but this was because in Geometry::clone(), only the controllers cloned as a result of cloning skin vertices were added. Thus I made this modification to Geometry, so that it added all SkinControllers, and cloned those that hadn’t already been cloned:

//for each (var skinController:SkinController in clonedskincontrollers)
//    geometry.skinControllers.push(skinController);
for each(var skinController:SkinController in skinControllers)
    
geometry.skinControllers.push(cloneSkinController(skinController)); 

That at least gave me all the skincontrollers in the cloned object, but it still wasn’t working. Many bones didin’t have the child bones (via the joint) that they should. I don’t completely understand this, but this is the Bone::cloneAll() function:

public override function cloneAll(object:Object3D null):Object3D
{
    
var bone:Bone = (object as Bone) || new Bone();
    
bone.removeChild(joint);
    
super.cloneAll(bone);
    
bone.boneId boneId;
    
bone.joint bone.children[0];
    return 
bone;

When creating a new Bone, it will have an empty joint, so super.cloneAll() will add the proper cloned joint at bone.children[1], and thus “bone.joint = bone.children[0];” will cause the bone’s joint to point to the empty default container.

I thought that maybe the removeChild() is supposed to fix that, but it doesn’t. It tries to remove the original bone’s joint from the new/cloned bone, which should never be there. Perhaps that is a safe gaurd to make sure just that never happens, but I changed that line to:

bone.removeChild(bone.joint); 

Which will remove the existing (default) joint, and thus the structure that super.cloneAll() will build up will be the first child, and be the actual joint.

Now it’s working!!!!

   
   

X

Away3D Forum

Member Login

Username

Password

Remember_me



X