HDK create new AgentShapeDeformer

   813   7   1
User Avatar
Member
82 posts
Joined:
オフライン
Hi, I'm looking for some pretty specific info regarding how to make and use a new AgentShapeDeformer
for houdini crowds with the HDK.
I'm trying to add a new deformer style alongside the "linearskinning" etc... that can be registered to a crowd agent.

According to the docs and header files, I think I need to subclass from AgentShapeDeformer, and create my own "name" and "deform" method? In this case, does "deform" still work similarly/replacing (cookMysop) at the sops level? or is it different? I see that you get access to a detail based on what "shape" is passed to it, which I assume is a standard detail gdp I can iterate thru the points of?

Also hoping that I can access the "rig" skeleton to get transforms/names etc.. from the GU_Agent that is passed?

I'm a bit fuzzy on what the "xform_idx" refers to, any better explanation for that? What data array is that an index into?

There is only explanation in the docs.... "Deformers are registered by calling GU_AgentLayer::registerDeformer() from the GUregisterAgentShapeDeformer() entry point." but I'm not entirely sure what that entails? Are there other examples of "registering" nodes I could reference?

I haven't been able to find any other documentation or examples of this yet, so asking here for a bit more clarification and guidance from the Gurus here on the forum, and SideFX directly.

thank you in advance!
User Avatar
スタッフ
792 posts
Joined: 10月 2012
オフライン
redpaw
In this case, does "deform" still work similarly/replacing (cookMysop) at the sops level? or is it different? I see that you get access to a detail based on what "shape" is passed to it, which I assume is a standard detail gdp I can iterate thru the points of?

Yeah this would be essentially the same idea - the GU_Detail that's provided contains a copy of the shape's rest geometry, and your deform() method should modify it to apply your deformer
Unlike a SOP node, one thing to note is that there is only a single instance of your deformer and its deform() method might be called in parallel to deform multiple shapes. So you just need to be cautious about any caching anything in member variables of your deformer

redpaw
Also hoping that I can access the "rig" skeleton to get transforms/names etc.. from the GU_Agent that is passed?
I'm a bit fuzzy on what the "xform_idx" refers to, any better explanation for that? What data array is that an index into?
Yes, from the GU_Agent you can access the joint names etc via GU_Agent::getRig(), and methods like GU_Agent::computeWorldTransforms() will give you the agent's current pose

The `xform_idx` is from the shape binding [www.sidefx.com], and would be >= 0 if this shape was attached to a joint in the agent's skeleton. This would be an index into the agent rig's list of transforms
As the documentation notes, you may not need to do anything with this since the resulting geometry is always transformed for you afterwards by this joint's transform. But for certain deformers you might need this (e.g. the skinning deformer first needs to apply the inverse of this transform)

redpaw
There is only explanation in the docs.... "Deformers are registered by calling GU_AgentLayer::registerDeformer() from the GUregisterAgentShapeDeformer() entry point." but I'm not entirely sure what that entails? Are there other examples of "registering" nodes I could reference?

You would need to have a function named GUregisterAgentShapeDeformer() in your plugin, which will be called when it's loaded by Houdini - https://www.sidefx.com/docs/hdk/_h_d_k__intro__creating_plugins.html [www.sidefx.com] has some more information and an example for the newSopOperator entry point
The SOP_BouncyAgent HDK example also has an example for GUregisterAgentCustomDataItemType() which is essentially the same idea as GUregisterAgentShapeDeformer(), just with a different function name
User Avatar
Member
82 posts
Joined:
オフライン
Thanks cwhite for the quick response! This all makes sense so far.
I'm trying to build out a boilerplate first to make sure I can get it registered and showing up first.

Have a few more questions:

Since this is not quite the same as a standard sop node, does it still require the same constructors/destructor?
or is there a different type/way to do this?

OP_Node* MyAgentDeformer::myConstructor(OP_Network *net, const char *name, OP_Operator *op)
{
    return new MyAgentDeformer(net, name, op);
}

MyAgentDeformer::MyAgentDeformer(OP_Network *net, const char *name, OP_Operator *op)
    : SOP_Node(net, name, op)
{
}
MyDeformer::~MyDeformer() {};



Also do I still need to register it to the "OP_OperatorTable *table" or is that replaced with the GUregisterAgentShapeDeformer()?


Just trying to get a minimal non-functional but registered AgentDeformer to show up first in the list.

thanks again!
User Avatar
スタッフ
792 posts
Joined: 10月 2012
オフライン
No, that all is specific to OP nodes. It should be roughly something like:

class MyAgentDeformer : public GU_AgentShapeDeformer
{
    ...
};

void
GUregisterAgentShapeDeformer()
{
    GU_AgentLayer::registerDeformer(UTmakeIntrusive<MyAgentDeformer>());
}

