HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
SOP_Flatten.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  * The Flatten SOP. This SOP Flattens the geometry onto a plane.
27  */
28 
29 #include "SOP_Flatten.h"
30 
31 #include <SOP/SOP_Guide.h>
32 #include <GU/GU_Detail.h>
33 #include <GA/GA_Iterator.h>
34 #include <OP/OP_AutoLockInputs.h>
35 #include <OP/OP_Operator.h>
36 #include <OP/OP_OperatorTable.h>
37 #include <PRM/PRM_Include.h>
38 #include <UT/UT_DSOVersion.h>
39 #include <UT/UT_Interrupt.h>
40 #include <UT/UT_Matrix3.h>
41 #include <UT/UT_Matrix4.h>
42 #include <UT/UT_Vector3.h>
43 #include <SYS/SYS_Math.h>
44 #include <stddef.h>
45 
46 using namespace HDK_Sample;
47 
48 void
50 {
51  table->addOperator(new OP_Operator(
52  "hdk_flatten",
53  "Flatten",
56  1,
57  1,
58  NULL));
59 }
60 
61 static PRM_Name names[] = {
62  PRM_Name("usedir", "Use Direction Vector"),
63  PRM_Name("dist", "Distance"),
64 };
65 
71  PRM_Template(PRM_FLT_J, 1, &names[1], PRMzeroDefaults, 0,
72  &PRMscaleRange),
73  PRM_Template(PRM_TOGGLE, 1, &names[0]),
76  PRM_Template(),
77 };
78 
79 
80 OP_Node *
82 {
83  return new SOP_Flatten(net, name, op);
84 }
85 
87  : SOP_Node(net, name, op), myGroup(NULL)
88 {
89  // This indicates that this SOP manually manages its data IDs,
90  // so that Houdini can identify what attributes may have changed,
91  // e.g. to reduce work for the viewport, or other SOPs that
92  // check whether data IDs have changed.
93  // By default, (i.e. if this line weren't here), all data IDs
94  // would be bumped after the SOP cook, to indicate that
95  // everything might have changed.
96  // If some data IDs don't get bumped properly, the viewport
97  // may not update, or SOPs that check data IDs
98  // may not cook correctly, so be *very* careful!
100 
101  // Make sure to flag that we can supply a guide geometry
103 }
104 
106 
107 bool
109 {
110  bool changed;
111 
112  changed = enableParm(3, !DIRPOP());
113  changed |= enableParm(4, DIRPOP());
114 
115  return changed;
116 }
117 
118 
119 OP_ERROR
121 {
122  // The SOP_Node::cookInputPointGroups() provides a good default
123  // implementation for just handling a point selection.
124  return cookInputPointGroups(
125  context, // This is needed for cooking the group parameter, and cooking the input if alone.
126  myGroup, // The group (or NULL) is written to myGroup if not alone.
127  alone, // This is true iff called outside of cookMySop to update handles.
128  // true means the group will be for the input geometry.
129  // false means the group will be for gdp (the working/output geometry).
130  true, // (default) true means to set the selection to the group if not alone and the highlight flag is on.
131  0, // (default) Parameter index of the group field
132  -1, // (default) Parameter index of the group type field (-1 since there isn't one)
133  true, // (default) true means that a pointer to an existing group is okay; false means group is always new.
134  false, // (default) false means new groups should be unordered; true means new groups should be ordered.
135  true, // (default) true means that all new groups should be detached, so not owned by the detail;
136  // false means that new point and primitive groups on gdp will be owned by gdp.
137  0 // (default) Index of the input whose geometry the group will be made for if alone.
138  );
139 }
140 
141 
142 OP_ERROR
144 {
145  // We must lock our inputs before we try to access their geometry.
146  // OP_AutoLockInputs will automatically unlock our inputs when we return.
147  // NOTE: Don't call unlockInputs yourself when using this!
148  OP_AutoLockInputs inputs(this);
149  if (inputs.lock(context) >= UT_ERROR_ABORT)
150  return error();
151 
152  fpreal now = context.getTime();
153 
154  duplicateSource(0, context);
155 
156  // These three lines enable the local variable support. This allows
157  // $CR to get the red colour, for example, as well as supporting
158  // any varmap created by the Attribute Create SOP.
159  // Note that if you override evalVariableValue for your own
160  // local variables (like SOP_Star does) it is essential you
161  // still call the SOP_Node::evalVariableValue or you'll not
162  // get any of the benefit of the built in local variables.
163 
164  // The variable order controls precedence for which attribute will be
165  // be bound first if the same named variable shows up in multiple
166  // places. This ordering ensures point attributes get precedence.
167  setVariableOrder(3, 2, 0, 1);
168 
169  // The setCur* functions track which part of the gdp is currently
170  // being processed - it is what is used in the evalVariableValue
171  // callback as the current point. The 0 is for the first input,
172  // you can have two inputs so $CR2 would get the second input's
173  // value.
175 
176  // Builds the lookup table matching attributes to the local variables.
177  setupLocalVars();
178 
179  // Here we determine which groups we have to work on. We only
180  // handle point groups.
181  if (error() < UT_ERROR_ABORT && cookInputGroups(context) < UT_ERROR_ABORT &&
182  (!myGroup || !myGroup->isEmpty()))
183  {
184  UT_AutoInterrupt progress("Flattening Points");
185 
186  // Handle all position, normal, and vector attributes.
187  // It's not entirely clear what to do for quaternion or transform attributes.
188  // We bump the data IDs of the attributes to modify in advance,
189  // since we're already looping over them, and we want to avoid
190  // bumping them all for each point, in case that's slow.
191  UT_Array<GA_RWHandleV3> positionattribs(1);
192  UT_Array<GA_RWHandleV3> normalattribs;
193  UT_Array<GA_RWHandleV3> vectorattribs;
194  GA_Attribute *attrib;
196  {
197  // Skip non-transforming attributes
198  if (!attrib->needsTransform())
199  continue;
200 
201  GA_TypeInfo typeinfo = attrib->getTypeInfo();
202  if (typeinfo == GA_TYPE_POINT || typeinfo == GA_TYPE_HPOINT)
203  {
204  GA_RWHandleV3 handle(attrib);
205  if (handle.isValid())
206  {
207  positionattribs.append(handle);
208  attrib->bumpDataId();
209  }
210  }
211  else if (typeinfo == GA_TYPE_NORMAL)
212  {
213  GA_RWHandleV3 handle(attrib);
214  if (handle.isValid())
215  {
216  normalattribs.append(handle);
217  attrib->bumpDataId();
218  }
219  }
220  else if (typeinfo == GA_TYPE_VECTOR)
221  {
222  GA_RWHandleV3 handle(attrib);
223  if (handle.isValid())
224  {
225  vectorattribs.append(handle);
226  attrib->bumpDataId();
227  }
228  }
229  }
230 
231  // Iterate over points up to GA_PAGE_SIZE at a time using blockAdvance.
233  GA_Offset end;
234  for (GA_Iterator it(gdp->getPointRange(myGroup)); it.blockAdvance(start, end);)
235  {
236  // Check if user requested abort
237  if (progress.wasInterrupted())
238  break;
239 
240  for (GA_Offset ptoff = start; ptoff < end; ++ptoff)
241  {
242  // This sets the current point that is beint processed to
243  // ptoff. This means that ptoff will be used for any
244  // local variable for any parameter evaluation that occurs
245  // after this point.
246  // NOTE: Local variables and repeated parameter evaluation
247  // is significantly slower and sometimes more complicated
248  // than having a string parameter that specifies the name
249  // of an attribute whose values should be used instead.
250  // That parameter would only need to be evaluated once,
251  // the attribute could be looked up once, and quickly
252  // accessed; however, a separate point attribute would
253  // be needed for each property that varies per point.
254  // Local variable evaluation isn't threadsafe either,
255  // whereas attributes can be read safely from multiple
256  // threads.
257  //
258  // Long story short: *Local variables are terrible.*
259  myCurPtOff[0] = ptoff;
260  float dist = DIST(now);
261  UT_Vector3 normal;
262  if (!DIRPOP())
263  {
264  switch (ORIENT())
265  {
266  case 0 : // XY Plane
267  normal.assign(0, 0, 1);
268  break;
269  case 1 : // YZ Plane
270  normal.assign(1, 0, 0);
271  break;
272  case 2 : // XZ Plane
273  normal.assign(0, 1, 0);
274  break;
275  }
276  }
277  else
278  {
279  normal.assign(NX(now), NY(now), NZ(now));
280  normal.normalize();
281  }
282 
283  // Project positions onto the plane by subtracting
284  // off the normal component.
285  for (exint i = 0; i < positionattribs.size(); ++i)
286  {
287  UT_Vector3 p = positionattribs(i).get(ptoff);
288  p -= normal * (dot(normal, p) - dist);
289  positionattribs(i).set(ptoff, p);
290  }
291 
292  // Normals will now all either be normal or -normal.
293  for (exint i = 0; i < normalattribs.size(); ++i)
294  {
295  UT_Vector3 n = normalattribs(i).get(ptoff);
296  if (dot(normal, n) < 0)
297  n = -normal;
298  else
299  n = normal;
300  normalattribs(i).set(ptoff, n);
301  }
302 
303  // Project vectors onto the plane through the origin by
304  // subtracting off the normal component.
305  for (exint i = 0; i < vectorattribs.size(); ++i)
306  {
307  UT_Vector3 v = vectorattribs(i).get(ptoff);
308  v -= normal * dot(normal, v);
309  vectorattribs(i).set(ptoff, v);
310  }
311  }
312  }
313  }
314 
315  // Clears out all the myCur* variables to ensure we have no
316  // stray references. This ensures that if the parameters are
317  // evaluated outside of this cook path they don't try to read
318  // possibly stale point pointers.
320 
321  return error();
322 }
323 
324 OP_ERROR
326 {
327  const int divs = 5;
328 
329  OP_AutoLockInputs inputs(this);
330  if (inputs.lock(context) >= UT_ERROR_ABORT)
331  return error();
332 
333  float now = context.getTime();
334 
335  myGuide1->clearAndDestroy();
336 
337  float dist = DIST(now);
338 
339  float nx = 0;
340  float ny = 0;
341  float nz = 1;
342  if (!DIRPOP())
343  {
344  switch (ORIENT())
345  {
346  case 0 : // XY Plane
347  nx = 0; ny = 0; nz = 1;
348  break;
349  case 1 : // YZ Plane
350  nx = 1; ny = 0; nz = 0;
351  break;
352  case 2 : // XZ Plane
353  nx = 0; ny = 1; nz = 0;
354  break;
355  }
356  }
357  else
358  {
359  nx = NX(now); ny = NY(now); nz = NZ(now);
360  }
361 
362  if (error() >= UT_ERROR_ABORT)
363  return error();
364 
365  UT_Vector3 normal(nx, ny, nz);
366  normal.normalize();
367 
368  UT_BoundingBox bbox;
369  inputGeo(0, context)->getBBox(&bbox);
370 
371  float sx = bbox.sizeX();
372  float sy = bbox.sizeY();
373  float sz = bbox.sizeZ();
374  float size = SYSsqrt(sx*sx + sy*sy + sz*sz);
375 
376  float cx = normal.x() * dist;
377  float cy = normal.y() * dist;
378  float cz = normal.z() * dist;
379 
380  myGuide1->meshGrid(divs, divs, size, size);
381 
382  UT_Vector3 zaxis(0, 0, 1);
383  UT_Matrix3 mat3;
384  mat3.dihedral(zaxis, normal);
385  UT_Matrix4 xform;
386  xform = mat3;
387  xform.translate(cx, cy, cz);
388 
389  myGuide1->transform(xform);
390 
391  return error();
392 }
393 
394 const char *
395 SOP_Flatten::inputLabel(unsigned) const
396 {
397  return "Geometry to Flatten";
398 }
SYS_FORCE_INLINE void bumpDataId()
Definition: GA_Attribute.h:306
GA_API const UT_StringHolder dist
Definition of a geometry attribute.
Definition: GA_Attribute.h:198
virtual OP_ERROR error()
void setNeedGuide1(bool onOff)
Definition: SOP_NodeFlags.h:32
Iteration over a range of elements.
Definition: GA_Iterator.h:29
PRM_API const PRM_Type PRM_STRING
bool getBBox(UT_BoundingBox *bbox, const GA_PrimitiveGroup *g=nullptr) const
OP_ERROR lock(OP_Context &context)
Locks all inputs.
const GLdouble * v
Definition: glcorearb.h:837
fpreal getTime() const
Definition: OP_Context.h:62
bool blockAdvance(GA_Offset &start, GA_Offset &end)
GLuint start
Definition: glcorearb.h:475
static PRM_SpareData * getGroupSelectButton(GA_GroupType group_type, const char *group_type_parm=NULL, int input_index=0, PRM_SpareData *merge_spare_data=NULL, const char *assoc_groups=NULL, GroupSelectAsOrdered ordered=GroupSelectAsOrdered::AUTO, const char *use_name_attr=nullptr, const char *select_script=nullptr)
SOP_Flatten(OP_Network *net, const char *name, OP_Operator *op)
Definition: SOP_Flatten.C:86
PRM_API PRM_Name PRMorientName
constexpr SYS_FORCE_INLINE T & z() noexcept
Definition: UT_Vector3.h:667
int64 exint
Definition: SYS_Types.h:125
PRM_API const PRM_Type PRM_ORD
UT_ErrorSeverity
Definition: UT_Error.h:25
bool updateParmsFlags() override
Definition: SOP_Flatten.C:108
bool addOperator(OP_Operator *op, std::ostream *err=nullptr)
GU_DetailHandle myGdpHandle
Definition: SOP_Node.h:1617
OP_ERROR cookInputGroups(OP_Context &context, int alone=0) override
Definition: SOP_Flatten.C:120
SOP_Guide * myGuide1
Definition: SOP_Node.h:1623
OP_ERROR cookMyGuide1(OP_Context &context) override
Definition: SOP_Flatten.C:325
static PRM_ChoiceList pointGroupMenu
Definition: SOP_Node.h:1190
void newSopOperator(OP_OperatorTable *table)
Definition: SOP_Flatten.C:49
void resetLocalVarRefs()
exint size() const
Definition: UT_Array.h:646
T sizeY() const
bool setupLocalVars()
GA_Size GA_Offset
Definition: GA_Types.h:641
GLdouble n
Definition: glcorearb.h:2008
GA_Range getPointRange(const GA_PointGroup *group=0) const
Get a range of all points in the detail.
Definition: GA_Detail.h:1730
PRM_API const PRM_Type PRM_DIRECTION
static PRM_Template myTemplateList[]
Definition: SOP_Flatten.h:48
const GU_Detail * inputGeo(int index, OP_Context &)
Definition: SOP_Node.h:1147
#define GA_FOR_ALL_POINT_ATTRIBUTES(gdp, A)
Definition: GA_GBMacros.h:282
bool enableParm(int pi, int state, int v=-1)
static UT_Matrix3T< T > dihedral(UT_Vector3T< S > &a, UT_Vector3T< S > &b, UT_Vector3T< S > &c, int norm=1)
OP_ERROR cookMySop(OP_Context &context) override
Method to cook geometry for the SOP.
Definition: SOP_Flatten.C:143
fpreal64 dot(const CE_VectorT< T > &a, const CE_VectorT< T > &b)
Definition: CE_Vector.h:130
GLuint GLuint end
Definition: glcorearb.h:475
bool needsTransform(bool include_P=true) const
Definition: GA_Attribute.h:437
SOP_NodeFlags mySopFlags
Definition: SOP_Node.h:1625
OP_ERROR cookInputPointGroups(OP_Context &context, const GA_PointGroup *&group, bool alone=false, bool do_selection=true, int parm_index=0, int group_type_index=-1, bool allow_reference=true, bool ordered=false, bool detached=true, int input_index=0)
See cookInputPrimitiveGroups.
PRM_API PRM_Default PRMzaxisDefaults[]
PRM_API PRM_Range PRMscaleRange
GLuint const GLchar * name
Definition: glcorearb.h:786
PRM_API PRM_Name PRMgroupName
void setVariableOrder(int detail, int prim, int pt, int vtx)
PRM_API const PRM_Type PRM_FLT_J
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
GA_TypeInfo
Definition: GA_Types.h:100
IMATH_HOSTDEVICE constexpr int divs(int x, int y) IMATH_NOEXCEPT
Definition: ImathFun.h:140
GU_Detail * gdp
Definition: SOP_Node.h:1622
SYS_FORCE_INLINE bool isValid() const
Definition: GA_Handle.h:187
void assign(T xx=0.0f, T yy=0.0f, T zz=0.0f)
Set the values of the vector components.
Definition: UT_Vector3.h:694
GLsizeiptr size
Definition: glcorearb.h:664
void setCurGdh(int index, const GU_DetailHandle &gdh)
void translate(T dx, T dy, T dz=0)
Definition: UT_Matrix4.h:773
SYS_FORCE_INLINE GA_TypeInfo getTypeInfo() const
Definition: GA_Attribute.h:252
fpreal64 fpreal
Definition: SYS_Types.h:277
T sizeZ() const
Data represents a normal vector. Token "normal".
Definition: GA_Types.h:113
static OP_Node * myConstructor(OP_Network *, const char *, OP_Operator *)
Definition: SOP_Flatten.C:81
GLbyte ny
Definition: glad.h:2247
GLbyte GLbyte nz
Definition: glad.h:2247
PRM_API const PRM_Type PRM_TOGGLE
Data represents a direction vector. Token "vector".
Definition: GA_Types.h:111
Data represents a position in space. Token "point".
Definition: GA_Types.h:105
SYS_FORCE_INLINE UT_StorageMathFloat_t< T > normalize() noexcept
Definition: UT_Vector3.h:376
SYS_FORCE_INLINE bool isEmpty() const
Query whether the group is empty of primary elements.
PRM_API PRM_Name PRMdirectionName
constexpr SYS_FORCE_INLINE T & y() noexcept
Definition: UT_Vector3.h:665
PRM_API PRM_Default PRMzeroDefaults[]
const char * inputLabel(unsigned idx) const override
Definition: SOP_Flatten.C:395
PRM_API PRM_ChoiceList PRMplaneMenu
T sizeX() const
GA_Offset myCurPtOff[2]
Definition: SOP_Node.h:1807
constexpr SYS_FORCE_INLINE T & x() noexcept
Definition: UT_Vector3.h:663
OP_ERROR duplicateSource(unsigned index, OP_Context &context, GU_Detail *gdp, bool clean=true)