[HDK] Any GUI_PrimitiveHook example?

   17889   40   0
User Avatar
Staff
5156 posts
Joined: July 2005
Offline
RE_ElementArray gives you the ability to add primitive connectivity indices in a fairly easy way:

RE_ElementArray *elem = new RE_ElementArray();

elem->setPrimitiveType(RE_PRIM_POLYGONS);
elem->tessellateToTriangles(true); // basically creates a trifan, no complex convexing

elem->beginPrims(r, true, num_vertices_hint);

elem->addPrim(r, num_points_in_polygon, point_indices);
// add more polygons…

elem->endPrims(r);

regeometry->connectIndexedPrimsI(r, 0, elem);


The RE_Geometry class takes ownership of the element array. If you have a set of constant sided-polygons, you can call addPrims() instead with the entire list of vertices (say, for RE_PRIM_QUADS), and that's a bit faster.

Also, by “point indices” I mean the index of the point in your vertex array, not a Houdini GA_Index of a point in the detail.
User Avatar
Member
678 posts
Joined: July 2005
Offline
Thanks for answers. This ElementArray looks tasty .

I should have some time this weekend so I will sit to this again.
User Avatar
Member
678 posts
Joined: July 2005
Offline
Couple problems:

1. My geometry doesn't update when it changes. If I bump up amount of points it's still shaded in the same way as it was when I turned on hook first time. I thought that GR_GEO_TOPOLOGY_CHANGED should force it to update, no?

2. Another thing is, when I turn on the hook it changes colors of persp and cam options in viewport, why? (see attached pic)

3. I thought that when the hook is not turned on, Houdini will create geometry as always, but instead it doesn't create any geometry and I get empty viewport. Is there a way to force it to use standard way of rendering when hook is not ON?

4. Also, if there is more than one OBJ node in scene, only geometry from one of them is shaded and second one is not, why?

Below is updated code:

DEFINE_GUI_ColorTopology.h

#ifndef _DEFINE_GUI_COLOR_TOPOLOGY_
#define _DEFINE_GUI_COLOR_TOPOLOGY_

// —————— INCLUDES —————— //

#include <GUI/GUI_PrimitiveHook.h>
#include <UT/UT_DSOVersion.h>
#include <GR/GR_Primitive.h>
#include <GR/GR_GeoRender.h>
#include <GR/GR_DisplayOption.h>
#include <GR/GR_OptionTable.h>
#include <GR/GR_UserOption.h>
#include <GR/GR_UtilsGL3.h>
#include <GEO/GEO_PrimPoly.h>
#include <GT/GT_GEOPrimitive.h>
#include <DM/DM_RenderTable.h>
#include <RE/RE_Geometry.h>
#include <RE/RE_ElementArray.h>

#include <vector>
#include <ostream>

// —————— SETTINGS —————— //

// hook and primitive class names
#define THIS_HOOK_NAME “GUI_ColorTopology”
#define THIS_PRIMITIVE_NAME “GR_ColoredPrimitive”

// option settings
#define TOPOLOGY_COLOR_OPTION_NAME “color_topology”
#define TOPOLOGY_COLOR_OPTION_LABEL “Color Topology”

// topology groups
enum TOPOLOGY_POLYGON_GROUP_OF
{
TWO = 2,
THREE,
FOUR,
FIVE,
SIX,
MORE_THAN_SIX
};

// —————— DEBUG ——————
//#define PRINT_REPORT
#define REPORT(what, report) std::cout << what << report << std::endl;

#endif


GUI_ColorTopology.h

#ifndef _GUI_COLOR_TOPOLOGY_
#define _GUI_COLOR_TOPOLOGY_

#include “DEFINE_GUI_ColorTopology.h”

