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