PDA

View Full Version : Blend shape deformer pitfall (requires "nice" weight)


zoharl
11-09-2012, 10:30 PM
Hi,

I'm using the following code to blend five shapes as in between.


MFnBlendShapeDeformer blend;
...
int nShapes = 5;
for ( int ib = 0 ; ib < nShapes < ib++ ) {
...
double step = 1.0/(nShapes-1);
//step = int(step*1000)/1000.0; // round the step, else the blend shape fails with internal error
double w = ib*step;
path.extendToShape();
cout << path.fullPathName().asChar() << endl;
cout << "we: " << w << endl;
MStatus stat = blend.addTarget(bpath.node(), 0, path.node(), w);
if ( stat.statusCode() != MStatus::kSuccess )
cout << "blend.addTarget failed!" << endl;
}


The output:


|morph|polySurface3|polySurfaceShape3
we: 0
|morph|polySurface4|polySurfaceShape4
we: 0.166667
blend.addTarget failed!
|morph|polySurface5|polySurfaceShape5
we: 0.333333
blend.addTarget failed!
|morph|polySurface6|polySurfaceShape6
we: 0.5
|morph|polySurface7|polySurfaceShape7
we: 0.666667
blend.addTarget failed!
|morph|polySurface8|polySurfaceShape8
we: 0.833333
blend.addTarget failed!
|morph|polySurface9|polySurfaceShape9
we: 1

Which means that only shapes 3, 6, and 9 were added to the blend deformer, and the rest failed (with internal error).

Now, if I uncomment the following line:

step = int(step*1000)/1000.0; // round the step, else the blend shape fails with internal error

that rounds the weights, all the shapes are added:

|morph|polySurface3|polySurfaceShape3
we: 0
|morph|polySurface4|polySurfaceShape4
we: 0.166
|morph|polySurface5|polySurfaceShape5
we: 0.332
|morph|polySurface6|polySurfaceShape6
we: 0.498
|morph|polySurface7|polySurfaceShape7
we: 0.664
|morph|polySurface8|polySurfaceShape8
we: 0.83
|morph|polySurface9|polySurfaceShape9
we: 0.996


So WTF, does MFnBlendShapeDeformer:addTarget require "nice" numbers with only 3 digits after the floating point?
Am I missing something or is this another pearl by one of AD prodigies?

djx
11-10-2012, 04:20 AM
Interesting find. I would not have thought of trying to reduce the precision like that.
Since your code only tests for kSuccess, I was just wondering if you checked what the actual return code was? (ie InvalidParameter, InsufficientMemory or just Failure)

David

zoharl
11-10-2012, 09:02 AM
Funny... :sad: No, as I said, I get the most common error that I get 90% of the time from the api: Internal failure.

I think this should be tested in python openmaya as well.

djx
11-10-2012, 09:25 AM
What I meant was the MFnBlendShapeDeformer should return 1 of 4 codes, kInvalidParameter, kInsufficientMemory, kFailure or kSuccess. I was just wondering which of the 4 it was in this case (obviously it is not kSuccess).

David

zoharl
11-10-2012, 09:32 AM
blend.addTarget failed! Status code:1 Error message: (kFailure): Unexpected Internal Failure

zoharl
11-10-2012, 10:17 AM
I reproduced the error in python:

import pymel.core as mc # sorry about mc
import maya.OpenMaya as om
import maya.OpenMayaAnim as oma

s1 = mc.polySphere()
#os1 = s1[0].__apimobject__()
os1 = s1[0].__apimdagpath__()
node = om.MFnDagNode(os1)
path = om.MDagPath()
node.getPath(path)
path.extendToShape()
os1 = path.node()

s2 = mc.polySphere()
#os2 = s2[0].__apimobject__()
os2 = s2[0].__apimdagpath__()
node = om.MFnDagNode(os2)
path = om.MDagPath()
node.getPath(path)
path.extendToShape()
os2 = path.node()

fnBlend = oma.MFnBlendShapeDeformer()
origin = oma.MFnBlendShapeDeformer.kLocalOrigin
#oblend = fnBlend.create(os1, origin)
oblend = fnBlend.create(os1)
fnBlend.setObject(oblend)
w = 1/3.0
fnBlend.addTarget(os1, 0, os2, w)

Change the w=1/3 to w=1, and it does work!

BTW I'm using maya 2013 x64.

zoharl
11-10-2012, 10:46 PM
What I meant at the end was compare w=0.3333 to w=0.333 .
Can you verify the bug?

djx
11-11-2012, 01:05 PM
Yes. Same result here. Very strange indeed!

David

uiron
11-11-2012, 09:08 PM
Could have something to do with the fact that blend shape inbetweens are using integer indexes from 5000 to 6000 for blend weights 0 - 1.0 ? Some failsafe in API prevents you from adding ambiguous weights, so your weight value has to be of resolution 0.001. It's just a guess, but if you try to add weight at -1.0, 0 and 1.0, you'll see them being connected with indexes 4000, 5000 and 6000 respectively at inputTargetItem for a blendshape.

zoharl
11-11-2012, 09:32 PM
But when you do in between, all the meshes use the same index. In my case I used index 0. Although if there's a fail safe mechanism for the general case, I can understand where this bug is coming from (since right now I can't even imagine how someone would produce such a bug).

djx
11-11-2012, 10:22 PM
I think you are correct Viktoras.
In zoharl's example using w=0.333 we get this connection...

blendShape1.inputTarget[0].inputTargetGroup[0].inputTargetItem[5333].inputGeomTarget

I find it hard to imagine why someone would code this weight as a float arg, but not round it to the closest integer index, or at least document the limitation. The secrets out now though!

David

zoharl
11-12-2012, 02:13 AM
Okay, now I see the connection, and I understand what @uiron was referring to by buckets between 5000-6000. But I still don't understand:

1. If it's a fail-safe, what happens if I enter w=-9?

2. Can you show me an algorithm that takes my weight (double), convert it to int, and fails because there are more than 3 digits after the floating point (i.e. 0.333 vs. 0.3333)? Don't forget to consider the previous point, e.g. w=3333 is still acceptable.

I'm telling you, someone worked quite hard to produce this bug, since I can't even imagine the code that would produce this...:rolleyes:

zoharl
11-12-2012, 02:34 AM
Hmm, I think I can imagine something:

1. Multiply the weight by 1000.
2. Embed it in a mel command string using sprintf, where it index some array, and execute it:

char cmd[1000];
sprintf(cmd, "inputTargetItem[5000+%f] = 1;", w);
MGlobal::executeCommand(cmd);



Indexing an array with a float should cause a syntax error (at least in C++), while any int should be fine (besides indexing unallocated memory in run-time).

uiron
11-12-2012, 07:05 AM
I think it's just a simple check added to make sure that you would not try to add targets at weights 0.4999, 0.5001 and 0.5004 by accident, as because of rounding they would overwrite each other.

zoharl
11-12-2012, 08:36 AM
I wonder, all unsuccessful simple checks in the api result in an unexplained internal failure?

djx
11-12-2012, 11:23 AM
In my opinion, you fixed the bug in a single line... w= int(w*1000)/1000.0
So thanks for that. I'm happy to move on.

David

CGTalk Moderation
11-12-2012, 11:23 AM
This thread has been automatically closed as it remained inactive for 12 months. If you wish to continue the discussion, please create a new thread in the appropriate forum.