HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
SOP_HDKObject.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 is an example of an implementation of the Object Merge SOP to
27  * demonstrate using DYNAMIC_PATHs, MULTIPARMS, and proper cooking of other
28  * SOPs.
29  */
30 
31 #include "SOP_HDKObject.h"
32 
33 #include <GU/GU_Detail.h>
34 #include <OP/OP_Director.h>
35 #include <OP/OP_Operator.h>
36 #include <OP/OP_OperatorTable.h>
37 #include <PRM/PRM_Include.h>
38 #include <PRM/PRM_SpareData.h>
39 #include <PRM/PRM_Parm.h>
40 #include <UT/UT_DSOVersion.h>
41 #include <UT/UT_DirUtil.h>
42 
43 using namespace HDK_Sample;
44 
45 // Define the new sop operator...
46 void
48 {
49  table->addOperator(new OP_Operator(
50  "proto_objectmerge",
51  "HDK Object Merge",
54  0, 0, 0, OP_FLAG_GENERATOR));
55 }
56 
57 static PRM_Name objnames[] =
58 {
59  PRM_Name("numobj", "Number of Objects"),
60  PRM_Name("objpath#","Object #"),
61  PRM_Name("xformpath","Transform Object"),
62  PRM_Name("enable#", "Enable Merge #"),
63  PRM_Name(0)
64 };
65 
66 static PRM_Template theObjectTemplates[] = {
67  PRM_Template(PRM_TOGGLE, 1, &objnames[3], PRMoneDefaults),
69  &objnames[1], 0, 0,
70  0, 0, &PRM_SpareData::sopPath),
71  PRM_Template()
72 };
73 
76 {
77  PRM_Template(PRM_MULTITYPE_LIST, theObjectTemplates, 2, &objnames[0],
80  0, 0, 0, 0, &PRM_SpareData::objPath),
81  PRM_Template()
82 };
83 
84 OP_Node *
86  OP_Operator *entry)
87 {
88  return new SOP_HDKObject(net, name, entry);
89 }
90 
91 
93  : SOP_Node(net, name, entry)
94 {
95  // NOTE: GEO_Detail::copy(), when GEO_COPY_ONCE is used, will copy all
96  // data IDs from the source, and GEO_Detail::transform() will
97  // bump data IDs of any transformed attributes, as well as the
98  // primitive list data ID if there are any transforming primitives
99  // that were transformed.
101 }
103 
104 bool
106 {
107  bool changed = false;
108 
109  int n = NUMOBJ();
110  for (int i = 1; i <= n; i++)
111  {
112  changed |= enableParmInst(objnames[1].getToken(), &i, ENABLEMERGE(i));
113  }
114 
115  return changed;
116 }
117 
118 int
120 {
121  // don't do anything if we're locked
122  if (flags().getHardLocked())
123  return 1;
124 
125  int numobj = NUMOBJ();
126 
127  // Determine if any of our SOPs are evil.
128  for (int objindex = 1; objindex <= numobj; objindex++)
129  {
130  if (!ENABLEMERGE(objindex)) // Ignore disabled ones.
131  continue;
132 
133  UT_String objname;
134  SOPPATH(objname, objindex, 0.0f);
135 
136  OP_Network *objptr = (OP_Network *)findNode((const char *)objname);
137  if (!objptr)
138  continue;
139 
140  // Self-referential nodes are assumed to have equal DandR ops,
141  // as it doesn't matter as they will be ignored and flagged
142  // as errors during cook anyways.
143  if (objptr == this)
144  {
145  continue;
146  }
147 
148  if (!objptr->getDandROpsEqual())
149  {
150  return 0;
151  }
152  }
153  // None of our interests was unequal, thus we are equal!
154  return 1;
155 }
156 
157 OP_ERROR
159 {
160  fpreal t = context.getTime();
161 
162  // Get our xform object, if any.
163  UT_String objname;
164  XFORMPATH(objname, t);
165  OP_Network *xformobjptr = (OP_Network *)findNode(objname);
166  if (xformobjptr && xformobjptr->getOpTypeID() == SOP_OPTYPE_ID)
167  {
168  // The user pointed to the SOP. We silently promote it to the
169  // containing object. This allows the intuitive "." to be used
170  // for the path to transform relative to our own op (rather than
171  // having to track up an arbitrary number of paths)
172  xformobjptr = xformobjptr->getCreator();
173  }
174 
175  // We must explicitly cast down as OBJ_Node * is unknown here.
176  // We also do the cast here instead of in findNode as we want
177  // to allow people to reference SOPs.
178  xformobjptr = (OP_Network *)CAST_OBJNODE(xformobjptr);
179 
180  if (!xformobjptr && objname.isstring())
181  {
182  // We didn't get an xform object, but they had something typed,
183  // badmerge.
184  addError(SOP_BAD_SOP_MERGED, objname);
185  }
186 
187  int numobj = NUMOBJ();
188 
189  // Peek as to whether we are a render cook or not.
190  int cookrender = getCreator()->isCookingRender();
191 
192  bool copiedfirst = false;
193  bool copiedlast = false;
194 
195  for (int objindex = 1; objindex <= numobj; objindex++)
196  {
197  if (!ENABLEMERGE(objindex)) // Ignore disabled ones.
198  continue;
199 
200  UT_String sopname;
201  SOPPATH(sopname, objindex, t);
202 
203  if (!sopname.isstring())
204  continue; // Blank means ignore.
205 
206  SOP_Node *sopptr = getSOPNode(sopname, 1); // We want extra inputs.
207 
208  if (sopptr == this)
209  {
210  // Self-reference. Special brand of evil.
212  continue;
213  }
214 
215  if (!sopptr)
216  {
217  // Illegal merge. Just warn so we don't abort everything.
218  addWarning(SOP_BAD_SOP_MERGED, sopname);
219  continue;
220  }
221 
222  // Get the creator, which is our objptr.
223  OP_Network *objptr = sopptr->getCreator();
224 
225  // Change over so any subnet evaluation will properly track...
226  int savecookrender = objptr->isCookingRender();
227  objptr->setCookingRender(cookrender);
228 
229  // Actually cook...
230  const GU_Detail *cookedgdp = sopptr->getCookedGeo(context);
231 
232  // Restore the cooking render state.
233  objptr->setCookingRender(savecookrender);
234 
235  if (!cookedgdp)
236  {
237  // Something went wrong with the cooking. Warn the hapless user.
238  addWarning(SOP_BAD_SOP_MERGED, sopname);
239  continue;
240  }
241 
242  // Now add the extra inputs...
244  // The sop extra inputs were set by the getSOPNode
245 
246  bool firstmerge = !copiedfirst;
247 
248  // Choose the best copy method we can
249  GEO_CopyMethod copymethod = GEO_COPY_ADD;
250  if (!copiedfirst)
251  {
252  copymethod = GEO_COPY_START;
253  copiedfirst = true;
254  if (objindex == numobj)
255  {
256  copymethod = GEO_COPY_ONCE;
257  copiedlast = true;
258  }
259  }
260  else if (objindex == numobj)
261  {
262  copymethod = GEO_COPY_END;
263  copiedlast = true;
264  }
265 
266  // Mark where the new prims and points start
267  GA_IndexMap::Marker pointmarker(gdp->getPointMap());
268  GA_IndexMap::Marker primmarker(gdp->getPrimitiveMap());
269 
270  // Don't copy internal groups!
271  // Accumulation of internal groups may ensue.
272  gdp->copy(*cookedgdp, copymethod, true, false, GA_DATA_ID_CLONE);
273 
274  // Apply the transform.
275  if (xformobjptr)
276  {
277  // GEO_Detail::transform supports double-precision,
278  // so we might as well use double-precision transforms.
279  UT_Matrix4D xform, xform2;
280 
281  if (!objptr->getWorldTransform(xform, context))
282  addTransformError(*objptr, "world");
283  if (!xformobjptr->getIWorldTransform(xform2, context))
284  addTransformError(*xformobjptr, "inverse world");
285  xform *= xform2;
286 
287  if (firstmerge)
288  {
289  // The first object we merge we can just do a full gdp transform
290  // rather than building the subgroup.
291  gdp->transform(xform);
292  }
293  else
294  {
295  gdp->transform(xform, primmarker.getRange(), pointmarker.getRange(), false);
296  }
297  }
298  }
299 
300  if (xformobjptr)
301  {
302  addExtraInput(xformobjptr, OP_INTEREST_DATA);
303  }
304 
305  // Finish & clean up the copy procedure, if not already done.
306  if (!copiedfirst)
307  {
308  gdp->clearAndDestroy();
309  }
310  else if (!copiedlast)
311  {
312  GU_Detail blank_gdp;
313  gdp->copy(blank_gdp, GEO_COPY_END, true, false, GA_DATA_ID_CLONE);
314  }
315 
316  // Set the node selection for this primitive. This will highlight all
317  // the primitives of the node, but only if the highlight flag for this node
318  // is on and the node is selected.
319  if (error() < UT_ERROR_ABORT)
321 
322  return error();
323 }
const GU_Detail * getCookedGeo(OP_Context &, int forced=0)
virtual OP_ERROR error()
virtual bool getWorldTransform(UT_Matrix4D &xform, OP_Context &)
const GA_IndexMap & getPrimitiveMap() const
Definition: GA_Detail.h:743
PRM_API const PRM_Type PRM_STRING
const OP_NodeFlags & flags() const
Definition: OP_Node.h:1388
void XFORMPATH(UT_String &str, fpreal t)
Definition: SOP_HDKObject.h:80
OP_Node * findNode(const char *path, OTLSyncMode syncmode=OTLSYNC_DOSYNC) const
Uses the path (eg. "/obj/geo1") to find a node in our hierarchy.
fpreal getTime() const
Definition: OP_Context.h:62
void addTransformError(const OP_Node &node, const char *label=0) const
void SOPPATH(UT_String &str, int i, fpreal t)
Definition: SOP_HDKObject.h:70
void clearAndDestroy()
Clear all the points/primitives out of this detail.
Definition: GEO_Detail.h:267
#define OP_FLAG_GENERATOR
Definition: OP_Operator.h:82
UT_ErrorSeverity
Definition: UT_Error.h:25
void transform(const UT_Matrix4T< FLOAT_T > &mat, const GA_Range &primitives, bool just_P, bool keep_vector_lengths=true, bool check_pasting=true, bool neg_determinant_flip_vector=true, bool update_ptnormals=false, GEO_Delta *geodelta=nullptr, bool updateaffectednormals=false, const char *attribpattern=nullptr)
bool addOperator(OP_Operator *op, std::ostream *err=nullptr)
int getDandROpsEqual() override
OP_Network * getCreator() const
void addError(int code, const char *msg=0)
Definition: SOP_Node.h:1158
GLdouble n
Definition: glcorearb.h:2008
GLfloat f
Definition: glcorearb.h:1926
void newSopOperator(OP_OperatorTable *table)
Definition: SOP_HDKObject.C:47
const GA_IndexMap & getPointMap() const
Definition: GA_Detail.h:741
OP_ERROR cookMySop(OP_Context &context) override
static PRM_SpareData sopPath
void addWarning(SOP_ErrorCodes code, const char *msg=0)
Definition: SOP_Node.h:1164
bool enableParmInst(const char *name, const int *inst, int state, int nestlevel=1, int v=-1)
virtual bool getIWorldTransform(UT_Matrix4D &xform, OP_Context &)
SOP_NodeFlags mySopFlags
Definition: SOP_Node.h:1625
GLuint const GLchar * name
Definition: glcorearb.h:786
static PRM_Template myTemplateList[]
Definition: SOP_HDKObject.h:47
SOP_Node * getSOPNode(const char *path, int addextra=0, bool *got_by_flag=nullptr) const
bool copy(const GEO_Detail &src, GEO_CopyMethod method=GEO_COPY_ONCE, bool this_parameter_is_ignored=true, bool keep_internal_groups=true, GA_DataIdStrategy data_id_strategy=GA_DATA_ID_BUMP)
bool updateParmsFlags() override
GLenum GLenum GLsizei void * table
Definition: glad.h:5129
void setManagesDataIDs(bool onOff)
Definition: SOP_NodeFlags.h:36
virtual OP_OpTypeId getOpTypeID() const
Definition: OP_Node.h:525
GLdouble t
Definition: glad.h:2397
GU_Detail * gdp
Definition: SOP_Node.h:1622
PRM_API PRM_Default PRMoneDefaults[]
static OP_Node * myConstructor(OP_Network *net, const char *name, OP_Operator *entry)
Definition: SOP_HDKObject.C:85
virtual void addExtraInput(OP_Node *op, OP_InterestType type)
fpreal64 fpreal
Definition: SYS_Types.h:277
int getDandROpsEqual() override
PRM_API const PRM_Type PRM_TOGGLE
bool isstring() const
Definition: UT_String.h:691
void select(GU_SelectionType stype)
virtual int isCookingRender() const
Definition: OP_Node.h:2474
SOP_HDKObject(OP_Network *net, const char *name, OP_Operator *entry)
Definition: SOP_HDKObject.C:92
GEO_CopyMethod
An enum for GEO_Detail::copy's method parameter.
Definition: GEO_Detail.h:240
static PRM_SpareData objPath
virtual void setCookingRender(int val)