namespace Mantragora
{
namespace Viewport
{
// hook ——————————————————————
class GUI_ColorTopologyHook : public GUI_PrimitiveHook
{
public:
GUI_ColorTopologyHook();

virtual
~GUI_ColorTopologyHook();

virtual auto
createPrimitive(const GT_PrimitiveHandle& gt_prim, const GEO_Primitive* geo_prim, const GR_RenderInfo* info, const char* cache_name, GR_PrimAcceptResult& processed)
-> GR_Primitive*;
};

// primitive ————————————————————-
class GR_ColoredPrimitive : public GR_Primitive
{
public:
// STANDARD CLASS METHODS ————————————————-
GR_ColoredPrimitive(const GR_RenderInfo* info, const char* cache_name, const GEO_Primitive* prim);

virtual
~GR_ColoredPrimitive();

virtual auto
className() const
-> const char*;

virtual auto
acceptPrimitive(GT_PrimitiveType t, int geo_type, const GT_PrimitiveHandle& ph, const GEO_Primitive* prim)
-> GR_PrimAcceptResult;

// GEOMETRY PROCESSING ————————————————-
virtual auto
update(RE_Render* r, const GT_PrimitiveHandle& primh, const GR_UpdateParms& p)
-> void;

virtual auto
render(RE_Render* r, GR_RenderMode render_mode, GR_RenderFlags flags, const GR_DisplayOption* opt, const RE_MaterialList* materials)
-> void;

// HOOK ON/OFF ————————————————-
virtual auto
renderDecoration(RE_Render* r, GR_Decoration decor, const GR_DecorationParms& p)
-> void;

private:
// CUSTOM SHADING ————————————————-
auto
_ColorTopology(RE_Render*r, const GR_DisplayOption* opts)
-> void;

// HELPERS & OTHER ————————————————-
auto
_RecreatePolygon(RE_Render* r, const GEO_PrimPoly* polygon, int npts, RE_Geometry* geo, int group, RE_PrimType type)
-> void;

auto
_MakeIndices(unsigned int howmany)
-> std::vector<int>;

RE_Geometry* _myGeo;
};
}
}

#endif



GUI_ColorTopology.cpp

#include “GUI_ColorTopology.h”
using namespace Mantragora::Viewport;

// HOOK_REGISTRATION ————————————————————————
void newRenderHook(GR_RenderTable *table)
{
DM_RenderTable* dm_table = static_cast<DM_RenderTable*>(table);
const int priority = 0;

// register hook
dm_table->registerGEOHook(new GUI_ColorTopologyHook(), GA_PrimitiveTypeId(GA_PRIMPOLY), priority, GUI_HOOK_FLAG_NONE);
// register custom display option
dm_table->installGeometryOption(TOPOLOGY_COLOR_OPTION_NAME, TOPOLOGY_COLOR_OPTION_LABEL);
}

// HOOK_INITIALIZATION ————————————————————————
GUI_ColorTopologyHook::GUI_ColorTopologyHook() : GUI_PrimitiveHook(THIS_HOOK_NAME){}
GUI_ColorTopologyHook::~GUI_ColorTopologyHook(){}

auto
GUI_ColorTopologyHook::createPrimitive(const GT_PrimitiveHandle& gt_prim, const GEO_Primitive* geo_prim, const GR_RenderInfo* info, const char* cache_name, GR_PrimAcceptResult& processed)
-> GR_Primitive*
{
// catch our primitive to prevent other hooks from taking over it
if(geo_prim->getTypeId().get() == GA_PRIMPOLY)
{
processed = GR_PROCESSED;
return new GR_ColoredPrimitive(info, cache_name, geo_prim);
}

return NULL;
}

// PRIMITIVE INITIALIZATION ————————————————————————
GR_ColoredPrimitive::GR_ColoredPrimitive(const GR_RenderInfo* info, const char* cache_name, const GEO_Primitive* prim) : GR_Primitive(info, cache_name, GA_PrimCompat::TypeMask(0)) { _myGeo = NULL; }
GR_ColoredPrimitive::~GR_ColoredPrimitive() { delete _myGeo; }

auto
GR_ColoredPrimitive::className() const
-> const char*
{ return THIS_PRIMITIVE_NAME; }

auto
GR_ColoredPrimitive::acceptPrimitive(GT_PrimitiveType t, int geo_type, const GT_PrimitiveHandle& ph, const GEO_Primitive* prim)
-> GR_PrimAcceptResult
{
if(geo_type == GA_PRIMPOLY)
return GR_PROCESSED;

return GR_NOT_PROCESSED;
}

// HOOK ON/OFF ————————————————-
auto
GR_ColoredPrimitive::renderDecoration(RE_Render* r, GR_Decoration decor, const GR_DecorationParms& p)
-> void
{
if(decor >= GR_USER_DECORATION)
{
// find what option is selected
int index = decor - GR_USER_DECORATION;
const GR_UserOption* option = GRgetOptionTable()->getOption(index);

// set rendering based on selected option
if(!strcmp(option->getName(), TOPOLOGY_COLOR_OPTION_NAME)) _ColorTopology(r, p.opts);
}
}

