HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
SOP_MinRay.C
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2025
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  * The HDK Minimum Ray SOP
27  */
28 
29 #include "SOP_MinRay.h"
30 
31 // This is an automatically generated header file based on theDsFile, below,
32 // to provide SOP_MinRayParms, an easy way to access parameter values from
33 // SOP_MinRayVerb::cook with the correct type.
34 #include "SOP_MinRay.proto.h"
35 
36 #include <GA/GA_ElementGroup.h>
37 #include <GA/GA_SplittableRange.h>
38 #include <GU/GU_Detail.h>
39 #include <GU/GU_RayIntersect.h>
40 #include <GOP/GOP_Manager.h>
41 #include <OP/OP_Operator.h>
42 #include <OP/OP_OperatorTable.h>
43 #include <PRM/PRM_Include.h>
45 #include <UT/UT_DSOVersion.h>
46 #include <UT/UT_StringHolder.h>
47 
48 
49 using namespace UT::Literal;
50 using namespace HDK_Sample;
51 
52 constexpr static int thePointsInput = 0;
53 constexpr static int theSurfaceInput = 1;
54 
55 //
56 // Help is stored in a "wiki" style text file. This text file should be copied
57 // to $HOUDINI_PATH/help/nodes/sop/hdk_expand.txt
58 //
59 // See the sample_install.sh file for an example.
60 //
61 
62 /// This is the internal name of the SOP type.
63 /// It isn't allowed to be the same as any other SOP's type name.
64 const UT_StringHolder SOP_MinRay::theSOPTypeName("hdk_minray"_sh);
65 
66 /// newSopOperator is the hook that Houdini grabs from this dll
67 /// and invokes to register the SOP. In this case, we add ourselves
68 /// to the specified operator table.
69 void
71 {
72  table->addOperator(new OP_Operator(
73  SOP_MinRay::theSOPTypeName, // Internal name
74  "HDK Minimum Ray", // UI name
75  SOP_MinRay::myConstructor, // How to build the SOP
76  SOP_MinRay::buildTemplates(), // My parameters
77  2, // Min # of sources
78  2, // Max # of sources
79  nullptr, // Custom local variables (none)
80  0, // No flags, not a generator, no merge input, not an output node.
81  0, // Not supplying custom input labels here.
82  1, // One output
83  "HDK Examples")); // Make the SOP appear in the HDK Examples tab submenu
84 }
85 
86 /// This is a multi-line raw string specifying the parameter interface
87 /// for this SOP.
88 
89 // For our purpose we will customize the string input parameter to
90 // more easily input the group.
91 // In SOP_MinRay::buildTemplates we bind a drop down menu to the group field.
92 // Next, to get a selector button we use the 'script_action' parmtag to
93 // specify the script to execute when the button is pressed, the
94 // 'script_action_help' parmtag to create the hover tooltip, and the
95 // 'script_action_icon' parmtag to set the icon.
96 // All group selector buttons use the `soputils.selectGroupParm(kwargs)`
97 // function, and can use the kwargs['geometrytype'] to set the allowed
98 // geometry types, and the kwargs['indexindex'] to set the input
99 // to select on.
100 static const char *theDsFile = R"THEDSFILE(
101 {
102  name parameters
103  parm {
104  name "group"
105  label "Group"
106  type string
107  default { "" }
108  parmtag { "script_action" "import soputils\nkwargs['geometrytype'] = (hou.geometryType.Points,)\nkwargs['inputindex'] = 0\nsoputils.selectGroupParm(kwargs)" }
109  parmtag { "script_action_help" "Select geometry from an available viewport.\nShift-click to turn on Select Groups." }
110  parmtag { "script_action_icon" "BUTTONS_reselect" }
111  }
112 }
113 )THEDSFILE";
114 
116 SOP_MinRay::buildTemplates()
117 {
118  static PRM_TemplateBuilder templ("SOP_MinRay.C"_sh, theDsFile);
119 
120  if (templ.justBuilt())
121  {
122  // Add drop down menu to the attribute string field.
123  // The menu type is a PRM_ChoiceList and SOP_Node provides a
124  // number of nice pre-built menus that you can use.
125 
126  // In this case we want to list all the primitive groups on the
127  // second input. We can use SOP_Node::groupMenu to generate the
128  // menu for us which will list all point
129  // and primitive groups.
131  }
132 
133  return templ.templates();
134 }
135 
136 // Set custom input labels for the source and destination inputs.
137 const char *
138 SOP_MinRay::inputLabel(OP_InputIdx idx) const
139 {
140  switch (idx)
141  {
142  case thePointsInput:
143  return "Ray Points";
144  case theSurfaceInput:
145  return "Collision Geometry";
146  default:
147  return "Invalid Source";
148  }
149 }
150 
151 // Build the cache. We use this to store structures computed
152 // for the surface geometry that let us very quickly find the
153 // hits. This means that as long as the surface geometry
154 // stays the same, we can keep using these acceleration
155 // structures so that successive cooks are very fast.
157 {
158 public:
159  SOP_MinRayCache() : myRayIntersect{nullptr} {}
160 
161  ~SOP_MinRayCache() override {}
162 
164 };
165 
166 // Define the verb for this SOP Node.
168 {
169 public:
171 
172  ~SOP_MinRayVerb() override {}
173 
174  SOP_NodeParms *allocParms() const override { return new SOP_MinRayParms(); }
175 
176  // This is needed when we have a node cache.
177  SOP_NodeCache *allocCache() const override { return new SOP_MinRayCache(); }
178 
179  UT_StringHolder name() const { return SOP_MinRay::theSOPTypeName; }
180 
181  // There are a few choices for the cookMode.
182  // For SOPs with no inputs that only generate geometry use COOK_GENERATOR
183  // For SOPs that modify the input, COOK_DUPLICATE or COOK_INPLACE is usually
184  // the best option, with COOK_DUPLICATE creating a full copy of the input detail
185  // and COOK_INPLACE working on the input detail directly.
186  // The final option COOK_GENERIC allows any number of inputs, but the detail you
187  // get in ::cook will be empty by default. This mode is useful for SOPs like Sweep
188  // that combine two separate geometries in an interesting way.
189  CookMode cookMode(const SOP_NodeParms *parms) const { return COOK_INPLACE; }
190 
191  void cook(const CookParms &cookparms) const;
192 
193  /// This static data member automatically registers
194  /// this verb class at library load time.
196 };
197 
198 // The static member variable definition has to be outside the class definition.
199 // The declaration is inside the class.
201 
202 // The ::cookVerb member on the node must return an instance of the class
203 // we just defined above.
204 const SOP_NodeVerb *
205 SOP_MinRay::cookVerb() const
206 {
207  return SOP_MinRayVerb::theVerb.get();
208 }
209 
210 /// This is the function that does the actual work.
211 void
213 {
214  auto &&sopparms = cookparms.parms<SOP_MinRayParms>();
215 
216  // Get the node cache.
217  SOP_MinRayCache *cache = (SOP_MinRayCache *)cookparms.cache();
218 
219  // The destination detail.
220  GU_Detail *detail = cookparms.gdh().gdpNC();
221 
222  // Get the second input for the source detail.
223  const GU_Detail * collision_detail = cookparms.inputGeo(theSurfaceInput);
224 
225  // Check if the cache data is no longer valid and recreate it
226  // if necessary.
227  // The call to GU_RayIntersect::validForDetail will check if
228  // collision_detail->getUniqueId() and collision_detail->getMetaCacheCount()
229  // have not changed since the GU_RayIntersect instance was created.
230  if (!cache->myRayIntersect || !cache->myRayIntersect->validForDetail(collision_detail))
231  {
232  cache->myRayIntersect = UTmakeUnique<GU_RayIntersect>(collision_detail);
233  }
234 
235  // Get the input group pattern, grouptype and the scale.
236  UT_StringHolder groupname = sopparms.getGroup();
237 
238  // Parse the group.
239  const GA_PointGroup * group = nullptr;
240  GOP_Manager gop;
241  if (groupname.isstring())
242  {
243  // Parse the point group.
244  group = gop.parsePointGroups(groupname, GOP_Manager::GroupCreator(detail));
245 
246  // If we have a group name but no group then it did not parse correctly.
247  if (!group)
248  cookparms.sopAddWarning(SOP_ERR_BADGROUP, groupname);
249  }
250 
251  // NOTE: GA_SplittableRange is guaranteed to give sub-ranges that do not
252  // cross the page boundaries. Therefore if we are only writing to
253  // point attributes, we will not end up crossing any page boundaries
254  // and so we can safely write to the attribute.
255  // If however we wanted to allow two threads to write to the same
256  // page (i.e. iterating over prims but writing to point attributes),
257  // then we will need to harden any of these pages that may get
258  // accessed by separate threads. This can be quite costly so
259  // it is best to avoid if possible, and also best to harden only
260  // a specific range of pages and not all of them.
262  GA_SplittableRange(detail->getPointRange(group)),
263  [&](const GA_Range &r)
264  {
265  // For efficiency we can iterate through the range in blocks
266  // and then use a simple for loop within each block.
268  for (GA_Iterator it(r); it.blockAdvance(start, end);)
269  {
270  for (GA_Offset pt = start; pt < end; ++pt)
271  {
272  // Get the source point position.
273  UT_Vector3 pos = detail->getPos3(pt);
274 
275  // Ray the position onto the collision geometry.
276  // The hit information is then collected in min_info.
277  GU_MinInfo min_info;
278  exint nhit = cache->myRayIntersect->minimumPoint(pos, min_info);
279 
280  // Get the primitive that was hit, if any.
281  GEO_ConstPrimitiveP hit_prim = min_info.prim;
282 
283  // If not hit, skip it and leave the point where it is.
284  if (nhit <= 0)
285  continue;
286 
287  // Get the u,v,w coords of the hit in the interior of the prim
288  float u = min_info.u1;
289  float v = min_info.v1;
290  float w = min_info.w1;
291 
292  // GU_RayIntersect returns real coordinates not unit
293  // coordinates so we need to convert to unit coordinates
294  // which evaluateInteriorPoint expects.
295  cache->myRayIntersect->fixBrokenUVs(hit_prim, u, v);
296 
297  // Evaluate the position at the interior of the prim
298  // given the u,v,w coordinates.
299  UT_Vector4 out_pos4;
300  hit_prim->evaluateInteriorPoint(out_pos4, u, v, w);
301 
302  // Finally set the point position to our hit position.
303  detail->setPos3(pt, UT_Vector3(out_pos4));
304  }
305  }
306  });
307 
308  // We have modified the 'P' attribute so we need to bump its data id.
309  // To bump all data ids you can use detail->bumpAllDataIds();
310  // For our case we only modified a single attribute so we can just bump
311  // that one attribute's data ids.
312  detail->getP()->bumpDataId();
313 }
SYS_FORCE_INLINE void bumpDataId()
Definition: GA_Attribute.h:306
void UTparallelFor(const Range &range, const Body &body, const int subscribe_ratio=2, const int min_grain_size=1, const bool force_use_task_scope=true)
static const SOP_NodeVerb::Register< SOP_MinRayVerb > theVerb
Definition: SOP_MinRay.C:195
void newSopOperator(OP_OperatorTable *table)
Definition: SOP_MinRay.C:70
Iteration over a range of elements.
Definition: GA_Iterator.h:29
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:509
const GLdouble * v
Definition: glcorearb.h:837
bool blockAdvance(GA_Offset &start, GA_Offset &end)
GLuint start
Definition: glcorearb.h:475
bool evaluateInteriorPoint(GA_Offset result_vtx, GA_AttributeRefMap &map, fpreal u, fpreal v, fpreal w=0) const
~SOP_MinRayCache() override
Definition: SOP_MinRay.C:161
UT_Vector3T< float > UT_Vector3
int OP_InputIdx
Definition: OP_DataTypes.h:184
int64 exint
Definition: SYS_Types.h:125
GA_Attribute * getP()
Convenience method to access the P attribute.
Definition: GA_Detail.h:164
GEO_ConstPrimitiveP prim
bool addOperator(OP_Operator *op, std::ostream *err=nullptr)
SYS_FORCE_INLINE UT_Vector3 getPos3(GA_Offset ptoff) const
The ptoff passed is the point offset.
Definition: GA_Detail.h:185
UT_UniquePtr< GU_RayIntersect > myRayIntersect
Definition: SOP_MinRay.C:163
static PRM_ChoiceList pointGroupMenu
Definition: SOP_Node.h:1194
A range of elements in an index-map.
Definition: GA_Range.h:42
std::unique_ptr< T, Deleter > UT_UniquePtr
A smart pointer for unique ownership of dynamically allocated objects.
Definition: UT_UniquePtr.h:39
GA_Size GA_Offset
Definition: GA_Types.h:646
const T & parms() const
Definition: SOP_NodeVerb.h:423
~SOP_MinRayVerb() override
Definition: SOP_MinRay.C:172
GA_Range getPointRange(const GA_PointGroup *group=0) const
Get a range of all points in the detail.
Definition: GA_Detail.h:1749
Constructs a PRM_Template list from an embedded .ds file or an istream.
PRM_Template * templates() const
GLuint GLuint end
Definition: glcorearb.h:475
void cook(const CookParms &cookparms) const
This is the function that does the actual work.
Definition: SOP_MinRay.C:212
UT_StringHolder name() const
Definition: SOP_MinRay.C:179
GU_Detail * gdpNC()
GLenum GLenum GLsizei void * table
Definition: glad.h:5129
GA_API const UT_StringHolder parms
SOP_NodeCache * allocCache() const override
Definition: SOP_MinRay.C:177
SYS_FORCE_INLINE void setPos3(GA_Offset ptoff, const UT_Vector3 &pos)
Set P from a UT_Vector3.
Definition: GA_Detail.h:237
CookMode cookMode(const SOP_NodeParms *parms) const
Definition: SOP_MinRay.C:189
const GU_Detail * inputGeo(exint idx) const
Definition: SOP_NodeVerb.h:387
SOP_NodeCache * cache() const
Definition: SOP_NodeVerb.h:428
GLubyte GLubyte GLubyte GLubyte w
Definition: glcorearb.h:857
GLboolean r
Definition: glcorearb.h:1222
SOP_NodeParms * allocParms() const override
Definition: SOP_MinRay.C:174
const GA_PointGroup * parsePointGroups(const char *pat, const GroupCreator &creator, bool numok=true, bool ordered=false, bool strict=false, GA_Index point_offset=GA_Index(0), ParseInfo *info=0)
GU_DetailHandle & gdh() const
The initial state of gdh depends on the cookMode()
Definition: SOP_NodeVerb.h:341
SYS_FORCE_INLINE bool isstring() const