Jump to content

Writing a deformer, need advice


Recommended Posts

I have a deformer that weights points in the first input to polys on the second. Based on distance (and possibly connectivity... but don't have that working yet) I weight each point to multiple polys on the driver surface.. the number simply depends on proximity.

I actually have this broken into 2 sops.. the first builds all the weights and stores which poly your bound to and some extra info.

This all works in my test case when i bind each point to a single poly and I'm about to add the multi-poly support but I'm trying to decide how to represent the weights as attributes. I imagine this could get quite heavy since each point could be weighted to many points on the driver surface creating an array of weights that's potentially as long as as many verts on the driver surface.

Anyone push attribute size that far before? is it a bad idea?

The other option is to roll this into 1 sop and store my own data on the node apart from attributes and have a "bind" button or a 3rd input for a rest (like the lattice sop does). The thing i want to avoid is re-binding each frame since that's the expensive part.

I was looking at the deform sop.. those pcapt attributes are a bit cryptic .. there's a detail array called pCaptData that's about [20] long for each region.. so I'm guessing that can get pretty huge as well (though I almost never use capture regions so I don't have much experiance w/them)

anyway.. any advice would be appreciated.

thx

daniel

Link to comment
Share on other sites

At the heart of it, you basically don't want to store a full array per point for each possible primitive to capture. It doesn't make sense anyhow as its going to be a sparse array with lots of zeros. What the Capture/Deform and WireCapture/WireDeform SOPs do is to store them in pairs, but only for the non-zero weights. eg.

(bone1_id, bone1_weight) (bone2_id, bone2_weight) ...

Let's say all your points are weighted by at most N primitives. Then the size of your attribute only needs to be N*2*sizeof(float) bytes. For points that are weighted by less than N primitives, the unused entries just get (-1, -1). Note though that the unused entries *must* be the at the end of the attribute. You're in luck as we added a lot more HDK support for them in H7.0 with the new WireCapture/WireDeform SOPs which automatically include proper support from the AttribTransfer SOP.

If you look in the GEO_Detail class, we have the following functions:

addPtIndexPairAttribute( attrib_name, size_in_bytes_of_attrib );

findPtIndexPairAttribute( attrib_name );

destroyPtIndexPairAttribute( attrib_name );

When you get at the pt attrib data, it's just an array of floats where you interpret the even entries as the indices (into some array of primitives which in your case can just be the primitive number itself), and the odd entries their corresponding data (which in your case are the weights).

The actual case with the Capture/Deform and WireCapture/WireDeform SOPs is slightly more complicated. The indices that they use actually point into the index attributes found in the detail attribute as it stores extra information for each cregion and wire primitive. So that's what the pCaptData attribute is, it stores extra information on each of the rest cregions. The actual indices in the point attribute point into the pCaptPath index attribute.

Link to comment
Share on other sites

Ah, thanks for the excelent insider info on the attributes. I already started to impliment a single sop where the bind data is stored inside the node (not as attrs) and added a 3rd input for a rest position. I'll put a button o something to trigger a "bind".

I will also take a look at the attr pairs you pointed out and see if I can't just leverage off the internal deform sop instead by providing the right attrs. I'm not yet sure if my scheme will be as simple as a single weight per point, I think I can boil it down to that though.

Thanks again!

d

Link to comment
Share on other sites

If you look in the GEO_Detail class, we have the following functions:

addPtIndexPairAttribute( attrib_name, size_in_bytes_of_attrib );

findPtIndexPairAttribute( attrib_name );

destroyPtIndexPairAttribute( attrib_name );

When you get at the pt attrib data, it's just an array of floats where you interpret the even entries as the indices (into some array of primitives which in your case can just be the primitive number itself), and the odd entries their corresponding data (which in your case are the weights).

16779[/snapback]

Ok, after looking at the docs I'm a bit clearer.. Although I am weighting to prims I can figure out a weight on each vert of each prim. At first I thought I might be able to use the deform sop, but looks like this is only for capture regions. Pointing me to the addPtIndexPairAttribute is great though.. I've got that making a nice array on my object... but, I can't figure out how to load it up. Here's what I tried:

attrib = gdp->pointAttribs().find("weight", GB_ATTRIB_FLOAT);

if (!attrib)

  gdp->addPtIndexPairAttribute( "weight", 30*sizeof(float)*2 );

aoff = gdp->findPtIndexPairAttribute( "weight" );

weight = (float*)ppt->getAttribData(aoff);

This does make the attr with values set to -1 across the board

Then I tried loading it up:

UT_FloatArray w;

(loop over points)

w.append(myindex);

w.append(myweight);

(end loop)

w.entries(30*2*numpoints);

weight = w.array();

Since my array may have been shorter then the total possible I pad it at the end by growing the size.. then return a float array to weight. My plugin doesn't crash, but it also doesn't set any data.

thnx for any help on that one, havn't delt with a large attr array like this before

d

Link to comment
Share on other sites

I think that the best bet is to leverage the existing WireDeform SOP as it can already handle polys. The trick is to turn off the Treat Polygons as Lines parameter in both of the SOPs. See the attached hip file.

16785[/snapback]

Thanks for the example, I didn't know that the wire sop had that functionality actually.. very good to know!