// CUSTOM SHADING ————————————————-
auto
GR_ColoredPrimitive::_ColorTopology(RE_Render* r, const GR_DisplayOption* opts)
-> void
{
// enable smooth lines if the option is on
if(opts->common().antiAliasedLines())
{
r->pushSmoothLines();
r->smoothBlendLines(RE_SMOOTH_ON);
}

if(getRenderVersion() >= GR_RENDER_GL3)
{
r->pushShader(GR_UtilsGL3::getWireShader(r));

// TODO: color for OGL3

r->popShader();
}
else
{
r->pushColor((UT_Color(UT_RGB, 0.9, 0.45, 0.0)));
_myGeo->draw(r, TWO);

r->pushColor(UT_Color(UT_RGB, 0.9, 0.9, 0.3));
_myGeo->draw(r, THREE);

r->pushColor((UT_Color(UT_RGB, 0.15, 0.9, 0.15)));
_myGeo->draw(r, FOUR);

r->pushColor((UT_Color(UT_RGB, 0.23, 0.9, 0.9)));
_myGeo->draw(r, FIVE);

r->pushColor((UT_Color(UT_RGB, 0.12, 0.5, 0.9)));
_myGeo->draw(r, SIX);

r->pushColor((UT_Color(UT_RGB, 0.56, 0.22, 0.9)));
_myGeo->draw(r, MORE_THAN_SIX);

r->popColor();
}

r->popLineWidth();
r->popPointSize();

//r->popLineStyle();
if(opts->common().antiAliasedLines()) r->popSmoothLines();
}

// GEOMETRY PROCESSING ————————————————-
auto
GR_ColoredPrimitive::update(RE_Render* r, const GT_PrimitiveHandle& primh, const GR_UpdateParms& p)
-> void
{
if(p.reason & (GR_GEO_CHANGED |
GR_GEO_TOPOLOGY_CHANGED))
{
// get polygon
auto prim = static_cast<const GT_GEOPrimitive*>(primh.get());
auto poly = static_cast<const GEO_PrimPoly*>(prim->getPrimitive(0));

#ifdef PRINT_REPORT
REPORT(“VTX count: ”, poly->getVertexCount());
REPORT(“Prim number: ”, poly->getNum());
#endif // PRINT_REPORT

// create new geo based on polygon point count
auto points = poly->getPointRange();
auto npts = points.getEntries();
auto new_geo = false;
if(!_myGeo)
{
_myGeo = new RE_Geometry(npts);
new_geo = true;
}

// create position array and populate it
UT_Vector3FArray positions;
for(GA_Iterator pointIt(points); !pointIt.atEnd(); pointIt.advance())
{
// get point position
auto currentPosition = poly->getDetail().getPos3(*pointIt);

// add position to array
positions.append(currentPosition);
}

if(getRenderVersion() >= GR_RENDER_GL3) _myGeo->createAttribute(r, “P”, RE_GPU_FLOAT32, 3, positions.array()->data());
else _myGeo->createArray(r, RE_BUFFER_POSITION, 0, RE_GPU_FLOAT32, 3, positions.array()->data());

if(new_geo)
{
// connect and add to correct group based on point count
switch(npts)
{
case TWO:
_RecreatePolygon(r, poly, npts, _myGeo, TWO, RE_PRIM_LINES);
break;
case THREE:
_RecreatePolygon(r, poly, npts, _myGeo, THREE, RE_PRIM_TRIANGLES);
break;
case FOUR:
_RecreatePolygon(r, poly, npts, _myGeo, FOUR, RE_PRIM_QUADS);
break;
case FIVE:
_RecreatePolygon(r, poly, npts, _myGeo, FIVE, RE_PRIM_POLYGONS);
break;
case SIX:
_RecreatePolygon(r, poly, npts, _myGeo, SIX, RE_PRIM_POLYGONS);
break;
}

if (npts >= MORE_THAN_SIX) _RecreatePolygon(r, poly, npts, _myGeo, MORE_THAN_SIX, RE_PRIM_POLYGONS);
}
}
}

auto
GR_ColoredPrimitive::render(RE_Render* r, GR_RenderMode render_mode, GR_RenderFlags flags, const GR_DisplayOption* opt, const RE_MaterialList* materials)
-> void
{
// The native Houdini primitive for primpoly will do the rendering.
}

