HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
SOP_TimeCompare.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 TimeCompare SOP. This SOP TimeCompares the geometry onto a plane.
27  */
28 
29 #include "SOP_TimeCompare.h"
30 
31 #include <SOP/SOP_Error.h>
32 #include <GU/GU_Detail.h>
33 #include <GA/GA_AIFMath.h>
34 #include <OP/OP_AutoLockInputs.h>
35 #include <OP/OP_Operator.h>
36 #include <OP/OP_OperatorTable.h>
37 #include <OP/OP_Director.h>
38 #include <PRM/PRM_Include.h>
39 #include <UT/UT_DSOVersion.h>
40 
41 using namespace HDK_Sample;
42 
43 void
45 {
46  table->addOperator(new OP_Operator(
47  "hdk_timecompare",
48  "Time Compare",
51  2, // Must have at least 2 inputs
52  2, // Max 2 inputs
53  0));
54 }
55 
56 static PRM_Default frameDefault(0, "$FF");
57 
58 static PRM_Name names[] = {
59  PRM_Name("attrib", "Comparison Point Attribute"),
60  PRM_Name("resultattrib","Result Attribute"),
61  PRM_Name("frame", "Second Input Frame"),
62 };
63 
69  PRM_Template(PRM_STRING, 1, &names[0]),
70  PRM_Template(PRM_STRING, 1, &names[1]),
71  PRM_Template(PRM_FLT_J, 1, &names[2], &frameDefault),
72  PRM_Template(),
73 };
74 
75 
76 OP_Node *
78 {
79  return new SOP_TimeCompare(net, name, op);
80 }
81 
83  : SOP_Node(net, name, op), myGroup(0)
84 {
85  // This indicates that this SOP manually manages its data IDs,
86  // so that Houdini can identify what attributes may have changed,
87  // e.g. to reduce work for the viewport, or other SOPs that
88  // check whether data IDs have changed.
89  // By default, (i.e. if this line weren't here), all data IDs
90  // would be bumped after the SOP cook, to indicate that
91  // everything might have changed.
92  // If some data IDs don't get bumped properly, the viewport
93  // may not update, or SOPs that check data IDs
94  // may not cook correctly, so be *very* careful!
96 }
97 
99 
100 OP_ERROR
102 {
103  // The SOP_Node::cookInputPointGroups() provides a good default
104  // implementation for just handling a point selection.
105  return cookInputPointGroups(
106  context, // This is needed for cooking the group parameter, and cooking the input if alone.
107  myGroup, // The group (or NULL) is written to myGroup if not alone.
108  alone, // This is true iff called outside of cookMySop to update handles.
109  // true means the group will be for the input geometry.
110  // false means the group will be for gdp (the working/output geometry).
111  true, // (default) true means to set the selection to the group if not alone and the highlight flag is on.
112  0, // (default) Parameter index of the group field
113  -1, // (default) Parameter index of the group type field (-1 since there isn't one)
114  true, // (default) true means that a pointer to an existing group is okay; false means group is always new.
115  false, // (default) false means new groups should be unordered; true means new groups should be ordered.
116  true, // (default) true means that all new groups should be detached, so not owned by the detail;
117  // false means that new point and primitive groups on gdp will be owned by gdp.
118  0 // (default) Index of the input whose geometry the group will be made for if alone.
119  );
120 }
121 
122 OP_ERROR
124 {
125  fpreal t = context.getTime();
126 
127  // Note we only lock our first input here as it is the one we want
128  // to evaluate at context.getTime(). OP_AutoLockInputs will
129  // automatically unlock any locked inputs when we return.
130  // NOTE: Don't call unlockInput or unlockInputs yourself when using this!
131  OP_AutoLockInputs inputs(this);
132  if (inputs.lockInput(0, context) >= UT_ERROR_ABORT)
133  return error();
134 
135  // Now compute our second input's time.
136  OP_Context secondinput_context = context;
137 
138  // This is the frame number we want to evaluate
139  fpreal secondinput_t = FRAME(t);
140 
141  // The context takes seconds, not frame, so we convert.
142  secondinput_t = OPgetDirector()->getChannelManager()->getTime(secondinput_t);
143 
144  // Adjust our cooking context to reflect this
145  secondinput_context.setTime(secondinput_t);
146 
147  // And lock our second input at this specified time.
148  // Note if we fail we still have to unlock our first input before
149  // returning, but this is done automatically by OP_AutoLockInputs.
150  if (inputs.lockInput(1, secondinput_context) >= UT_ERROR_ABORT)
151  return error();
152 
153  // Duplicate our incoming geometry
154  duplicateSource(0, context);
155 
156  // Fetch our attribute names
157  UT_String attribname;
158  ATTRIB(attribname, t);
159  UT_String resultname;
160  RESULTATTRIB(resultname, t);
161 
162  // We do some simple validation here. If no attribute names
163  // are specified, we treat it as a no-op.
164  if (!attribname.isstring() || !resultname.isstring())
165  return error();
166 
167  // Attributes need to follow certain naming conventions for
168  // compatibility with other houdini tools.
169  attribname.forceValidVariableName();
170  resultname.forceValidVariableName();
171 
172  // Find the specified attribute in our source geometry
173  const GU_Detail *agdp = inputGeo(0);
174  const GU_Detail *bgdp = inputGeo(1);
175  const GA_Attribute *ah = agdp->findAttribute(GA_ATTRIB_POINT, attribname);
176  const GA_Attribute *bh = bgdp->findAttribute(GA_ATTRIB_POINT, attribname);
177 
178  // If source attribute doesn't exist, error.
179  if (!ah || !bh)
180  {
181  addError(SOP_ATTRIBUTE_INVALID, (const char *)attribname);
182  return error();
183  }
184 
185  // Create a destination attribute on our own gdp.
186  // First see if it already exists.
187  GA_Attribute *dsth = gdp->findAttribute(GA_ATTRIB_POINT, resultname);
188  const GA_AIFMath *math = dsth ? dsth->getAIFMath() : NULL;
189  if (!math)
190  {
191  // Clone our source attribute
192  dsth = gdp->getAttributes().cloneAttribute(
193  GA_ATTRIB_POINT, resultname, *ah, true);
194 
195  const GA_AIFMath *math = dsth ? dsth->getAIFMath() : NULL;
196  if (!math)
197  {
198  addError(SOP_ATTRIBUTE_INVALID, dsth ? (const char *)attribname
199  : (const char *)resultname);
200  return error();
201  }
202  }
203 
204  if (error() >= UT_ERROR_ABORT)
205  return error();
206 
207  // Here we determine which groups we have to work on. We only
208  // handle point groups.
209  if (cookInputGroups(context) >= UT_ERROR_ABORT)
210  return error();
211 
212  GA_Offset ptoff;
213  GA_FOR_ALL_GROUP_PTOFF(gdp, myGroup, ptoff)
214  {
215  GA_Index ptind = gdp->pointIndex(ptoff);
216  // Fetch the corresponding point from our inputs. We know
217  // we have a corresponding point in agdp since we duplicated
218  // from it, but bgdp might have fewer points. We just
219  // ignore them in this case.
220  GA_Offset aptoff = agdp->pointOffset(ptind);
221  if (ptind >= bgdp->getNumPoints())
222  continue;
223  GA_Offset bptoff = bgdp->pointOffset(ptind);
224 
225  math->sub(*dsth, ptoff, *ah, aptoff, *bh, bptoff);
226  }
227 
228  // We've modified dsth, so we must bump its data ID.
229  dsth->bumpDataId();
230 
231  return error();
232 }
SYS_FORCE_INLINE void bumpDataId()
Definition: GA_Attribute.h:306
Definition of a geometry attribute.
Definition: GA_Attribute.h:198
virtual OP_ERROR error()
PRM_API const PRM_Type PRM_STRING
Attribute Interface class to perform numeric operations on attributes.
Definition: GA_AIFMath.h:88
fpreal getTime() const
Definition: OP_Context.h:62
static OP_Node * myConstructor(OP_Network *, const char *, OP_Operator *)
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)
UT_ErrorSeverity
Definition: UT_Error.h:25
OP_ERROR lockInput(exint input, OP_Context &context)
Locks a single input.
bool addOperator(OP_Operator *op, std::ostream *err=nullptr)
SOP_TimeCompare(OP_Network *net, const char *name, OP_Operator *op)
static PRM_ChoiceList pointGroupMenu
Definition: SOP_Node.h:1193
void addError(int code, const char *msg=0)
Definition: SOP_Node.h:1161
CH_Manager * getChannelManager()
Definition: OP_Director.h:113
GA_Size GA_Offset
Definition: GA_Types.h:646
GA_Attribute * cloneAttribute(GA_AttributeOwner owner, const UT_StringHolder &name, namevalidcertificate, const GA_Attribute &src, bool clone_options, GA_DataIdStrategy data_id_strategy=GA_DATA_ID_BUMP, const GA_ReuseStrategy &reuse=GA_ReuseStrategy())
const GU_Detail * inputGeo(int index, OP_Context &)
Definition: SOP_Node.h:1150
static PRM_Template myTemplateList[]
OP_ERROR cookMySop(OP_Context &context) override
Method to cook geometry for the SOP.
GA_AttributeSet & getAttributes()
Definition: GA_Detail.h:799
int int forceValidVariableName(const char *safechars=NULL)
SOP_NodeFlags mySopFlags
Definition: SOP_Node.h:1628
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.
GLuint const GLchar * name
Definition: glcorearb.h:786
SYS_FORCE_INLINE const GA_Attribute * findAttribute(GA_AttributeScope scope, const UT_StringRef &name, const GA_AttributeOwner search_order[], int search_size) const
Definition: GA_Detail.h:1010
PRM_API PRM_Name PRMgroupName
GA_Size GA_Index
Define the strictness of GA_Offset/GA_Index.
Definition: GA_Types.h:640
PRM_API const PRM_Type PRM_FLT_J
GLenum GLenum GLsizei void * table
Definition: glad.h:5129
void setTime(fpreal t)
Definition: OP_Context.h:63
void setManagesDataIDs(bool onOff)
Definition: SOP_NodeFlags.h:36
GLdouble t
Definition: glad.h:2397
GU_Detail * gdp
Definition: SOP_Node.h:1625
SYS_FORCE_INLINE GA_Index pointIndex(GA_Offset offset) const
Given a point's data offset, return its index.
Definition: GA_Detail.h:349
OP_ERROR cookInputGroups(OP_Context &context, int alone=0) override
#define GA_FOR_ALL_GROUP_PTOFF(gdp, grp, ptoff)
Definition: GA_GBMacros.h:96
fpreal64 fpreal
Definition: SYS_Types.h:277
OP_API OP_Director * OPgetDirector()
bool isstring() const
Definition: UT_String.h:691
virtual const GA_AIFMath * getAIFMath() const
Return the attribute's math interface or NULL.
void newSopOperator(OP_OperatorTable *table)
SYS_FORCE_INLINE GA_Offset pointOffset(GA_Index index) const
Given a point's index (in append order), return its data offset.
Definition: GA_Detail.h:345
fpreal getTime(fpreal sample) const
Definition: CH_Manager.h:1229
SYS_FORCE_INLINE GA_Size getNumPoints() const
Return the number of points.
Definition: GA_Detail.h:334
OP_ERROR duplicateSource(unsigned index, OP_Context &context, GU_Detail *gdp, bool clean=true)