HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
SOP_Cop2Raster.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_Cop2Raster.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_Cop2Resolver.h>
44 #include <TIL/TIL_Raster.h>
45 #include <UT/UT_Vector3.h>
46 #include <UT/UT_DSOVersion.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 
70  PRM_Template(PRM_TOGGLE, 1, &prmnames[0], PRMoneDefaults),
72  0, 0, 0, 0, &PRM_SpareData::cop2Path),
73  PRM_Template(PRM_STRING, 1, &prmnames[3], &colorDef, &colorMenu),
74  PRM_Template(PRM_FLT_J, 1, &prmnames[1], &frameDefault),
75  PRM_Template(PRM_PICFILE, 1, &prmnames[2], &fileDef,
77 
78  PRM_Template()
79 };
80 
81 OP_Node *
83 {
84  return new SOP_Cop2Raster(dad, name, op);
85 }
86 
88  : SOP_Node(dad, name, op)
89 {
90  // This indicates that this SOP manually manages its data IDs,
91  // so that Houdini can identify what attributes may have changed,
92  // e.g. to reduce work for the viewport, or other SOPs that
93  // check whether data IDs have changed.
94  // By default, (i.e. if this line weren't here), all data IDs
95  // would be bumped after the SOP cook, to indicate that
96  // everything might have changed.
97  // If some data IDs don't get bumped properly, the viewport
98  // may not update, or SOPs that check data IDs
99  // may not cook correctly, so be *very* careful!
101 }
102 
104 
105 bool
107 {
108  int state;
109  bool changed = SOP_Node::updateParmsFlags();
110 
111  // Here, we disable parameters which we don't care about...
112  state = (USEDISK()) ? 0 : 1;
113  changed |= enableParm("coppath", state);
114  changed |= enableParm("copcolor", state);
115  changed |= enableParm("copframe", state);
116  changed |= enableParm("file", !state);
117 
118  return changed;
119 }
120 
121 //
122 // This is a static method which builds a menu of all the planes in the COP.
123 //
124 void
125 SOP_Cop2Raster::buildColorMenu(void *data, PRM_Name *theMenu, int theMaxSize,
126  const PRM_SpareData *, const PRM_Parm *)
127 {
128  SOP_Cop2Raster *me = (SOP_Cop2Raster *)data;
129  UT_ValArray<char *> items;
130  UT_String relpath, fullpath, netpath, nodepath;
131  int i, useflag = 0;
132 
133  me->COPPATH(relpath, 0.0F);
134  me->getFullCOP2Path(relpath, fullpath, useflag);
135  me->splitCOP2Path(fullpath, netpath, nodepath);
136 
137  TIL_Cop2Resolver::buildColorMenu(netpath, nodepath, items);
138 
139  for (i = 0; i < items.entries() && i < theMaxSize; i++)
140  {
141  theMenu[i].setToken( items(i) );
142  theMenu[i].setLabel( items(i) );
143 
144  free ( items(i) );
145  }
146  theMenu[i].setToken(0); // Need a null terminater
147 }
148 
149 void
151  UT_String &node)
152 {
153  // We split the path into the network and node portion.
154  OP_Node *node_ptr, *parent_ptr;
155  UT_String fullpath;
156 
157  node_ptr = findNode(path);
158  if (!node_ptr) // Failed to find the node
159  {
160  net = "";
161  node = "";
162  return;
163  }
164 
165  parent_ptr = node_ptr->getCreator();
166  if (!parent_ptr)
167  net = "";
168  else
169  parent_ptr->getFullPath(net);
170 
171  node_ptr->getFullPath(fullpath);
172  if (net.isstring())
173  {
174  // The relative path from the net to the fullpath is our node path.
175  node.getRelativePath(net, fullpath);
176  }
177  else
178  node.harden(fullpath);
179 }
180 
181 // This builds an absolute path out of the provided relative path by
182 // expanding to the node and doing a getFullPath.
183 // It also changes net into nodes by diving into the render pointer.
184 int
185 SOP_Cop2Raster::getFullCOP2Path(const char *relpath, UT_String &fullpath,
186  int &flagdependent)
187 {
188  OP_Node *node;
189 
190  fullpath = "";
191  flagdependent = 0;
192 
193  node = findNode(relpath);
194  if (!node)
195  return -1;
196 
197  if (node->getOpTypeID() != COP2_OPTYPE_ID)
198  {
199  // Not the right type. Check to see if its child is the right type.
200  // If so, get the render pointer...
201  if (((OP_Network *)node)->getChildTypeID() == COP2_OPTYPE_ID)
202  {
203  node = ((OP_Network *)node)->getRenderNodePtr();
204  flagdependent = 1;
205  }
206  }
207 
208  // The following call will return NULL if this is not a COP2 node.
209  node = (OP_Node *) CAST_COP2NODE(node);
210  if (!node)
211  return -1;
212 
213  node->getFullPath(fullpath);
214 
215  // Success!
216  return 0;
217 }
218 
219 //
220 // Here's the method which will update our raster. It will load from a
221 // disk file or from the specified COP.
222 //
223 // Returns: 1 if new raster, 0 if old raster, -1 if no raster
224 //
225 int
226 SOP_Cop2Raster::updateRaster(fpreal t)
227 {
228  UT_String fname;
229  int rcode;
230 
231  // We don't have to do this, but for the example, we only care about 8
232  // bit rasters.
233  myRaster.setRasterDepth(myRaster.UT_RASTER_8);
234 
235  rcode = -1;
236  if (USEDISK())
237  {
238  // Loading from a disk is easy. We simply do so.
239  FNAME(fname, t);
240  if (myCurrentName == fname)
241  {
242  rcode = 0;
243  }
244  else
245  {
246  if (!myRaster.load(fname))
247  {
248  addCommonError(UT_CE_FILE_ERROR, (const char *)fname);
249  rcode = -1;
250  }
251  else
252  {
253  myCurrentName.harden(fname);
254  rcode = 1;
255  }
256  }
257  }
258  else
259  {
260  UT_String relpath, fullpath;
261  OP_Node *node = 0;
262  int id, useflag = 0;
263 
264  // We need a cop2 resolver to be able to grab the raster from the node.
266 
267  // Clear out the filename, so that if the user changes our method,
268  // we will reload the file from disk...
269  myCurrentName.harden("");
270  COPPATH(relpath, t); // Find the relative path to the node
271  getFullCOP2Path(relpath, fullpath, useflag);
272 
273  // We use the cop2 resolver to find the unique ID of the node, given
274  // the full path. This ID can then be used to get at the node.
275  id = TIL_Cop2Resolver::getNodeId(fullpath);
276  if (id >= 0)
277  node = OP_Node::lookupNode(id);
278 
279  if (node)
280  {
281  TIL_Raster *r = 0;
282  float frame;
283  UT_String cplane;
284 
285  // Here we have a valid COP. Now, we have to add interests in
286  // the COP. For example, if it re-cooks, ir the COP parameters
287  // change, we have to know about it...
289 
290  // If we used the flag to resolve this node, we need to know
291  // if that flag changes as well.
292  if (useflag)
294 
295  frame = COPFRAME(t);
296  CPLANE(cplane, t);
297 
298  r = cr->getNodeRaster(fullpath, cplane, TIL_NO_PLANE, true,
299  (int)frame, TILE_INT8);
300 
301  // Now we need to make a local copy of this raster.
302  if (r)
303  {
304  myRaster.size((short) r->getXres(), (short) r->getYres());
305  memcpy(myRaster.getRaster(), r->getPixels(), r->getSize());
306  rcode = 1;
307  }
308  else
309  rcode = -1;
310 
311  // One last thing. If the COP is time dependent or dependent on
312  // channels for cooking, we have to flag ourselves as time
313  // dependent. Otherwise, we won't get the correct changes
314  // passed thru.
315  if (node->flags().getTimeDep())
316  flags().setTimeDep(true);
317  }
318  else rcode = -1;
319  }
320  return rcode;
321 }
322 
323 
324 
325 OP_ERROR
327 {
328  fpreal t = context.getTime();
329 
330  // Update our raster...
331  int rstate = updateRaster(t);
332 
333  if (rstate < 0)
334  {
335  // There's no raster, so destroy everything.
336  gdp->clearAndDestroy();
337  }
338  else if (rstate > 0 || gdp->getNumPoints() == 0)
339  {
340  // Here, we've loaded a new image, or our detail was uncached, so lets change our geometry
341 
342  // If we have the same number of points as on the last
343  // cook, we don't have to destroy them.
344  GA_Offset startptoff;
345  int xres = myRaster.Xres();
346  int yres = myRaster.Yres();
347  exint n = exint(xres)*exint(yres);
348  bool samenum = (n == gdp->getNumPoints());
349  bool sameres = samenum && (myPrevXRes == xres) && (myPrevYRes == yres);
350  myPrevXRes = xres;
351  myPrevYRes = yres;
352  if (samenum)
353  {
354  startptoff = gdp->pointOffset(GA_Index(0));
355  }
356  else
357  {
358  // NOTE: This will bump the data IDs for remaining attributes:
359  // i.e. P and the topology attributes.
360  gdp->clearAndDestroy();
361 
362  // For each pixel in the raster, create a point in our geometry
363  startptoff = gdp->appendPointBlock(n);
364  }
365 
366  // Clear the current node selection. The argument GA_GROUP_POINT shows
367  // that we want a point selection after this routine call.
369 
370  // Add diffuse color, if not already added on previous cook.
372  if (!colorh.isValid())
374 
375  // Now find out about our raster
376  UT_RGBA *rgba = myRaster.getRaster();
377 
378  // Copy the colour from each pixel to the corresponding point.
379  // This SOP always generates a contiguous block of point offsets,
380  // so we can just start at startptoff and increment.
381  GA_Offset ptoff = startptoff;
382  for (int x = 0; x < xres; ++x)
383  {
384  for (int y = 0; y < yres; ++y, ++rgba, ++ptoff)
385  {
386  // We don't need to set the point positions again
387  // if the resolution is the same as on the last cook.
388  if (!sameres)
389  gdp->setPos3(ptoff, (float)x/(float)xres, (float)y/(float)yres, 0);
390  UT_Vector3 clr(rgba->r, rgba->g, rgba->b);
391  clr *= 1.0/255.0;
392  colorh.set(ptoff, clr);
393  }
394  }
395 
396  // Add the newly created points to the node selection.
397  // We could have added them one-by-one with
398  // selectPoint(ptoff, true, true), but that would have been slow.
400 
401  // Bump the attribute data IDs of the modified attributes.
402  colorh.bumpDataId();
403  if (!sameres)
404  gdp->getP()->bumpDataId();
405  }
406  return error();
407 }
408 
409 void
411 {
412  OP_Operator *op = new OP_Operator(
413  "hdk_cop2raster", // Internal name
414  "COP Raster", // UI name
415  SOP_Cop2Raster::myConstructor, // How to build
416  SOP_Cop2Raster::myTemplateList, // My parms
417  0, // Min # of sources
418  0, // Max # of sources
419  0, // Local variables
420  OP_FLAG_GENERATOR); // Flag it as generator
421 
422  table->addOperator(op);
423 }
void * getPixels()
Definition: PXL_Raster.h:145
void setRasterDepth(UT_RasterDepth depth)
SYS_FORCE_INLINE void bumpDataId()
Definition: GA_Attribute.h:306
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:98
virtual OP_ERROR error()
PRM_API const PRM_Type PRM_STRING
const OP_NodeFlags & flags() const
Definition: OP_Node.h:1388
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:1172
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)
OP_Network * getCreator() const
bool updateParmsFlags() override
GA_RWHandleT< UT_Vector3F > GA_RWHandleV3
Definition: GA_Handle.h:1382
static PRM_SpareData cop2Path
GA_Size GA_Offset
Definition: GA_Types.h:641
const char * getFullPath(UT_String &str) const
Definition: PRM_ParmOwner.h:52
GLdouble n
Definition: glcorearb.h:2008
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)
void harden()
Take shallow copy and make it deep.
Definition: UT_String.h:215
SOP_NodeFlags mySopFlags
Definition: SOP_Node.h:1625
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:635
GLint GLenum GLint x
Definition: glcorearb.h:409
void splitCOP2Path(const char *path, UT_String &net, UT_String &node)
Splits a full cop2 path into the net and node portions.
PRM_API const PRM_Type PRM_FLT_J
int getFullCOP2Path(const char *relpath, UT_String &fullpath, int &flagdependent)
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
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
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.
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)
int64 getSize() const
Definition: PXL_Raster.h:101
#define TIL_DEFAULT_COLOR_PLANE
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:696
bool isstring() const
Definition: UT_String.h:691
UT_RGBA * getRaster() const
void select(GU_SelectionType stype)
static void buildColorMenu(const char *net, const char *node, UT_ValArray< char * > &items)
#define TIL_NO_PLANE
GLboolean r
Definition: glcorearb.h:1222
OP_ERROR cookMySop(OP_Context &context) override
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
static PRM_Template myTemplateList[]
exint getXres() const
Definition: PXL_Raster.h:97
void clearSelection()
Definition: format.h:895
void newSopOperator(OP_OperatorTable *table)
static int getNodeId(const char *net, const char *node)
static TIL_Cop2Resolver * getResolver()
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
SOP_Cop2Raster(OP_Network *, const char *, OP_Operator *)
static OP_Node * myConstructor(OP_Network *, const char *, OP_Operator *)