// HELPERS & OTHER ————————————————-
auto
GR_ColoredPrimitive::_RecreatePolygon(RE_Render* r, const GEO_PrimPoly* polygon, int npts, RE_Geometry* geo, int group, RE_PrimType type)
-> void
{
auto elements = new RE_ElementArray();

elements->setPrimitiveType(type);
elements->tessellateToTriangles(true);

elements->beginPrims(r, true, npts);
elements->addPrim(r, npts, &_MakeIndices(npts));
elements->endPrims(r);

_myGeo->connectIndexedPrimsI(r, group, elements);
}

auto
GR_ColoredPrimitive::_MakeIndices(unsigned int howmany)
-> std::vector<int>
{
std::vector<int> indices;
for(unsigned i = 0; i < howmany; i++) indices.push_back(i);

return indices;
}

Attachments:
wtf.jpg (25.2 KB)

User Avatar
Staff
5156 posts
Joined: July 2005
Offline
1. My geometry doesn't update when it changes. If I bump up amount of points it's still shaded in the same way as it was when I turned on hook first time. I thought that GR_GEO_TOPOLOGY_CHANGED should force it to update, no?
Is your update method not being called when you alter the geometry? (how are you changing the #points?)

2. Another thing is, when I turn on the hook it changes colors of persp and cam options in viewport, why? (see attached pic)
You've pushed the color 6x, and only popped it once. Try pushColor(), setColor() x5, popColor() instead. The RE push/pop methods are stack-based and need to be paired.

3. I thought that when the hook is not turned on, Houdini will create geometry as always, but instead it doesn't create any geometry and I get empty viewport. Is there a way to force it to use standard way of rendering when hook is not ON?
If you return GR_PROCESSED from your hook, it assumes your hook is entirely taking care of the rendering. Return GR_PROCESSED_NON_EXCLUSIVE instead if you still want the Houdini geometry to appear. There is currently no way to have the Houdini geometry update but not render while your hook is active (nor can I really think of a way that this could be done).

I would enable polygon offset and render your polygons with negative offsets so that they appear in front of the Houdini geometry, possibly with blending so that you can see the underlying shading.

4. Also, if there is more than one OBJ node in scene, only geometry from one of them is shaded and second one is not, why?
I'm not sure what you mean by ‘shaded’. Perhaps try fixing the color bug above,as that's going to cause some problems.

Also, this hook isn't going to scale well with thousands of polygons. Do you have plans to collect multiple polygons and process them all in one hook?
User Avatar
Member
678 posts
Joined: July 2005
Offline
twod
Also, this hook isn't going to scale well with thousands of polygons. Do you have plans to collect multiple polygons and process them all in one hook?

Yes I do, but first I want to get comfortable with what's happening there.

twod
Is your update method not being called when you alter the geometry? (how are you changing the #points?)

If I place circle and start changing amount of points, color stays the same and geometry is redrawed like if it had still the same amount of points.

twod
I'm not sure what you mean by ‘shaded’. Perhaps try fixing the color bug above,as that's going to cause some problems.

I'm mean colored.

Thanks.
User Avatar
Member
678 posts
Joined: July 2005
Offline
Here is what's happening.

Since I could't get to work my project with GR_PROCESSED_NON_EXCLUSIVE (it was halting my Houdini and System), I took GUI_PrimFramework example and just changed
DESIRED_PRIM_TYPE to GA_PRIMPOLY and changed in createPrimitive() to processed = GR_PROCESSED_NON_EXCLUSIVE
Now if I run Houdini and load polygon scene it halts Houdini and OS so badly that the only way to get control of it again is to restart computer.

I can see in Task Manager that the amount of memory used by Houdini jumps to 1.7GB from 132MB (in just under 1 second) with just a simple 6 polygon scene. That's before I lose total control on system.

Any ideas why this may happen?
User Avatar
Staff
5156 posts
Joined: July 2005
Offline
mantragora
Yes I do, but first I want to get comfortable with what's happening there.
Great, just checking. I wouldn't have wanted you finish the hook and then have to recode it for multiple polygons.

'm mean colored.
This is very likely due to the push/popColor() mismatch, then. The color stack isn't very large.

Now if I run Houdini and load polygon scene it halts Houdini and OS so badly that the only way to get control of it again is to restart computer.
There was an error in the primitive hook loop that was causing it to constantly create a new primitive for that one polygon. This has been fixed in tomorrow's build (231).

Incidentally, putting your swap partition or pagefile on an SSD is a good way to avoid long system hangs due to runaway memory usage.
User Avatar
Member
678 posts
Joined: July 2005
Offline
k, till next build then , thank you
User Avatar
Member
678 posts
Joined: July 2005
Offline
meh, GR_PROCESSED_NON_EXCLUSIVE works almost like GUI_HOOK_FLAG_AUGMENT_PRIM. I need to see default rendering of primitives only when Hook is turned OFF. With GR_PROCESSED_NON_EXCLUSIVE I get my geometry mixed with default one when hook is ON (See attached pic.) So is there a way to have the best of both worlds?

Watch the video to see all the flaws I get:
1. Position change doesn't update.
2. And since position doesn't update correctly it looks like if there is only one OBJ colored, but as you can see around 54 second of movie I do get two green quads, one from current OBJ I'm in and second from Ghosted one.
3. Changing amount of points renders geometry like if it was still in the group it was added the moment I turned hook ON for the first time. So if quad become triangle it's still recreated and colored as quad.
4. Two OBJ selected at once and I lose color like if the hook was turned OFF.

Attachments:
Untitled.mp4 (1.3 MB)
meh.jpg (5.7 KB)

User Avatar
Member
678 posts
Joined: July 2005
Offline
Ok, so I checked GUI_PolySoupBox example and it also doesn't update positions correcty if you are on OBJ level. Move object and the BoundingBox will stay in the center of the world. Any more info what should I look for to update positions?
Edited by - Nov. 15, 2013 07:15:50

Attachments:
bug_160.jpg (15.3 KB)

User Avatar
Staff
5156 posts
Joined: July 2005
Offline
The image is exhibiting z-fighting. It's caused when two coplanar polygons with slight precision differences in their vertices are displayed. You can use polygon offset to move your polygon forward slightly:

bool polyoff = r->getPolygonOffset();
float offv, offc;
r->getOffsetAmount(offv, offc);
r->setOffsetAmount(-2.0, -2.0);
if(!polyoff) r->polygonOffset(true);

// draw

r->setOffsetAmount(offv, offc);
if(!polyoff) r->polygonOffset(false);

I've also updated the GUI_PolySoupBox example for tomorrow's build. Essentially you need to grab the object transform and apply it to the view matrix in GL2:

UT_Matrix4D obj = r->getUniform(RE_UNIFORM_OBJECT_MATRIX)->getMatrix4();

r>pushMatrix(0);
r->multiplyMatrix(obj);

// draw

r>popMatrix(0);

The RE_Uniform::getMatrix4() method is new in 232; otherwise you need to do getValues() and memcpy() it into a UT_Matrix4D.
User Avatar
Member
678 posts
Joined: July 2005
Offline
…Aaaaand I'm back!

What happened with new builds? I managed to downloaded 232 when it was available for download but it looks that not everything is available in it.

There is no

getPolygonOffset()

in 232.

Maybe it should be

r->isPolygonOffset()

?
It also returns bool. I used this and it seems to work.

————————————————————————————–

The RE_Uniform::getMatrix4() method is new in 232;

That did the trick . Thank you.

Now the only things left to do are:
1) Figuring out why when I change amount of points my groups are not updated and prims are still colored and redrawed as they where the first time hook was turned on.
2) Implementing it with GUI_HOOK_FLAG_COLLECT_PRIMS, but for this I will first check the last GUI_PrimVolumeVelField example that implements it to see where it leads me.
User Avatar
Member
1 posts
Joined: Nov. 2013
Offline
It's Cool
รับผลิตครีม [at-z.co.th],โรงงานผลิตครีม [at-z.co.th],ผลิตครีม [at-z.co.th]
User Avatar
Member
678 posts
Joined: July 2005
Offline
What if I would like to merge DM and GUI hooks together (DM for info display and GUI for primitive rendering) in one tool activated by one switch in menu?

