HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
SOP_SplitPointsHDK.C
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2024
3  * Side Effects Software Inc. All rights reserved.
4  *
5  * Redistribution and use of Houdini Development Kit samples in source and
6  * binary forms, with or without modification, are permitted provided that the
7  * following conditions are met:
8  * 1. Redistributions of source code must retain the above copyright notice,
9  * this list of conditions and the following disclaimer.
10  * 2. The name of Side Effects Software may not be used to endorse or
11  * promote products derived from this software without specific prior
12  * written permission.
13  *
14  * THIS SOFTWARE IS PROVIDED BY SIDE EFFECTS SOFTWARE `AS IS' AND ANY EXPRESS
15  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
17  * NO EVENT SHALL SIDE EFFECTS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
18  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
19  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
20  * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
21  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
22  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
23  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  *
25  *----------------------------------------------------------------------------
26  * This SOP splits points, optionally limited based on differences in vertex
27  * or primitive attributes.
28  */
29 
30 #include "SOP_SplitPointsHDK.proto.h"
31 
32 #include "GEO_SplitPoints.h"
33 
34 #include <SOP/SOP_Node.h>
35 #include <GU/GU_Detail.h>
36 #include <GU/GU_Promote.h>
37 #include <PRM/PRM_Include.h>
39 #include <UT/UT_Assert.h>
40 #include <UT/UT_DSOVersion.h>
41 
42 using namespace UT::Literal;
43 
44 namespace HDK_Sample {
45 
46 //******************************************************************************
47 //* Setup *
48 //******************************************************************************
49 
51 {
52 public:
55 
56  virtual SOP_NodeParms *allocParms() const { return new SOP_SplitPointsHDKParms(); }
57  virtual UT_StringHolder name() const { return theSOPTypeName; }
58 
59  virtual CookMode cookMode(const SOP_NodeParms *parms) const { return COOK_INPLACE; }
60 
61  virtual void cook(const CookParms &cookparms) const;
62 
63  /// This is the internal name of the SOP type.
64  /// It isn't allowed to be the same as any other SOP's type name.
66 
67  /// This static data member automatically registers
68  /// this verb class at library load time.
70 
71  /// This is the parameter interface string, below.
72  static const char *const theDsFile;
73 };
74 
75 // The static member variable definitions have to be outside the class definition.
76 // The declarations are inside the class.
77 const UT_StringHolder SOP_SplitPointsHDKVerb::theSOPTypeName("hdk_splitpoints"_sh);
78 const SOP_NodeVerb::Register<SOP_SplitPointsHDKVerb> SOP_SplitPointsHDKVerb::theVerb;
79 
80 /// This is the SOP class definition.
82 {
83 public:
84  SOP_SplitPointsHDK(OP_Network *net, const char *, OP_Operator *entry);
85  virtual ~SOP_SplitPointsHDK();
86 
87  virtual OP_ERROR cookInputGroups(OP_Context &context, int alone);
88 
89  static OP_Node *myConstructor(OP_Network *net, const char *name, OP_Operator *entry);
90  static PRM_Template *buildTemplates();
91  virtual const SOP_NodeVerb *cookVerb() const;
92 
93  static void buildAttribMenu(
94  void *data, PRM_Name *entries, int size,
95  const PRM_SpareData *, const PRM_Parm *);
96 
97  virtual OP_ERROR cookMySop(OP_Context &context);
98 };
99 
100 static GA_GroupType
101 sopSplitPointsGroupType(SOP_SplitPointsHDKParms::GroupType parmgrouptype)
102 {
103  using namespace SOP_SplitPointsHDKEnums;
104  switch (parmgrouptype)
105  {
106  case GroupType::GUESS: return GA_GROUP_INVALID; break;
107  case GroupType::POINTS: return GA_GROUP_POINT; break;
108  case GroupType::PRIMS: return GA_GROUP_PRIMITIVE; break;
109  case GroupType::VERTICES: return GA_GROUP_VERTEX; break;
110  }
111  UT_ASSERT_MSG(0, "Unhandled group type!");
112  return GA_GROUP_INVALID;
113 }
114 
115 void
116 SOP_SplitPointsHDK::buildAttribMenu(
117  void *data, PRM_Name *entries, int size,
118  const PRM_SpareData *, const PRM_Parm *)
119 {
120  SOP_SplitPointsHDK *sop = (SOP_SplitPointsHDK *)data;
121  if (!sop)
122  return;
123 
124  const int input = 0; // input from which to obtain attribs
125 
126  sop->fillAttribNameMenu(entries, size, GA_ATTRIB_VERTEX, input);
127 }
128 static PRM_ChoiceList sop_attribmenu(
130  SOP_SplitPointsHDK::buildAttribMenu);
131 
133 SOP_SplitPointsHDK::buildTemplates()
134 {
135  static PRM_TemplateBuilder templ("SOP_SplitPointsHDK.C"_sh, SOP_SplitPointsHDKVerb::theDsFile);
136  if (templ.justBuilt())
137  {
139  templ.setChoiceListPtr("attribname", &sop_attribmenu);
140  }
141  return templ.templates();
142 }
143 
144 OP_Node *
145 SOP_SplitPointsHDK::myConstructor(OP_Network *net,const char *name,OP_Operator *entry)
146 {
147  return new SOP_SplitPointsHDK(net, name, entry);
148 }
149 
150 
151 SOP_SplitPointsHDK::SOP_SplitPointsHDK(OP_Network *net, const char *name, OP_Operator *entry)
152  : SOP_Node(net, name, entry)
153 {
155 }
156 
158 {}
159 
160 OP_ERROR
162 {
163  UT_ASSERT(alone);
164 
165  GA_GroupType grouptype = sopSplitPointsGroupType(
166  (SOP_SplitPointsHDKEnums::GroupType)evalInt("grouptype"_sh, 0, context.getTime()));
167 
168  const GA_Group *group;
170  context,
171  group,
172  alone != 0,
173  true, // do_selection
174  0, // group parm index
175  1, // group type parm index
176  grouptype,
177  true, // allow_reference
178  false // is_default_prim
179  );
180  return error();
181 }
182 
183 OP_ERROR
185 {
186  return cookMyselfAsVerb(context);
187 }
188 
189 const SOP_NodeVerb *
191 {
192  return SOP_SplitPointsHDKVerb::theVerb.get();
193 }
194 
195 } // End of HDK_Sample namespace
196 
197 /// newSopOperator is the hook that Houdini grabs from this dll
198 /// and invokes to register the SOP. In this case, we add ourselves
199 /// to the specified operator table.
201 {
202  table->addOperator(new OP_Operator(
204  "HDK Split Points", // UI name
205  HDK_Sample::SOP_SplitPointsHDK::myConstructor, // How to build the SOP
207  1, // Min # of sources
208  1, // Max # of sources
209  nullptr,// Custom local variables (none)
210  0)); // No flags: not a generator, no merge input, not an output
211 }
212 
213 namespace HDK_Sample {
214 
215 //******************************************************************************
216 //* Parameters *
217 //******************************************************************************
218 
219 /// This is a multi-line raw string specifying the parameter interface for this SOP.
220 const char *const SOP_SplitPointsHDKVerb::theDsFile = R"THEDSFILE(
221 {
222  name parameters
223  parm {
224  name "group"
225  label "Group"
226  type string
227  default { "" }
228  parmtag { "script_action" "import soputils\nkwargs['geometrytype'] = kwargs['node'].parmTuple('grouptype')\nkwargs['inputindex'] = 0\nsoputils.selectGroupParm(kwargs)" }
229  parmtag { "script_action_help" "Select geometry from an available viewport.\nShift-click to turn on Select Groups." }
230  parmtag { "script_action_icon" "BUTTONS_reselect" }
231  }
232  parm {
233  name "grouptype"
234  cppname "GroupType"
235  label "Group Type"
236  type ordinal
237  default { "0" }
238  menu {
239  "guess" "Guess from Group"
240  "vertices" "Vertices"
241  "points" "Points"
242  "prims" "Primitives"
243  }
244  }
245  parm {
246  name "useattrib"
247  cppname "UseAttrib"
248  label "Limit by Attribute"
249  type toggle
250  default { "0" }
251  nolabel
252  joinnext
253  }
254  parm {
255  name "attribname"
256  cppname "AttribName"
257  label "Attributes"
258  type string
259  default { "N" }
260  disablewhen "{ useattrib == 0 }"
261  }
262  parm {
263  name "tol"
264  label "Tolerance"
265  type log
266  default { "0.001" }
267  range { 0! 1 }
268  disablewhen "{ useattrib == 0 }"
269  }
270  parm {
271  name "promote"
272  label "Promote to Point Attribute"
273  type toggle
274  default { "0" }
275  disablewhen "{ useattrib == 0 }"
276  }
277 }
278 )THEDSFILE";
279 
280 
281 void
283 {
284  auto &&sopparms = cookparms.parms<SOP_SplitPointsHDKParms>();
285  GU_Detail *gdp = cookparms.gdh().gdpNC();
286  GOP_Manager gop;
287 
288  const GA_ElementGroup *group = nullptr;
289 
290  const UT_StringHolder &groupname = sopparms.getGroup();
291  if (groupname.isstring())
292  {
293  GA_GroupType grouptype = sopSplitPointsGroupType(sopparms.getGroupType());
294 
295  bool ok = true;
296  const GA_Group *anygroup = gop.parseGroupDetached(groupname, grouptype, gdp, true, false, ok);
297 
298  if (!ok || (anygroup && !anygroup->isElementGroup()))
299  {
300  cookparms.sopAddWarning(SOP_ERR_BADGROUP, groupname);
301  }
302  if (anygroup && anygroup->isElementGroup())
303  {
304  group = UTverify_cast<const GA_ElementGroup *>(anygroup);
305  }
306  }
307  notifyGroupParmListeners(cookparms.getNode(), 0, 1, gdp, group);
308 
309  if (sopparms.getUseAttrib() && sopparms.getAttribName().isstring())
310  {
311  fpreal tolerance = sopparms.getTol();
312  const bool promote = sopparms.getPromote();
313 
314  const char *pattern = sopparms.getAttribName().c_str();
315  if (UT_String::multiMatchCheck(pattern))
316  {
317  UT_StringArray attribsToPromote;
318  auto &&functor = [pattern,promote,gdp,group,tolerance,&attribsToPromote](GA_Attribute *attrib)
319  {
320  UT_String attribname_string(attrib->getName().c_str());
321  if (!attribname_string.multiMatch(pattern))
322  return;
323 
324  // NOTE: This will bump any data IDs as needed, if any points are split.
325  GEOsplitPointsByAttrib(gdp, group, attrib, tolerance);
326 
327  if (promote)
328  {
329  // Don't promote the attributes while iterating over them
330  attribsToPromote.append(attrib->getName());
331  }
332  };
333 
334  // NOTE: The iteration order must be alphabetical, instead of
335  // the hash table order, for consistency.
336  for (auto it = gdp->vertexAttribs().obegin(GA_SCOPE_PUBLIC); !it.atEnd(); ++it)
337  functor(it.item());
338  for (exint i = 0; i < attribsToPromote.size(); ++i)
339  {
340  GA_Attribute *attrib = gdp->findAttribute(GA_ATTRIB_VERTEX, GA_SCOPE_PUBLIC, attribsToPromote[i]);
341  if (attrib)
343  }
344  attribsToPromote.clear();
345 
346  for (auto it = gdp->primitiveAttribs().obegin(GA_SCOPE_PUBLIC); !it.atEnd(); ++it)
347  functor(it.item());
348  for (exint i = 0; i < attribsToPromote.size(); ++i)
349  {
350  GA_Attribute *attrib = gdp->findAttribute(GA_ATTRIB_PRIMITIVE, GA_SCOPE_PUBLIC, attribsToPromote[i]);
351  if (attrib)
353  }
354  attribsToPromote.clear();
355 
356  for (auto it = gdp->vertexAttribs().obegin(GA_SCOPE_GROUP); !it.atEnd(); ++it)
357  functor(it.item());
358  for (exint i = 0; i < attribsToPromote.size(); ++i)
359  {
360  GA_ElementGroup *cur_group = gdp->findVertexGroup(attribsToPromote[i]);
361  if (cur_group && !cur_group->isInternal())
363  }
364  attribsToPromote.clear();
365 
366  for (auto it = gdp->primitiveAttribs().obegin(GA_SCOPE_GROUP); !it.atEnd(); ++it)
367  functor(it.item());
368  for (exint i = 0; i < attribsToPromote.size(); ++i)
369  {
370  GA_ElementGroup *cur_group = gdp->findPrimitiveGroup(attribsToPromote[i]);
371  if (cur_group && !cur_group->isInternal())
373  }
374  attribsToPromote.clear();
375  }
376  else
377  {
378  GA_Attribute *attrib = gdp->vertexAttribs().find(GA_SCOPE_PUBLIC, sopparms.getAttribName());
379  if (!attrib)
380  {
381  attrib = gdp->primitiveAttribs().find(GA_SCOPE_PUBLIC, sopparms.getAttribName());
382  if (!attrib)
383  {
384  // The attribute should be able to be a group, so that we
385  // can easily split along group boundaries.
386  attrib = gdp->vertexGroups().find(sopparms.getAttribName());
387  if (!attrib)
388  {
389  attrib = gdp->primitiveGroups().find(sopparms.getAttribName());
390  if (!attrib)
391  {
392  cookparms.sopAddWarning(SOP_MESSAGE, "Couldn't find specified vertex or primitive attribute");
393  return;
394  }
395  }
396  }
397  }
398 
399  // NOTE: This will bump any data IDs as needed, if any points are split.
400  GEOsplitPointsByAttrib(gdp, group, attrib, tolerance);
401 
402  if (promote)
403  {
405  }
406  }
407  }
408  else if (!sopparms.getUseAttrib())
409  {
410  // NOTE: This will bump any data IDs as needed, if any points are split.
411  GEOsplitPoints(gdp, group);
412  }
413 }
414 
415 } // End of HDK_Sample namespace
Definition of a geometry attribute.
Definition: GA_Attribute.h:198
SOP_Node * getNode() const
Definition: SOP_NodeVerb.h:347
virtual OP_ERROR error()
void setChoiceListPtr(const UT_StringRef &name, PRM_ChoiceList *list)
UT_ErrorSeverity sopAddWarning(int code, const char *msg=0, const UT_SourceLocation *loc=0) const
Definition: SOP_NodeVerb.h:498
static PRM_Template * buildTemplates()
fpreal getTime() const
Definition: OP_Context.h:62
int64 exint
Definition: SYS_Types.h:125
virtual OP_ERROR cookInputGroups(OP_Context &context, int alone)
const GA_Group * parseGroupDetached(const char *pat, GA_GroupType grouptype, const GEO_Detail *pgdp, bool forceexistence, bool defaultprim, bool &success)
UT_ErrorSeverity
Definition: UT_Error.h:25
GA_Size GEOsplitPointsByAttrib(GEO_Detail *detail, const GA_ElementGroup *group, const GA_Attribute *attrib, fpreal tolerance)
static const char *const theDsFile
This is the parameter interface string, below.
static const SOP_NodeVerb::Register< SOP_SplitPointsHDKVerb > theVerb
PRM_ChoiceListType
SYS_FORCE_INLINE TO_T UTverify_cast(FROM_T from)
Definition: UT_Assert.h:229
bool addOperator(OP_Operator *op, std::ostream *err=nullptr)
Standard user attribute level.
Definition: GA_Types.h:148
virtual CookMode cookMode(const SOP_NodeParms *parms) const
static PRM_ChoiceList pointGroupMenu
Definition: SOP_Node.h:1190
exint size() const
Definition: UT_Array.h:646
#define UT_ASSERT_MSG(ZZ,...)
Definition: UT_Assert.h:159
static GA_Attribute * promote(GU_Detail &gdp, GA_Attribute *attrib, GA_AttributeOwner new_owner, bool destroy_existing=true, PROMOTE_METHOD method=GU_PROMOTE_MEAN, const char *new_name=NULL, const GA_Attribute *piece_attrib=nullptr)
const T & parms() const
Definition: SOP_NodeVerb.h:412
Constructs a PRM_Template list from an embedded .ds file or an istream.
PRM_Template * templates() const
bool isElementGroup() const
Definition: GA_Group.h:63
OP_ERROR cookMyselfAsVerb(OP_Context &context)
SOP_NodeFlags mySopFlags
Definition: SOP_Node.h:1625
bool isInternal() const
Definition: GA_Group.h:45
GLuint const GLchar * name
Definition: glcorearb.h:786
int fillAttribNameMenu(PRM_Name *menu_entries, int max_menu_size, GA_AttributeOwner dictionary, int input_index, bool(*approve)(const GA_Attribute *, void *)=NULL, void *approve_data=NULL, bool decode_tokens=false)
GLushort pattern
Definition: glad.h:2583
void newSopOperator(OP_OperatorTable *table)
GU_Detail * gdpNC()
GLenum GLenum GLsizei void * table
Definition: glad.h:5129
void setManagesDataIDs(bool onOff)
Definition: SOP_NodeFlags.h:36
exint append()
Definition: UT_Array.h:142
static bool multiMatchCheck(const char *pattern)
void notifyGroupParmListeners(SOP_Node *oldsop, int groupparm_idx, int grouptype_idx, const GU_Detail *gdp, const GA_Group *group) const
static const UT_StringHolder theSOPTypeName
GLsizeiptr size
Definition: glcorearb.h:664
virtual const SOP_NodeVerb * cookVerb() const
OP_ERROR cookInputAllGroups(OP_Context &context, const GA_Group *&group, bool alone=false, bool do_selection=true, int parm_index=0, int group_type_index=-1, GA_GroupType grouptype=GA_GROUP_INVALID, bool allow_reference=true, bool is_default_prim=true, bool ordered=false, bool detached=true, int input_index=0)
fpreal64 fpreal
Definition: SYS_Types.h:277
static OP_Node * myConstructor(OP_Network *net, const char *name, OP_Operator *entry)
GA_GroupType
An ordinal enum for the different types of groups in GA.
Definition: GA_Types.h:160
This is the SOP class definition.
#define UT_ASSERT(ZZ)
Definition: UT_Assert.h:156
GA_Size GEOsplitPoints(GEO_Detail *detail, const GA_ElementGroup *group)
exint evalInt(int pi, int vi, fpreal t) const
virtual SOP_NodeParms * allocParms() const
void clear()
Resets list to an empty list.
Definition: UT_Array.h:716
virtual OP_ERROR cookMySop(OP_Context &context)
virtual void cook(const CookParms &cookparms) const
Compute the output geometry.
GU_DetailHandle & gdh() const
The initial state of gdh depends on the cookMode()
Definition: SOP_NodeVerb.h:341
SYS_FORCE_INLINE bool isstring() const
Definition: format.h:895
virtual UT_StringHolder name() const