// If you're also creating shape bindings from C++, use GU_AgentLayer::findDeformer(your_deformer_name) to access the deformer instance, or store the instance you passed to registerDeformer() somewhere
User Avatar
Member
82 posts
Joined:
オフライン
Thank you! I am so close with this.. but it doesn't seem to be registering to the list of deformers yet...
I'm trying to check this with the python command: (and looking in the pulldown menu on the agentLayers node)

import hou

deformers = hou.crowds.shapeDeformers()
print(deformers)

Here's what I have

<HEADER FILE>
class MyAgentDeformer : public GU_AgentShapeDeformer
{
public: 
    MyAgentDeformer(const UT_StringHolder &name);
    ~MyAgentDeformer();
    const UT_StringHolder name(); 

    void deform(GU_Detail &gdp, const GU_AgentShapeLib::Shape &src_shape, exint xform_idx, const GU_Agent &src_agent) const override;
    
private:
    
    UT_StringHolder myName;
  
};

<CPP>
MyAgentDeformer::MyAgentDeformer(const UT_StringHolder &name):GU_AgentShapeDeformer(name) 
{
    cout <<  "creating MyAgentDeformer" << endl;
}
MyAgentDeformer::~MyAgentDeformer() {}


const UT_StringHolder MyAgentDeformer::name() 
{
    cout << "setting name to MyAgentDeformer" << endl;
    MyAgentDeformer::myName = "MyAgentDeformer"; 
    return myName; 
}


void  MyAgentDeformer::deform(GU_Detail &gdp, const GU_AgentShapeLib::Shape &src_shape, exint xform_idx, const GU_Agent &src_agent) const  
{
  // do stuff here
    cout <<  "deform called" << endl;
}

void GUregisterAgentShapeDeformer()
{
    cout  <<  "calling register agent shape deformer" << endl; 
    UT_StringHolder name = "MyAgentDeformer";
    GU_AgentLayer::registerDeformer(UTmakeIntrusive<MyAgentDeformer>(name));
}
Edited by redpaw - 2025年7月8日 20:31:23
User Avatar
Member
82 posts
Joined:
オフライン
Gah.. I was missing this :

void GUregisterAgentShapeDeformer(void*)
{....
User Avatar
Member
82 posts
Joined:
オフライン
Ok! so fundamental question about the "packed agent" eval/draw on crowds..

What I'm ultimately after is being able to swap out the agentShapedeformer on particular shapes after the
Agent is set up.. Is this possible without unpacking the agent?

For example if I were to swap linearskinning to blendshape on a specific agent?
What node / workflow could I use to do this? Can I do this with an attribute?


I have a test agent, being scattered a few times using crowd source. everything looks good in linearskinning default.


Right now the only thing that has worked, is using an agentLayer node after, and changing the "shapeDeformer" in the
"use existing shapes/shapebinding" multiparm to set the shape deformer to "blendshape".
This seems to work as the agent returns to the t-pose origin in the viewport.

However when I try to change it to my custom "myDeformer" it continues to show the linear skinning in the viewport,
I have print statements as you can see above when "deform" and "computebounds" are called... and neither seem to get called.

They DO get called if I "agent_unpack" after the agentLayer node however. The viewport shows the mesh at the origin in T-pose as I would expect, since I'm not doing anything yet in "mydeformer". Also if unpacking, you can notice changes in skinning between linear and dualquat as well for example, but no changes before the unpack.

Is this something related to a draw override when agents are packed, that they only evaluate linear or blendshape ? I know that there's an OpenGL version of linearskinning that is used for fast viewport display, but I wasn't aware of blendshape also having an override for that as well? It seems like packedAgent = "use OpenGL shapedeformer display"? Is this whats going on?

Is there a way around this even if its slower to draw? something that allows disabling of the OpenGL override? Ideally on a per agent basis?


thanks for any information!
Edited by redpaw - 2025年7月10日 17:40:32
User Avatar
スタッフ
792 posts
Joined: 10月 2012
オフライン
redpaw
What I'm ultimately after is being able to swap out the agentShapedeformer on particular shapes after the
Agent is set up.. Is this possible without unpacking the agent?

For example if I were to swap linearskinning to blendshape on a specific agent?
What node / workflow could I use to do this? Can I do this with an attribute?
The deformer is a property of the shape binding (https://www.sidefx.com/docs/houdini/crowds/agents.html#layer), so you had the right idea with creating a separate layer which binds your custom deformer to the shape.
Then, after you've populated the crowd you can use something like the Crowd Assign Layers SOP, or just an Agent Edit SOP etc, to switch which layers are assigned to each agent.

redpaw
Is this something related to a draw override when agents are packed, that they only evaluate linear or blendshape ?
Yeah that's correct, the viewport only provides shaders for instanced linear skinning and blendshapes for agents, so custom deformers are only applied when unpacking or for non-viewport rendering, etc. I suspect the viewport is falling back to linear skinning for unknown custom deformers so that there is still some visualization of the agents' pose if possible.

I don't think there's a workaround other than unpacking, but this would be something to submit an RFE for
  • Quick Links