1) My first Idea was to inherit from both, so I end up with:

class GUI_DM_Renderer : public DM_SceneRenderHook, public GR_Primitive
{}

class GUI_DM_Manager : public DM_SceneHook, public GUI_PrimitiveHook
{}

but then I encountered problem with GUI_DM_Manager ::newSceneRender() method.

When I inherit from DM and GUI my constructor for new mixed GR_Primitive/DM_SceneRenderHook renderer contains things that this method arguments don't fullfill.
This is mixed constructor:

GUI_DM_Renderer(DM_VPortAgent& vport, const GR_RenderInfo* info, const char* cache_name, const GEO_Primitive* prim)

while GUI_DM_Manager::newSceneRender() method gives me only those

newSceneRender(DM_VPortAgent& vport, DM_SceneHookType type, DM_SceneHookPolicy policy)

so I can't create new instance of GUI_DM_Renderer.

2) Second idea was to develop each as separate construct and then just register installGeometryOption() and installSceneOption() under the same name, so I would end up with one switch for both. It compiled OK but didn't worked. Only one hook was working. And even if it would work I'm not sure how I should pass information from one hook to the other.

3) Third idea was to split only DM_SceneRenderHook and GR_Primitive as separate construct and just merge DM_SceneHook and GUI_PrimitiveHook into one class that I will instantiate and pass to registerSceneHook(), registerGEOHook() and register under one name in installSceneOption(), installGeometryOption().
Like this:

