HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
SOP_CopRaster.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 SOP reads a raster image and generates a point from each pixel in the
27  * image. The point is colored based on the pixel value of the image.
28  *
29  * NOTE: You must turn on the "Points" display option (this is not on by
30  * default) to see the generated points in the Houdini viewport. Because
31  * Houdini does not use point colors when it displays points, you will need to
32  * attach another SOP (for example, the Particle SOP) to see the effect of the
33  * colored pixels.
34  */
35 
36 #include "SOP_CopRaster.h"
37 
38 #include <GU/GU_Detail.h>
39 #include <OP/OP_Director.h>
40 #include <OP/OP_OperatorTable.h>
41 #include <PRM/PRM_Include.h>
42 #include <PRM/PRM_SpareData.h>
43 #include <TIL/TIL_CopResolver.h>
44 #include <TIL/TIL_Raster.h>
45 #include <UT/UT_DSOVersion.h>
46 #include <UT/UT_Vector3.h>
47 #include <stdio.h>
48 
49 using namespace HDK_Sample;
50 
51 static PRM_Name prmnames[] = {
52  PRM_Name("usedisk", "Use Disk Image"),
53  PRM_Name("copframe", "COP Frame"),
54  PRM_Name("file", "File Name"),
55  PRM_Name("copcolor", "Plane"),
56  PRM_Name("coppath", "COP Path"),
57 };
58 
59 static PRM_Default frameDefault(0, "$F");
60 
61 
62 static PRM_ChoiceList colorMenu(PRM_CHOICELIST_SINGLE,
64 
65 static PRM_Default fileDef(0, "circle.pic");
66 static PRM_Default colorDef(0, TIL_DEFAULT_COLOR_PLANE);
67 
69  PRM_Template(PRM_TOGGLE, 1, &prmnames[0], PRMoneDefaults),
71  0, 0, 0, 0, &PRM_SpareData::cop2Path),
72  PRM_Template(PRM_STRING, 1, &prmnames[3], &colorDef, &colorMenu),
73  PRM_Template(PRM_FLT_J, 1, &prmnames[1], &frameDefault),
74  PRM_Template(PRM_PICFILE, 1, &prmnames[2], &fileDef,
76 
77  PRM_Template()
78 };
79 
80 OP_Node *
82 {
83  return new SOP_CopRaster(dad, name, op);
84 }
85 
87  : SOP_Node(dad, name, op)
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 
103 
104 bool
106 {
107  int state;
108  bool changed = SOP_Node::updateParmsFlags();
109 
110  // Here, we disable parameters which we don't care about...
111  state = (USEDISK()) ? 0 : 1;
112  changed |= enableParm("coppath", state);
113  changed |= enableParm("copcolor", state);
114  changed |= enableParm("copframe", state);
115  changed |= enableParm("file", !state);
116 
117  return changed;
118 }
119 
120 //
121 // This is a static method which builds a menu of all the planes in the COP.
122 //
123 void
124 SOP_CopRaster::buildColorMenu(void *data, PRM_Name *theMenu, int theMaxSize,
125  const PRM_SpareData *, const PRM_Parm *)
126 {
127  SOP_CopRaster *me = (SOP_CopRaster *)data;
128  UT_ValArray<char *> items;
129  UT_String relpath, fullpath, netpath, nodepath;
130  int i, useflag = 0;
131 
132  me->COPPATH(relpath, 0.0F);
133  me->getFullCOP2Path(relpath, fullpath, useflag);
134  me->splitCOP2Path(fullpath, netpath, nodepath);
135 
136  TIL_CopResolver::buildColorMenu(netpath, nodepath, items);
137 
138  for (i = 0; i < items.entries() && i < theMaxSize; i++)
139  {
140  theMenu[i].setToken( items(i) );
141  theMenu[i].setLabel( items(i) );
142 
143  free ( items(i) );
144  }
145  theMenu[i].setToken(0); // Need a null terminater
146 }
147 
148 void
150  UT_String &node)
151 {
152  // We split the path into the network and node portion.
153  OP_Node *node_ptr, *parent_ptr;
154  UT_String fullpath;
155 
156  node_ptr = findNode(path);
157  if (!node_ptr) // Failed to find the node
158  {
159  net = "";
160  node = "";
161  return;
162  }
163 
164  parent_ptr = node_ptr->getCreator();
165  if (!parent_ptr)
166  net = "";
167  else
168  parent_ptr->getFullPath(net);
169 
170  node_ptr->getFullPath(fullpath);
171  if (net.isstring())
172  {
173  // The relative path from the net to the fullpath is our node path.
174  node.getRelativePath(net, fullpath);
175  }
176  else
177  node.harden(fullpath);
178 }
179 
180 // This builds an absolute path out of the provided relative path by
181 // expanding to the node and doing a getFullPath.
182 // It also changes net into nodes by diving into the render pointer.
183 int
184 SOP_CopRaster::getFullCOP2Path(const char *relpath, UT_String &fullpath,
185  int &flagdependent)
186 {
187  OP_Node *node;
188 
189  fullpath = "";
190  flagdependent = 0;
191 
192  node = findNode(relpath);
193  if (!node)
194  return -1;
195 
196  if (node->getOpTypeID() != COP2_OPTYPE_ID)
197  {
198  // Not the right type. Check to see if its child is the right type.
199  // If so, get the render pointer...
200  if (((OP_Network *)node)->getChildTypeID() == COP2_OPTYPE_ID)
201  {
202  node = ((OP_Network *)node)->getRenderNodePtr();
203  flagdependent = 1;
204  }
205  }
206 
207  // The following call will return NULL if this is not a COP2 node.
208  node = (OP_Node *) CAST_COP2NODE(node);
209  if (!node)
210  return -1;
211 
212  node->getFullPath(fullpath);
213 
214  // Success!
215  return 0;
216 }
217 
218 //
219 // Here's the method which will update our raster. It will load from a
220 // disk file or from the specified COP.
221 //
222 // Returns: 1 if new raster, 0 if old raster, -1 if no raster
223 //
224 int
225 SOP_CopRaster::updateRaster(fpreal t)
226 {
227  UT_String fname;
228  int rcode;
229 
230  // We don't have to do this, but for the example, we only care about 8
231  // bit rasters.
232  myRaster.setRasterDepth(myRaster.UT_RASTER_8);
233 
234  rcode = -1;
235  if (USEDISK())
236  {
237  // Loading from a disk is easy. We simply do so.
238  FNAME(fname, t);
239  if (myCurrentName == fname)
240  {
241  rcode = 0;
242  }
243  else
244  {
245  if (!myRaster.load(fname))
246  {
247  addCommonError(UT_CE_FILE_ERROR, (const char *)fname);
248  rcode = -1;
249  }
250  else
251  {
252  myCurrentName.harden(fname);
253  rcode = 1;
254  }
255  }
256  }
257  else
258  {
259  UT_String relpath, fullpath;
260  OP_Node *node = 0;
261  int id, useflag = 0;
262 
263  // We need a cop2 resolver to be able to grab the raster from the node.
265 
266  // Clear out the filename, so that if the user changes our method,
267  // we will reload the file from disk...
268  myCurrentName.harden("");
269  COPPATH(relpath, t); // Find the relative path to the node
270  getFullCOP2Path(relpath, fullpath, useflag);
271 
272  // We use the cop2 resolver to find the unique ID of the node, given
273  // the full path. This ID can then be used to get at the node.
274  id = TIL_CopResolver::getNodeId(fullpath);
275  if (id >= 0)
276  node = OP_Node::lookupNode(id);
277 
278  if (node)
279  {
280  TIL_Raster *r = 0;
281  float frame;
282  UT_String cplane;
283 
284  // Here we have a valid COP. Now, we have to add interests in
285  // the COP. For example, if it re-cooks, ir the COP parameters
286  // change, we have to know about it...
288 
289  // If we used the flag to resolve this node, we need to know
290  // if that flag changes as well.
291  if (useflag)
293 
294  frame = COPFRAME(t);
295  CPLANE(cplane, t);
296 
297  r = cr->getNodeRaster(fullpath, cplane, TIL_NO_PLANE, true,
298  (int)frame, TILE_INT8);
299 
300  // Now we need to make a local copy of this raster.
301  if (r)
302  {
303  myRaster.size((short) r->getXres(), (short) r->getYres());
304  memcpy(myRaster.getRaster(), r->getPixels(), r->getSize());
305  rcode = 1;
306  }
307  else
308  rcode = -1;
309 
310  // One last thing. If the COP is time dependent or dependent on
311  // channels for cooking, we have to flag ourselves as time
312  // dependent. Otherwise, we won't get the correct changes
313  // passed thru.
314  if (node->flags().getTimeDep())
315  flags().setTimeDep(true);
316  }
317  else rcode = -1;
318  }
319  return rcode;
320 }
321 
322 
323 
324 OP_ERROR
326 {
327  fpreal t = context.getTime();
328 
329  // Update our raster...
330  int rstate = updateRaster(t);
331 
332  if (rstate < 0)
333  {
334  // There's no raster, so destroy everything.
335  gdp->clearAndDestroy();
336  }
337  else if (rstate > 0 || gdp->getNumPoints() == 0)
338  {
339  // Here, we've loaded a new image, or our detail was uncached, so lets change our geometry
340 
341  // If we have the same number of points as on the last
342  // cook, we don't have to destroy them.
343  GA_Offset startptoff;
344  int xres = myRaster.Xres();
345  int yres = myRaster.Yres();
346  exint n = exint(xres)*exint(yres);
347  bool samenum = (n == gdp->getNumPoints());
348  bool sameres = samenum && (myPrevXRes == xres) && (myPrevYRes == yres);
349  myPrevXRes = xres;
350  myPrevYRes = yres;
351  if (samenum)
352  {
353  startptoff = gdp->pointOffset(GA_Index(0));
354  }
355  else
356  {
357  // NOTE: This will bump the data IDs for remaining attributes:
358  // i.e. P and the topology attributes.
359  gdp->clearAndDestroy();
360 
361  // For each pixel in the raster, create a point in our geometry
362  startptoff = gdp->appendPointBlock(n);
363  }
364 
365  // Clear the current node selection. The argument GA_GROUP_POINT shows
366  // that we want a point selection after this routine call.
368 
369  // Add diffuse color, if not already added on previous cook.
371  if (!colorh.isValid())
373 
374  // Now find out about our raster
375  UT_RGBA *rgba = myRaster.getRaster();
376 
377  // Copy the colour from each pixel to the corresponding point.
378  // This SOP always generates a contiguous block of point offsets,
379  // so we can just start at startptoff and increment.
380  GA_Offset ptoff = startptoff;
381  for (int x = 0; x < xres; ++x)
382  {
383  for (int y = 0; y < yres; ++y, ++rgba, ++ptoff)
384  {
385  // We don't need to set the point positions again
386  // if the resolution is the same as on the last cook.
387  if (!sameres)
388  gdp->setPos3(ptoff, (float)x/(float)xres, (float)y/(float)yres, 0);
389  UT_Vector3 clr(rgba->r, rgba->g, rgba->b);
390  clr *= 1.0/255.0;
391  colorh.set(ptoff, clr);
392  }
393  }
394 
395  // Add the newly created points to the node selection.
396  // We could have added them one-by-one with
397  // selectPoint(ptoff, true, true), but that would have been slow.
399 
400  // Bump the attribute data IDs of the modified attributes.
401  colorh.bumpDataId();
402  if (!sameres)
403  gdp->getP()->bumpDataId();
404  }
405  return error();
406 }
407 
408 void
410 {
411  OP_Operator *op = new OP_Operator(
412  "hdk_copraster", // Internal name
413  "COP Raster", // UI name
414  SOP_CopRaster::myConstructor, // How to build
415  SOP_CopRaster::myTemplateList, // My parms
416  0, // Min # of sources
417  0, // Max # of sources
418  0, // Local variables
419  OP_FLAG_GENERATOR); // Flag it as generator
420 
421  table->addOperator(op);
422 }
void * getPixels()
Definition: PXL_Raster.h:162
virtual TIL_Raster * getNodeRaster(const char *fullpath, const char *cmenu, const char *amenu, bool override_frame=false, fpreal frame=1.0, TIL_DataFormat f=TILE_MAX_DATA_FORMAT)
void setRasterDepth(UT_RasterDepth depth)
SYS_FORCE_INLINE void bumpDataId()
Definition: GA_Attribute.h:306
static PRM_Template myTemplateList[]
Definition: SOP_CopRaster.h:40
virtual bool updateParmsFlags()
PRM_API const PRM_Type PRM_PICFILE
short Yres() const
Definition: UT_Raster.h:34
exint getYres() const
Definition: PXL_Raster.h:115
virtual OP_ERROR error()
static void buildColorMenu(const char *net, const char *node, UT_ValArray< char * > &items)
PRM_API const PRM_Type PRM_STRING
const OP_NodeFlags & flags() const
Definition: OP_Node.h:1400
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 clearAndDestroy()
Clear all the points/primitives out of this detail.
Definition: GEO_Detail.h:267
GLsizei const GLchar *const * path
Definition: glcorearb.h:3341
void setToken(const char *s)
Sets the token, doing a deep copy.
Definition: PRM_Name.h:106
int64 exint
Definition: SYS_Types.h:125
bool getTimeDep() const
Definition: OP_NodeFlags.h:187
void getRelativePath(const char *src_fullpath, const char *dest_fullpath, bool file_path=false)
const GA_Attribute * findDiffuseAttribute(GA_AttributeOwner who) const
GA_Attribute * getP()
Convenience method to access the P attribute.
Definition: GA_Detail.h:164
GA_Attribute * addDiffuseAttribute(GA_AttributeOwner who, GA_Storage s=GA_STORE_INVALID)
#define OP_FLAG_GENERATOR
Definition: OP_Operator.h:82
UT_ErrorSeverity
Definition: UT_Error.h:25
void addCommonError(UT_CommonErrorCode what, const char *msg=0)
Definition: SOP_Node.h:1175
GLint y
Definition: glcorearb.h:103
#define UT_CE_FILE_ERROR
Definition: UT_Error.h:74
bool addOperator(OP_Operator *op, std::ostream *err=nullptr)
static void buildColorMenu(void *data, PRM_Name *, int, const PRM_SpareData *, const PRM_Parm *)
This static methods is used to build a menu for the UI.
OP_Network * getCreator() const
static TIL_CopResolver * getResolver()
GA_RWHandleT< UT_Vector3F > GA_RWHandleV3
Definition: GA_Handle.h:1382
static PRM_SpareData cop2Path
bool updateParmsFlags() override
void splitCOP2Path(const char *path, UT_String &net, UT_String &node)
Splits a full cop2 path into the net and node portions.
GA_Size GA_Offset
Definition: GA_Types.h:646
const char * getFullPath(UT_String &str) const
Definition: PRM_ParmOwner.h:52
GLdouble n
Definition: glcorearb.h:2008
OP_ERROR cookMySop(OP_Context &context) override
void newSopOperator(OP_OperatorTable *table)
SYS_FORCE_INLINE GA_Offset appendPointBlock(GA_Size npoints)
Append new points, returning the first offset of the contiguous block.
Definition: GA_Detail.h:330
bool enableParm(int pi, int state, int v=-1)
void size(short w, short h, void *newRaster=0)
SOP_CopRaster(OP_Network *, const char *, OP_Operator *)
Definition: SOP_CopRaster.C:86
void harden()
Take shallow copy and make it deep.
Definition: UT_String.h:215
SOP_NodeFlags mySopFlags
Definition: SOP_Node.h:1628
GLuint id
Definition: glcorearb.h:655
GLuint const GLchar * name
Definition: glcorearb.h:786
GA_Size GA_Index
Define the strictness of GA_Offset/GA_Index.
Definition: GA_Types.h:640
GLint GLenum GLint x
Definition: glcorearb.h:409
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
virtual OP_OpTypeId getOpTypeID() const
Definition: OP_Node.h:532
GLdouble t
Definition: glad.h:2397
GU_Detail * gdp
Definition: SOP_Node.h:1625
static int getNodeId(const char *net, const char *node)
static OP_Node * myConstructor(OP_Network *, const char *, OP_Operator *)
Definition: SOP_CopRaster.C:81
exint entries() const
Alias of size(). size() is preferred.
Definition: UT_Array.h:648
PRM_API PRM_Default PRMoneDefaults[]
SYS_FORCE_INLINE void setPos3(GA_Offset ptoff, const UT_Vector3 &pos)
Set P from a UT_Vector3.
Definition: GA_Detail.h:237
virtual void addExtraInput(OP_Node *op, OP_InterestType type)
void setTimeDep(bool on_off)
Definition: OP_NodeFlags.h:111
fpreal64 fpreal
Definition: SYS_Types.h:277
int64 getSize() const
Definition: PXL_Raster.h:118
int load(const char *pathname, IMG_ScaleOp scaled=IMG_SCALE_NONE, UT_FilterType filter=UT_FILTER_POINT, short size=100, bool watermarked=false, bool topremult=true)
PRM_API const PRM_Type PRM_TOGGLE
short Xres() const
Definition: UT_Raster.h:33
#define TILE_INT8
Definition: TIL_Defines.h:66
static OP_Node * lookupNode(int unique_id, bool include_proxy=false)
Definition: OP_Node.h:707
bool isstring() const
Definition: UT_String.h:691
UT_RGBA * getRaster() const
void select(GU_SelectionType stype)
#define TIL_NO_PLANE
GLboolean r
Definition: glcorearb.h:1222
#define TIL_DEFAULT_COLOR_PLANE
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
exint getXres() const
Definition: PXL_Raster.h:114
int getFullCOP2Path(const char *relpath, UT_String &fullpath, int &flagdependent)
void clearSelection()
Definition: format.h:895
void setLabel(const char *s)
Sets the label, doing a deep copy.
Definition: PRM_Name.h:114
SYS_FORCE_INLINE GA_Size getNumPoints() const
Return the number of points.
Definition: GA_Detail.h:334
static PRM_SpareData fileChooserModeRead