HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
MSS_CustomBrushState.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 code is for creating the state to go with this op.
27 */
28 
29 #include "MSS_CustomBrushState.h"
30 
31 #include <DM/DM_Defines.h>
32 #include <DM/DM_ViewportType.h>
33 #include <GR/GR_DisplayOption.h>
34 #include <GU/GU_PrimCircle.h>
35 #include <MSS/MSS_SingleOpState.h>
36 #include <OP/OP_OperatorTable.h>
37 #include <PRM/PRM_Parm.h>
38 #include <RE/RE_Render.h>
39 #include <SOP/SOP_Node.h>
40 #include <UT/UT_DSOVersion.h>
41 
42 #define MSS_CLICK_BUTTONS (DM_PRIMARY_BUTTON|DM_SECONDARY_BUTTON)
43 
44 using namespace HDK_Sample;
45 
46 // register the state
47 void
49 {
50  m->registerState(
51  new PI_StateTemplate("proto_custombrush", // state name
52  "Custom Brush", // English name
53  "SOP_proto_custombrush", // icon name
57  PI_NETMASK_SOP, // marks this as a SOP state
58  0));
59 }
60 
61 // our state has no parameters
64 
65 BM_State *
67  BM_SceneManager *scene)
68 {
69  return new MSS_CustomBrushState((JEDI_View &)view, templ, scene);
70 }
71 
73  JEDI_View &view,
74  PI_StateTemplate &templ,
75  BM_SceneManager *scene,
76  const char *cursor)
77  : MSS_SingleOpState(view, templ, scene, cursor),
78  myBrushHandle((DM_SceneManager &)workbench(), "MSS_CustomBrushState")
79 {
80  myIsBrushVisible = false;
81  myResizingCursor = false;
82 
83  // create brush geometry
84  GU_PrimCircleParms cparms;
85  cparms.gdp = &myBrushCursor;
86  cparms.order = 3; // quadratic
87  cparms.imperfect = 0; // rational
88  cparms.xform.identity();
89 #if defined(HOUDINI_11)
90  GU_PrimCircle::build(cparms, GEOPRIMBEZCURVE); // Bezier
91 #else
92  GU_PrimCircle::build(cparms, GEO_PRIMBEZCURVE); // Bezier
93 #endif
94 
95  myBrushCursorXform.identity();
96  myBrushRadius = 0.1;
97 
98  // only use this state in 3D viewports
100 }
101 
103 {
104  // Nothing needed.
105 }
106 
107 const char *
109 {
110  return "MSS_CustomBrushState";
111 }
112 
113 int
115 {
116  int result = MSS_SingleOpState::enter(how);
117  // ask for handleMouseEvent to be called when the mouse moves or a
118  // mouse button is clicked
119  wantsLocates(1);
121  updatePrompt();
122 
123  // turn off the highlight so we can see the color we are painting
124  OP_Node *op = getNode();
125  if(op)
126  op->setHighlight(0);
127  return result;
128 }
129 
130 void
132 {
133  // cleanup
134  wantsLocates(0);
136  myIsBrushVisible = false;
137  redrawScene();
139 }
140 
141 void
143 {
145  wantsLocates(1);
147  updatePrompt();
148 
149  // turn off the highlight so we can see the color we are painting
150  OP_Node *op = getNode();
151  if(op)
152  op->setHighlight(0);
153 }
154 
155 void
157 {
158  wantsLocates(0);
160  myIsBrushVisible = false;
161  redrawScene();
163 }
164 
165 int
167 {
168  SOP_Node *sop = (SOP_Node *)getNode();
169  if (!sop)
170  return 1; // consumed but useless
171 
172  fpreal t = getTime();
173  int x = event->state.values[X];
174  int y = event->state.values[Y];
175 
176  if (event->reason == UI_VALUE_START &&
177  (event->state.altFlags & UI_ALT_KEY ||
178  event->state.altFlags & UI_SHIFT_KEY))
179  {
180  // prepare for resizing the brush
181  myResizeCursorX = x;
182  myResizeCursorY = y;
183  myResizingCursor = true;
184  }
185  else if (myResizingCursor)
186  {
187  // scale the brush's radius
188  fpreal dist = x - myLastCursorX +
189  y - myLastCursorY;
190 
191  myBrushRadius *= powf(1.01, dist);
192 
193  if (event->reason == UI_VALUE_CHANGED)
194  myResizingCursor = false;
195 
196  updateBrush(myResizeCursorX, myResizeCursorY);
197  }
198  else if (event->reason == UI_VALUE_LOCATED)
199  {
200  // re-position the brush
201  updateBrush(x, y);
202  }
203  else
204  {
205  // Apply a stroke
206  //
207  // The set*() method calls below will automatically record the undo
208  // actions. Since we do automatic matching of the sop node type via the
209  // same name as the state, we're assuming here for simplicity that the
210  // parameters exist.
211 
212  UT_Vector3 rayorig, dir;
213  mapToWorld(x, y, dir, rayorig);
214 
215  xformToObjectCoord(rayorig);
216  xformToObjectVector(dir);
217 
218  bool begin = (event->reason == UI_VALUE_START ||
219  event->reason == UI_VALUE_PICKED);
220  if(begin)
222 
223  sop->setFloat("origin", 0, t, rayorig.x());
224  sop->setFloat("origin", 1, t, rayorig.y());
225  sop->setFloat("origin", 2, t, rayorig.z());
226 
227  sop->setFloat("direction", 0, t, dir.x());
228  sop->setFloat("direction", 1, t, dir.y());
229  sop->setFloat("direction", 2, t, dir.z());
230 
231  sop->setFloat("radius", 0, t, myBrushRadius);
232 
233  {
234  UT_String str = (event->state.values[W] == DM_SECONDARY_BUTTON)
235  ? "erase" : "paint";
236  sop->setString(str, CH_STRING_LITERAL, "operation", 0, t);
237  }
238 
239  OP_Context context(t);
240 
241  bool set_op = false;
242  if (begin)
243  {
244  // indicate the begin of a stroke
245  set_op = true;
246  sop->setString(UT_String("begin"), CH_STRING_LITERAL,"event",0,t);
247  }
248 
249  // indicate a stroke is active
250  bool active = (event->reason == UI_VALUE_ACTIVE ||
251  event->reason == UI_VALUE_PICKED);
252  if (active)
253  {
254  if(set_op)
255  {
256  // trigger a cook of the CustomBrush SOP so it can cook with
257  // the current stroke values
258  sop->getCookedGeo(context);
259  }
260 
261  set_op = true;
262  sop->setString(UT_String("active"), CH_STRING_LITERAL, "event",0,t);
263  }
264  // If the brush event is an end, we need to close the undo block.
265  if (event->reason == UI_VALUE_CHANGED ||
266  event->reason == UI_VALUE_PICKED)
267  {
268  if(set_op)
269  {
270  // trigger a cook of the CustomBrush SOP so it can cook
271  // with the current stroke values
272  sop->getCookedGeo(context);
273  }
274 
275  // now change the stroke parameter to indicate a no-op.
276  sop->setString(UT_String("end"), CH_STRING_LITERAL, "event", 0, t);
277 
278  // trigger a cook of the CustomBrush SOP so it can cook with
279  // the end stroke values
280  sop->getCookedGeo(context);
281 
282  // now change the stroke parameter to indicate a no-op.
283  sop->setString(UT_String("nop"), CH_STRING_LITERAL, "event", 0, t);
284 
286  }
287 
288  updateBrush(x, y);
289  }
290 
291  myLastCursorX = x;
292  myLastCursorY = y;
293 
294  return 1;
295 }
296 
297 void
299 {
300  if (!isPreempted() && myIsBrushVisible)
301  {
302  UT_Color clr;
303 
304  r->pushMatrix();
305  r->multiplyMatrix(myBrushCursorXform);
306 
307  if(ghost)
308  {
309  // color for obstructed parts of the brush
310  clr = UT_Color(UT_RGB, 0.625,0.4,0.375);
311  }
312  else
313  {
314  // color for unobstructed parts of the brush
315  clr = UT_Color(UT_RGB, 1, 0.1, 0);
316  }
317 
318  // TEMP FIX until doRender() takes a RE_RenderContext.
319  myBrushHandle.renderWire(r, 0, 0, 0, clr, &myBrushCursor);
320 
321  r->popMatrix();
322  }
323 }
324 
325 void
327 {
328  showPrompt("LMB to apply stroke. MMB to erase. Shift-LMB to adjust radius.");
329 }
330 
331 void
333 {
334  // get cameraspace to worldspace transform
335  getViewportItransform(myBrushCursorXform);
336 
337  // determine the direction the camera a facing
338  UT_Vector3 forward = rowVecMult3(UT_Vector3(0, 0, -1), myBrushCursorXform);
339 
340  // position the brush under the pointer and one unit away from the camera
341  UT_Vector3 rayorig, dir;
342  mapToWorld(x, y, dir, rayorig);
343  UT_Vector3 delta(1.0 / dot(dir, forward) * dir);
344  myBrushCursorXform.translate(delta.x(), delta.y(), delta.z());
345 
346  // scale the brush
347  myBrushCursorXform.prescale(myBrushRadius, myBrushRadius, 1);
348 
349  // ensure the brush is visible
350  myIsBrushVisible = true;
351  redrawScene();
352 }
const GU_Detail * getCookedGeo(OP_Context &, int forced=0)
void popMatrix(bool all_matrices=true, RE_MatrixMode mmode=RE_MATRIX_VIEWING)
GA_API const UT_StringHolder dist
void interrupt(BM_SimpleState *state=0) override
void interrupt(BM_SimpleState *=0) override
fpreal getTime() const
Obtains the current global time.
void mapToWorld(float x, float y, UT_Vector3 &dir, UT_Vector3 &rayorig)
Map viewport coordinates to worldspace location and direction.
int isPreempted() const
Definition: BM_State.h:270
Y
Definition: ImathEuler.h:184
void beginDistributedUndoBlock(const char *operation, UT_UndoBlockType blocktype, bool ignore_log=false)
void xformToObjectCoord(UT_Vector3 &p)
static PRM_Template * ourTemplateList
parameters for this state
UT_Vector3T< float > UT_Vector3
#define UI_SHIFT_KEY
const unsigned PI_NETMASK_SOP
constexpr SYS_FORCE_INLINE T & z() noexcept
Definition: UT_Vector3.h:667
#define DM_SECONDARY_BUTTON
Definition: DM_Defines.h:216
X
Definition: ImathEuler.h:183
SYS_API float powf(float x, float y)
GLint y
Definition: glcorearb.h:103
**But if you need a result
Definition: thread.h:613
int enter(BM_SimpleState::BM_EntryType how) override
void setString(const UT_StringRef &val, CH_StringMeaning meaning, int parmi, int vectori, fpreal t)
void showPrompt(const char *msg)
Set the status bar text.
void exit() override
virtual int registerState(PI_StateTemplate *type)
void newModelState(BM_ResourceManager *m)
int enter(BM_SimpleState::BM_EntryType how) override
called when the user enters the state
struct _cl_event * event
Definition: glcorearb.h:2961
void pushMatrix(bool all_matrices=true, RE_MatrixMode mmode=RE_MATRIX_VIEWING)
bool setHighlight(bool on_off)
static BM_State * ourConstructor(BM_View &view, PI_StateTemplate &templ, BM_SceneManager *scene)
used by DM to create our state
MSS_CustomBrushState(JEDI_View &view, PI_StateTemplate &templ, BM_SceneManager *scene, const char *cursor=BM_DEFAULT_CURSOR)
void wantsLocates(int yesNo)
Definition: BM_State.h:265
void removeClickInterest(int buttons)
fpreal64 dot(const CE_VectorT< T > &a, const CE_VectorT< T > &b)
Definition: CE_Vector.h:140
void setViewportMask(unsigned mask)
void prescale(T sx, T sy, T sz, T sw=1)
Definition: UT_Matrix4.h:723
#define MSS_CLICK_BUTTONS
void exit() override
called when the user leaves the state
virtual void updatePrompt()
sets the prompt's text
void identity()
Set the matrix to identity.
Definition: UT_Matrix4.h:1128
UI_DeviceEvent state
Definition: UI_Event.h:61
void updateBrush(int x, int y)
repositions the brush's guide geometry
void resume(BM_SimpleState *=0) override
GLint GLenum GLint x
Definition: glcorearb.h:409
that also have some descendant prim *whose name begins with which in turn has a child named baz where *the predicate active
GLdouble t
Definition: glad.h:2397
void redrawScene()
GEO_API const TypeMask GEOPRIMBEZCURVE
#define UI_ALT_KEY
void translate(T dx, T dy, T dz=0)
Definition: UT_Matrix4.h:773
UT_Vector3T< T > rowVecMult3(const UT_Vector3T< T > &v, const UT_Matrix4T< S > &m)
Definition: UT_Matrix4.h:1926
void setFloat(int parmi, int vectori, fpreal t, fpreal value, PRM_AddKeyType add_key=PRM_AK_MARK_PENDING)
fpreal64 fpreal
Definition: SYS_Types.h:277
void renderWire(RE_RenderContext r, int pickflag, uint id1, uint id2, const UT_Color &color, GU_Detail *gdp=NULL, const UT_DMatrix4 *xform=NULL)
int handleMouseEvent(UI_Event *event) override
Respond to mouse or keyboard events.
void addClickInterest(int buttons)
void resume(BM_SimpleState *state=0) override
#define DM_VIEWPORT_PERSPECTIVE
UI_Reason reason
Definition: UI_Event.h:63
const char * className() const override
The name and type of this class:
GLboolean r
Definition: glcorearb.h:1222
void getViewportItransform(UT_Matrix4 &xform)
Get cameraspace to worldspace transform.
void multiplyMatrix(const UT_Matrix4 &m)
constexpr SYS_FORCE_INLINE T & y() noexcept
Definition: UT_Vector3.h:665
static GEO_Primitive * build(const GU_PrimCircleParms &parms, GA_PrimitiveTypeId type=GEO_PRIMCIRCLE)
OP_Node * getNode() const
void xformToObjectVector(UT_Vector3 &v)
void endDistributedUndoBlock(bool ignore_log=false)
void doRender(RE_Render *r, int x, int y, int ghost) override
Render the brush "cursor" geometry:
constexpr SYS_FORCE_INLINE T & x() noexcept
Definition: UT_Vector3.h:663
PcpNodeRef_ChildrenIterator begin(const PcpNodeRef::child_const_range &r)
Support for range-based for loops for PcpNodeRef children ranges.
Definition: node.h:558