class GUI_Renderer : public GR_Primitive
{}
class DM_Renderer : public DM_SceneRenderHook
{}

class GUI_DM_Manager : public DM_SceneHook, public GUI_PrimitiveHook
{}



but like with second idea I'm not sure how I should pass informaton between GUI_Renderer and DM_Renderer.

How should I approach this marriage?
Edited by - Nov. 27, 2013 00:47:37
User Avatar
Member
678 posts
Joined: July 2005
Offline
4) Oh, and while I'm at this, how should I approach marriage of SOP and GUI? For example I have tool for creating polygons and while I select points I would like to have GUI showing me preview feedback of how polygon looks before I finish and accept.
User Avatar
Member
678 posts
Joined: July 2005
Offline
I think third solution will be something that may work. I partially solved the problem of sharing data between

class GUI_Renderer : public GR_Primitive

and

class DM_Renderer : public DM_SceneRenderHook

by creating my custom data in my

class GUI_DM_Manager : public DM_SceneHook, public GUI_PrimitiveHook

and passing it to constructor of DM_Renderer and GUI_Renderer.

The only problem is that when I cout some shared data from GUI_Renderer::update() and DM_Renderer::render() I get sometimes some funky number from DM_Renderer::render(). For exampe, If I set

int = 5

in my Custom data in GUI_Renderer (only it can modify data), DM_Renderer prints correct number but one I move my camera it loses it and prints some random number like 67947360 or similar. Now if I turn ON/OFF Bypass flag on my geometry once again DM_Renderer starts printing the same number like GUI_Renderer.

And even if my custom data starts with initialized

int = 5

I do get this funky number when I launch Houdini. It is only updated when I put geometry node in network view.

I can record movie if it's not clear what I'm asking here .

Still, question number 4 (SOP/FUI marriage) stays unanswered and if there is a better way to share data between GUI/DM I would love to know it too.
User Avatar
Member
678 posts
Joined: July 2005
Offline
Ok, this is what I got and it seems to work. Both hooks can communicate and are turned on with only one option.

#ifndef _GUI_DM_TEMPLATE_
#define _GUI_DM_TEMPLATE_

#include <UT/UT_DSOVersion.h>
#include <DM/DM_SceneHook.h>
#include <DM/DM_GeoDetail.h>
#include <DM/DM_RenderTable.h>
#include <DM/DM_VPortAgent.h>
#include <GEO/GEO_PrimPoly.h>
#include <GUI/GUI_PrimitiveHook.h>
#include <GUI/GUI_DisplayOption.h>
#include <GR/GR_Primitive.h>
#include <GT/GT_GEOPrimitive.h>
#include <RE/RE_Font.h>
#include <RE/RE_Render.h>
#include <RE/RE_ElementArray.h>
#include <RE/RE_Geometry.h>
#include <RE/RE_LightList.h>
#include <RE/RE_ShaderHandle.h>
#include <ostream>

using std::cout;
using std::endl;

#define THIS_HOOK_NAME “GUI_DM_Manager”
#define THIS_PRIMITIVE_NAME “GUI_Renderer”

#define THIS_OPTION_SMALL_NAME “mixed”
#define THIS_OPTION_BIG_NAME “Mixed”