Your example works quite well. I tried a diff example which is closer to the sort of thing I want to do and I wasn't able to get it to deform nicely. I've attached that file. Since I'm not very familiar with the wiredeform I am not very good at setting the weights possibly.

Since there are 81 polys in the driver surface so I had to set weights with a for look .. here's what I used:

/obj/model -> for i = 0 to 80

> set name = `strcat(u,strcat($i,radius))`

> opparm wirecapture2 $name(.2 1)

> end

The deformer I'm writing does a very good job in this case.. works much like the "wrap" deformer in maya. Poly orientation on the driver surface plays a big role in how it works. The wiredeform doesn't seem to respect the poly orentation change very well (at least in the short time I messed with it). hopfully that's just how I'm dialing it.

Take a look and tell me what you think

thanks

d

poly_wiredeform_v2.zip

Link to comment
Share on other sites

This is a minor point but the first part of your code could be simplified to something like:

aoff = findPtIndexPairAttribute("weight");
if( aoff < 0 )
    aoff = addPtIndexPairAttribute("weight");
...
weight = (float*)ppt->getAttribData(aoff);

Anyhow, the big problem with the second code snippet is with:

weight = w.array();

weight is just a pointer to the memory that you're supposed to set. Simply assigning the pointer won't do anything. Try something more like:

memcpy( weight, w.array(), w.entries()*2*sizeof(float) );

Alternatively, you could just assign into weight directly without using w.

I haven't looked at your hip file but Simon Barrick has a whole other thread on the deficiencies of the wiredeform with respect to orientation here. :) That's already been addressed for curves for the next major release of Houdini. However, I'd still need to check to see if properly uses the normals for surfaces.

Link to comment
Share on other sites

Anyhow, the big problem with the second code snippet is with:

weight = w.array();

weight is just a pointer to the memory that you're supposed to set. Simply assigning the pointer won't do anything. Try something more like:

memcpy( weight, w.array(), w.entries()*2*sizeof(float) );

Alternatively, you could just assign into weight directly without using w.

Right.. I tried that actually and I'm not having luck. I'm pretty much an amature programmer.. so it takes me a bit longer catch on.

I'm not having any luck setting the values of that float array. I tried defining weight as:

float *weight;
weight = (float *)malloc( maxp * sizeof(float) * 6 ); 
aoff = gdp->findPtIndexPairAttribute( "weight" );
if(aoff < 0)
  gdp->addPtIndexPairAttribute( "weight", maxp*sizeof(float)*6 );
weight= (float*)ppt->getAttribData(aoff);

When I actually set the values I do:

for(;entryNum<maxp*6;entryNum++)
{
   weight [(entryNum*5)+0] =  pindex;
   weight [(entryNum*5)+1] =  myDist;
   weight [(entryNum*5)+2] =  thedata[0];
   weight [(entryNum*5)+3] =  thedata[1];
   weight [(entryNum*5)+4] =  thedata[2];
   weight [(entryNum*5)+5] =  thedata[3];
}

I get garbage in my attributes. At this point I'm just stuck on figuring out the right way to format this.. just sort of my lack of C knowlege.

When I get into work tomorrow I'll get someone to point me in the right direction

thnx

d

Link to comment
Share on other sites

Ah.. I found a couple of bugs.. the big one was:

This should have read:

float *weight;
weight = (float *)malloc( maxp * sizeof(float) * 6 ); 
aoff = gdp->findPtIndexPairAttribute( "weight" );
if(aoff < 0)
  aoff = gdp->addPtIndexPairAttribute( "weight", maxp*sizeof(float)*6 );
weight= (float*)ppt->getAttribData(aoff);

The only diff is there's the "aoff =" when I add the attr.. I wasn't setting that.. duh.

Then the other was I should have been multing my entryNum by 6 not 5... once I got the values stuffed into the attr I realized that one too. Ok.. think I'm back on track. Thanks for the pointers!

d

Link to comment
Share on other sites

Oh wait, you're trying to stuff more than one piece of data into an index pair attribute. You can't do that as an index pair attribute must strictly contain one index and one piece of data. That's why its called "pair". :) It'll work for now as you've chosen an even sized entry. However, if you try to do an AttribTransfer on it, then it won't work because it expects all the even entries to be indices.

To associate more than one piece for data per index, you must use multiple attributes. That's why there's 3 wireCapt point attributes. Because for each index, we have the weight, closest U value, and closest V value.

Link to comment
Share on other sites

Oh wait, you're trying to stuff more than one piece of data into an index pair attribute. You can't do that as an index pair attribute must strictly contain one index and one piece of data. That's why its called "pair". :) It'll work for now as you've chosen an even sized entry. However, if you try to do an AttribTransfer on it, then it won't work because it expects all the even entries to be indices.

To associate more than one piece for data per index, you must use multiple attributes. That's why there's 3 wireCapt point attributes. Because for each index, we have the weight, closest U value, and closest V value.

16805[/snapback]

Right, I realized that. I was testing with just using this attribute to build the large array, I have a secondary sop that knows how to read the data. I may re-orgainize this later once I settle on a deformation scheme.. still playing with the algorithm a bit

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...