HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
SOP_Star.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 Star SOP
27  */
28 
29 #include "SOP_Star.h"
30 
31 // This is an automatically generated header file based on theDsFile, below,
32 // to provide SOP_StarParms, an easy way to access parameter values from
33 // SOP_StarVerb::cook with the correct type.
34 #include "SOP_Star.proto.h"
35 
36 #include <GU/GU_Detail.h>
37 #include <OP/OP_Operator.h>
38 #include <OP/OP_OperatorTable.h>
39 #include <PRM/PRM_Include.h>
41 #include <UT/UT_DSOVersion.h>
42 #include <UT/UT_Interrupt.h>
43 #include <UT/UT_StringHolder.h>
44 #include <SYS/SYS_Math.h>
45 #include <limits.h>
46 
47 
48 using namespace UT::Literal;
49 using namespace HDK_Sample;
50 
51 //
52 // Help is stored in a "wiki" style text file. This text file should be copied
53 // to $HOUDINI_PATH/help/nodes/sop/star.txt
54 //
55 // See the sample_install.sh file for an example.
56 //
57 
58 /// This is the internal name of the SOP type.
59 /// It isn't allowed to be the same as any other SOP's type name.
60 const UT_StringHolder SOP_Star::theSOPTypeName("hdk_star"_sh);
61 
62 /// newSopOperator is the hook that Houdini grabs from this dll
63 /// and invokes to register the SOP. In this case, we add ourselves
64 /// to the specified operator table.
65 void
67 {
68  table->addOperator(new OP_Operator(
69  SOP_Star::theSOPTypeName, // Internal name
70  "Star", // UI name
71  SOP_Star::myConstructor, // How to build the SOP
72  SOP_Star::buildTemplates(), // My parameters
73  0, // Min # of sources
74  0, // Max # of sources
75  nullptr, // Custom local variables (none)
76  OP_FLAG_GENERATOR)); // Flag it as generator
77 }
78 
79 /// This is a multi-line raw string specifying the parameter interface
80 /// for this SOP.
81 static const char *theDsFile = R"THEDSFILE(
82 {
83  name parameters
84  parm {
85  name "divs" // Internal parameter name
86  label "Divisions" // Descriptive parameter name for user interface
87  type integer
88  default { "5" } // Default for this parameter on new nodes
89  range { 2! 50 } // The value is prevented from going below 2 at all.
90  // The UI slider goes up to 50, but the value can go higher.
91  export all // This makes the parameter show up in the toolbox
92  // above the viewport when it's in the node's state.
93  }
94  parm {
95  name "rad"
96  label "Radius"
97  type vector2
98  size 2 // 2 components in a vector2
99  default { "1" "0.3" } // Outside and inside radius defaults
100  }
101  parm {
102  name "nradius"
103  label "Allow Negative Radius"
104  type toggle
105  default { "0" }
106  }
107  parm {
108  name "t"
109  label "Center"
110  type vector
111  size 3 // 3 components in a vector
112  default { "0" "0" "0" }
113  }
114  parm {
115  name "orient"
116  label "Orientation"
117  type ordinal
118  default { "0" } // Default to first entry in menu, "xy"
119  menu {
120  "xy" "XY Plane"
121  "yz" "YZ Plane"
122  "zx" "ZX Plane"
123  }
124  }
125 }
126 )THEDSFILE";
127 
129 SOP_Star::buildTemplates()
130 {
131  static PRM_TemplateBuilder templ("SOP_Star.C"_sh, theDsFile);
132  return templ.templates();
133 }
134 
136 {
137 public:
139  virtual ~SOP_StarVerb() {}
140 
141  virtual SOP_NodeParms *allocParms() const { return new SOP_StarParms(); }
142  virtual UT_StringHolder name() const { return SOP_Star::theSOPTypeName; }
143 
144  virtual CookMode cookMode(const SOP_NodeParms *parms) const { return COOK_GENERIC; }
145 
146  virtual void cook(const CookParms &cookparms) const;
147 
148  /// This static data member automatically registers
149  /// this verb class at library load time.
151 };
152 
153 // The static member variable definition has to be outside the class definition.
154 // The declaration is inside the class.
156 
157 const SOP_NodeVerb *
158 SOP_Star::cookVerb() const
159 {
160  return SOP_StarVerb::theVerb.get();
161 }
162 
163 /// This is the function that does the actual work.
164 void
166 {
167  auto &&sopparms = cookparms.parms<SOP_StarParms>();
168  GU_Detail *detail = cookparms.gdh().gdpNC();
169 
170  // We need two points per division
171  exint npoints = sopparms.getDivs()*2;
172 
173  if (npoints < 4)
174  {
175  // With the range restriction we have on the divisions, this
176  // is actually impossible, (except via integer overflow),
177  // but it shows how to add an error message or warning to the SOP.
178  cookparms.sopAddWarning(SOP_MESSAGE, "There must be at least 2 divisions; defaulting to 2.");
179  npoints = 4;
180  }
181 
182  // If this SOP has cooked before and it wasn't evicted from the cache,
183  // its output detail will contain the geometry from the last cook.
184  // If it hasn't cooked, or if it was evicted from the cache,
185  // the output detail will be empty.
186  // This knowledge can save us some effort, e.g. if the number of points on
187  // this cook is the same as on the last cook, we can just move the points,
188  // (i.e. modifying P), which can also save some effort for the viewport.
189 
190  GA_Offset start_ptoff;
191  if (detail->getNumPoints() != npoints)
192  {
193  // Either the SOP hasn't cooked, the detail was evicted from
194  // the cache, or the number of points changed since the last cook.
195 
196  // This destroys everything except the empty P and topology attributes.
197  detail->clearAndDestroy();
198 
199  // Build 1 closed polygon (as opposed to a curve),
200  // namely that has its closed flag set to true,
201  // and the right number of vertices, as a contiguous block
202  // of vertex offsets.
203  GA_Offset start_vtxoff;
204  detail->appendPrimitivesAndVertices(GA_PRIMPOLY, 1, npoints, start_vtxoff, true);
205 
206  // Create the right number of points, as a contiguous block
207  // of point offsets.
208  start_ptoff = detail->appendPointBlock(npoints);
209 
210  // Wire the vertices to the points.
211  for (exint i = 0; i < npoints; ++i)
212  {
213  detail->setVertexPoint(start_vtxoff+i,start_ptoff+i);
214  }
215 
216  // We added points, vertices, and primitives,
217  // so this will bump all topology attribute data IDs,
218  // P's data ID, and the primitive list data ID.
219  detail->bumpDataIdsForAddOrRemove(true, true, true);
220  }
221  else
222  {
223  // Same number of points as last cook, and we know that last time,
224  // we created a contiguous block of point offsets, so just get the
225  // first one.
226  start_ptoff = detail->pointOffset(GA_Index(0));
227 
228  // We'll only be modifying P, so we only need to bump P's data ID.
229  detail->getP()->bumpDataId();
230  }
231 
232  // Everything after this is just to figure out what to write to P and write it.
233 
234  const SOP_StarParms::Orient plane = sopparms.getOrient();
235  const bool allow_negative_radius = sopparms.getNradius();
236 
237  UT_Vector3 center = sopparms.getT();
238 
239  int xcoord, ycoord, zcoord;
240  switch (plane)
241  {
242  case SOP_StarParms::Orient::XY: // XY Plane
243  xcoord = 0;
244  ycoord = 1;
245  zcoord = 2;
246  break;
247  case SOP_StarParms::Orient::YZ: // YZ Plane
248  xcoord = 1;
249  ycoord = 2;
250  zcoord = 0;
251  break;
252  case SOP_StarParms::Orient::ZX: // XZ Plane
253  xcoord = 0;
254  ycoord = 2;
255  zcoord = 1;
256  break;
257  }
258 
259  // Start the interrupt scope
260  UT_AutoInterrupt boss("Building Star");
261  if (boss.wasInterrupted())
262  return;
263 
264  float tinc = M_PI*2 / (float)npoints;
265  float outer_radius = sopparms.getRad().x();
266  float inner_radius = sopparms.getRad().y();
267 
268  // Now, set all the points of the polygon
269  for (exint i = 0; i < npoints; i++)
270  {
271  // Check to see if the user has interrupted us...
272  if (boss.wasInterrupted())
273  break;
274 
275  float angle = (float)i * tinc;
276  bool odd = (i & 1);
277  float rad = odd ? inner_radius : outer_radius;
278  if (!allow_negative_radius && rad < 0)
279  rad = 0;
280 
281  UT_Vector3 pos(SYScos(angle)*rad, SYSsin(angle)*rad, 0);
282  // Put the circle in the correct plane.
283  pos = UT_Vector3(pos(xcoord), pos(ycoord), pos(zcoord));
284  // Move the circle to be centred at the correct position.
285  pos += center;
286 
287  // Since we created a contiguous block of point offsets,
288  // we can just add i to start_ptoff to find this point offset.
289  GA_Offset ptoff = start_ptoff + i;
290  detail->setPos3(ptoff, pos);
291  }
292 }
virtual CookMode cookMode(const SOP_NodeParms *parms) const
Definition: SOP_Star.C:144
void newSopOperator(OP_OperatorTable *table)
Definition: SOP_Star.C:66
UT_ErrorSeverity sopAddWarning(int code, const char *msg=0, const UT_SourceLocation *loc=0) const
Definition: SOP_NodeVerb.h:498
SIM_API const UT_StringHolder angle
#define M_PI
Definition: fmath.h:90
UT_Vector3T< float > UT_Vector3
int64 exint
Definition: SYS_Types.h:125
#define OP_FLAG_GENERATOR
Definition: OP_Operator.h:82
bool addOperator(OP_Operator *op, std::ostream *err=nullptr)
GA_Size GA_Offset
Definition: GA_Types.h:641
virtual UT_StringHolder name() const
Definition: SOP_Star.C:142
const T & parms() const
Definition: SOP_NodeVerb.h:412
Constructs a PRM_Template list from an embedded .ds file or an istream.
virtual void cook(const CookParms &cookparms) const
This is the function that does the actual work.
Definition: SOP_Star.C:165
static const SOP_NodeVerb::Register< SOP_StarVerb > theVerb
Definition: SOP_Star.C:150
PRM_Template * templates() const
GA_Size GA_Index
Define the strictness of GA_Offset/GA_Index.
Definition: GA_Types.h:635
GU_Detail * gdpNC()
GLenum GLenum GLsizei void * table
Definition: glad.h:5129
virtual SOP_NodeParms * allocParms() const
Definition: SOP_Star.C:141
virtual ~SOP_StarVerb()
Definition: SOP_Star.C:139
GU_DetailHandle & gdh() const
The initial state of gdh depends on the cookMode()
Definition: SOP_NodeVerb.h:341