namespace Mantragora
{
namespace Viewport
{
// SHARED DATA ————————————————————
class GUI_DM_SharedData
{
protected:
static int _numNGons;
};

// GUI RENDERER ————————————————————
class GUI_Renderer : public GR_Primitive, public GUI_DM_SharedData
{
public:
GUI_Renderer(const GR_RenderInfo* info, const char* cache_name, const GEO_Primitive* prim);

virtual ~GUI_Renderer()
override;

virtual auto className() const
-> const char* override;

virtual auto acceptPrimitive(GT_PrimitiveType t, int geo_type, const GT_PrimitiveHandle& ph, const GEO_Primitive* prim)
-> GR_PrimAcceptResult override;

virtual auto resetPrimitives()
-> void override;

virtual auto update(RE_Render* r, const GT_PrimitiveHandle& primh, const GR_UpdateParms& p)
-> void override;

virtual auto render(RE_Render* r, GR_RenderMode render_mode, GR_RenderFlags flags, const GR_DisplayOption* opt, const RE_MaterialList* materials)
-> void override;

virtual auto renderDecoration(RE_Render* r, GR_Decoration decor, const GR_DecorationParms& parms)
-> void override;

private:
RE_Geometry* _myGeo;
};

// DM RENDERER ————————————————————
class DM_Renderer : public DM_SceneRenderHook, public GUI_DM_SharedData
{
public:
DM_Renderer(DM_VPortAgent& vport);

virtual ~DM_Renderer()
override;

virtual auto render(RE_Render* r, const DM_SceneHookData& hook_data)
-> bool override;
};

// MANAGER ————————————————————
class GUI_DM_Manager : public DM_SceneHook, public GUI_PrimitiveHook
{
public:
GUI_DM_Manager();

virtual ~GUI_DM_Manager()
override;

// DM
virtual auto newSceneRender(DM_VPortAgent& vport, DM_SceneHookType type, DM_SceneHookPolicy policy)
-> DM_SceneRenderHook* override;

virtual auto retireSceneRender(DM_VPortAgent& vport, DM_SceneRenderHook* hook)
-> void override;

// GUI
virtual auto createPrimitive(const GT_PrimitiveHandle& gt_prim, const GEO_Primitive* geo_prim, const GR_RenderInfo* info, const char* cache_name, GR_PrimAcceptResult& processed)
-> GR_Primitive* override;
};
}
}

#endif



#include “GUI_DM_Template.h”
using namespace Mantragora::Viewport;

// REGISTRATION ————————————————————
void newRenderHook(GR_RenderTable* table)
{
auto dm_table = static_cast<DM_RenderTable*>(table);
const int prim_hook_priority = 1;

auto hook = new GUI_DM_Manager;

auto success = dm_table->registerSceneHook(hook, DM_HOOK_FOREGROUND, DM_HOOK_AFTER_NATIVE);
success = dm_table->installSceneOption(THIS_OPTION_SMALL_NAME, THIS_OPTION_BIG_NAME);

success = dm_table->registerGEOHook(hook, GA_PrimitiveTypeId(GA_PRIMPOLY), prim_hook_priority);//, GUI_HOOK_FLAG_COLLECT_PRIMS);
success = dm_table->installGeometryOption(THIS_OPTION_SMALL_NAME, THIS_OPTION_BIG_NAME);
}

// SHARED DATA ————————————————————
int GUI_DM_SharedData::_numNGons = 0;

// MANAGER ————————————————————
GUI_DM_Manager::GUI_DM_Manager() : DM_SceneHook(THIS_HOOK_NAME, 0), GUI_PrimitiveHook(THIS_HOOK_NAME, GUI_RENDER_MASK_GL3) {}
GUI_DM_Manager::~GUI_DM_Manager() {}

auto GUI_DM_Manager::newSceneRender(DM_VPortAgent& vport, DM_SceneHookType type, DM_SceneHookPolicy policy)
-> DM_SceneRenderHook*
{ return new DM_Renderer(vport); }

auto GUI_DM_Manager::retireSceneRender(DM_VPortAgent& vport, DM_SceneRenderHook* hook)
-> void
{ delete hook; }

auto GUI_DM_Manager::createPrimitive(const GT_PrimitiveHandle& gt_prim, const GEO_Primitive* geo_prim, const GR_RenderInfo* info, const char* cache_name, GR_PrimAcceptResult& processed)
-> GR_Primitive*
{
// catch our primitive to prevent other hooks from taking over it
if(geo_prim->getTypeId().get() == GA_PRIMPOLY)
{
processed = GR_PROCESSED_NON_EXCLUSIVE;
return new GUI_Renderer(info, cache_name, geo_prim);
}
return NULL;
}

// GUI RENDERER ————————————————————
GUI_Renderer::GUI_Renderer(const GR_RenderInfo* info, const char* cache_name, const GEO_Primitive* prim) : GR_Primitive(info, cache_name, GA_PrimCompat::TypeMask(0))
{ _myGeo = NULL; }

GUI_Renderer::~GUI_Renderer()
{ delete _myGeo; }

auto GUI_Renderer::className() const
-> const char*
{ return THIS_PRIMITIVE_NAME; }

auto GUI_Renderer::acceptPrimitive(GT_PrimitiveType t, int geo_type, const GT_PrimitiveHandle& ph, const GEO_Primitive* prim)
-> GR_PrimAcceptResult
{
if(geo_type == GA_PRIMPOLY) return GR_PROCESSED;

return GR_NOT_PROCESSED;
}

auto GUI_Renderer::resetPrimitives()
-> void
{
}

// DM RENDERER ————————————————————
DM_Renderer:M_Renderer(DM_VPortAgent& vport)
: DM_SceneRenderHook(vport, DM_VIEWPORT_ALL) {}

DM_Renderer::~DM_Renderer() {}

// MAIN ————————————————————
auto GUI_Renderer::update(RE_Render* r, const GT_PrimitiveHandle& primh, const GR_UpdateParms& p)
-> void
{
}

auto GUI_Renderer::render(RE_Render* r, GR_RenderMode render_mode, GR_RenderFlags flags, const GR_DisplayOption* opt, const RE_MaterialList* materials)
-> void
{}

auto GUI_Renderer::renderDecoration(RE_Render* r, GR_Decoration decor, const GR_DecorationParms& p)
-> void
{
if(decor >= GR_USER_DECORATION)
{
// find what option is selected
auto index = decor - GR_USER_DECORATION;
auto option = GRgetOptionTable()->getOption(index);

// set rendering based on selected option
if(!strcmp(option->getName(), THIS_OPTION_SMALL_NAME))
{
}
}
}

auto DM_Renderer::render(RE_Render* r, const DM_SceneHookData& hook_data)
-> bool
{
return false;
}


Is this the correct way in how hooks should be paired together?
User Avatar
Staff
5156 posts
Joined: July 2005
Offline
I think first I should explain how the different hooks are created. A Scene Hook is created for each viewport, for each viewer. You'll get 4 scene hooks when you start Houdini in the Build desktop, for example. A Primitive Hook is created on a GU_Detail when its primitive type matches, and each object has a separate detail. Even if you collect all the primitives from a single detail into one GR_Primitive subclass, the presence of multiple objects may create more than one of your primitives. So the relationship between a Scene and Primitive hook is many-to-many.

For your primitive hook to communicate to your scene hook, you'd have to set up a singleton map of all your primitives' data, using GU_Detail::getUniqueId()'s as the key, which both hooks have access to. For multiple objects, I'd suggest displaying the info for the current object (DM_VPortAgent::getCurrentDetail()) in the scene hook, if any. You would grab the GU_Detail of the current object via getDetailHandle(), and do a map lookup using its unique ID. The GR_Primitive would need to stash data into the map using GR_UpdateParms::geometry->getUniqueId() as the key, and remove it when the primitive is deleted.

As for SOPs, there is no solid connection between the viewport and a SOP except for the main GU_Detail it produces and its guide GU_Details (see SOP_Node.h). However, you could add a private detail attribute such as “_poly_face_hook_enable_” and only instantiate your GR_Primitive if this is found in the GU_Detail that is being refined (check for this attrib in your GUI_PrimitiveHook::createPrimitive). The private detail attribute won't be visible, nor will it be copied to the SOP's outputs.

Hope that helps.
User Avatar
Member
4494 posts
Joined: Feb. 2012
Offline
Since this is viewport related, is there a file that lists all the special viewport related attributes like gl_lit? There is no mention of them elsewhere. Perhaps they are listed in viewport header files?
Senior FX TD @ Industrial Light & Magic
Get to the NEXT level in Houdini & VEX with Pragmatic VEX! [www.pragmatic-vfx.com]

youtube.com/@pragmaticvfx | patreon.com/animatrix | animatrix2k7.gumroad.com
User Avatar
Staff
5156 posts
Joined: July 2005
Offline
It's not a terribly exciting list. There are only two, gl_lit and gl_wireframe, both must be detail attributes and have either zero (off) or non-zero (on) as the value.

I'm not a fan of detail-based display options, since simply toggling these will cause cooks for all dependent SOPs. In this case, though, it's somewhat unavoidable.
  • Quick Links