#include "MSS_CustomBrushState.h"
#include <DM/DM_Defines.h>
#include <DM/DM_ViewportType.h>
#include <GR/GR_Detail.h>
#include <GR/GR_DisplayOption.h>
#include <GU/GU_PrimCircle.h>
#include <MSS/MSS_SingleOpState.h>
#include <OP/OP_OperatorTable.h>
#include <PRM/PRM_Parm.h>
#include <RE/RE_Render.h>
#include <SOP/SOP_Node.h>
#include <UT/UT_DSOVersion.h>
#define MSS_CLICK_BUTTONS (DM_PRIMARY_BUTTON|DM_SECONDARY_BUTTON)
using namespace HDK_Sample;
void
newModelState(BM_ResourceManager *m)
{
m->registerState(
new PI_StateTemplate("proto_custombrush",
"Custom Brush",
"SOP_proto_custombrush",
(void *)MSS_CustomBrushState::ourConstructor,
MSS_CustomBrushState::ourTemplateList,
PI_VIEWER_SCENE,
PI_NETMASK_SOP,
0));
}
PRM_Template *
MSS_CustomBrushState::ourTemplateList = 0;
BM_State *
MSS_CustomBrushState::ourConstructor(BM_View &view, PI_StateTemplate &templ,
BM_SceneManager *scene)
{
return new MSS_CustomBrushState((JEDI_View &)view, templ, scene);
}
MSS_CustomBrushState::MSS_CustomBrushState(
JEDI_View &view,
PI_StateTemplate &templ,
BM_SceneManager *scene,
char *cursor) : MSS_SingleOpState(view, templ, scene, cursor)
{
myIsBrushVisible = false;
myResizingCursor = false;
GU_PrimCircleParms cparms;
cparms.gdp = &myBrushCursor;
cparms.order = 3;
cparms.imperfect = 0;
cparms.xform.identity();
GU_PrimCircle::build(cparms, GEOPRIMBEZCURVE);
myBrushCursorXform.identity();
myBrushRadius = 0.1;
setViewportMask(DM_VIEWPORT_PERSPECTIVE);
}
MSS_CustomBrushState::~MSS_CustomBrushState()
{
}
const char *
MSS_CustomBrushState::className() const
{
return "MSS_CustomBrushState";
}
int
MSS_CustomBrushState::enter(BM_SimpleState::BM_EntryType how)
{
int result = MSS_SingleOpState::enter(how);
wantsLocates(1);
addClickInterest(MSS_CLICK_BUTTONS);
updatePrompt();
OP_Node *op = getNode();
if(op)
op->setHighlight(0);
return result;
}
void
MSS_CustomBrushState::exit()
{
wantsLocates(0);
removeClickInterest(MSS_CLICK_BUTTONS);
myIsBrushVisible = false;
redrawScene();
MSS_SingleOpState::exit();
}
void
MSS_CustomBrushState::resume(BM_SimpleState *state)
{
MSS_SingleOpState::resume(state);
wantsLocates(1);
addClickInterest(MSS_CLICK_BUTTONS);
updatePrompt();
OP_Node *op = getNode();
if(op)
op->setHighlight(0);
}
void
MSS_CustomBrushState::interrupt(BM_SimpleState *state)
{
wantsLocates(0);
removeClickInterest(MSS_CLICK_BUTTONS);
myIsBrushVisible = false;
redrawScene();
MSS_SingleOpState::interrupt(state);
}
int
MSS_CustomBrushState::handleMouseEvent(UI_Event *event)
{
SOP_Node *sop = (SOP_Node *)getNode();
if (!sop)
return 1;
float t = getTime();
int x = event->state.values[X];
int y = event->state.values[Y];
if (event->reason == UI_VALUE_START &&
(event->state.altFlags & UI_ALT_KEY ||
event->state.altFlags & UI_SHIFT_KEY))
{
myResizeCursorX = x;
myResizeCursorY = y;
myResizingCursor = true;
}
else if (myResizingCursor)
{
fpreal dist = x - myLastCursorX +
y - myLastCursorY;
myBrushRadius *= powf(1.01f, dist);
if (event->reason == UI_VALUE_CHANGED)
myResizingCursor = false;
updateBrush(myResizeCursorX, myResizeCursorY);
}
else if (event->reason == UI_VALUE_LOCATED)
{
updateBrush(x, y);
}
else
{
UT_Vector3 rayorig, dir;
mapToWorld(x, y, dir, rayorig);
bool begin = (event->reason == UI_VALUE_START ||
event->reason == UI_VALUE_PICKED);
if(begin)
beginDistributedUndoBlock("Stroke", ANYLEVEL);
PRM_ParmList *parmlist = sop->getParmList();
PRM_Parm *parm;
parm = parmlist->getParmPtr("origin");
if(parm)
{
parm->setValue(t, rayorig.x(), 0, 0);
parm->setValue(t, rayorig.y(), 0, 1);
parm->setValue(t, rayorig.z(), 0, 2);
}
parm = parmlist->getParmPtr("direction");
if(parm)
{
parm->setValue(t, dir.x(), 0, 0);
parm->setValue(t, dir.y(), 0, 1);
parm->setValue(t, dir.z(), 0, 2);
}
parm = parmlist->getParmPtr("radius");
if(parm)
parm->setValue(t, myBrushRadius);
parm = parmlist->getParmPtr("operation");
if(parm)
{
const char *str = (event->state.values[W] == DM_SECONDARY_BUTTON) ? "erase" : "paint";
parm->setValue(t, UT_String(str), CH_STRING_LITERAL);
}
OP_Context context(t);
parm = parmlist->getParmPtr("event");
bool set_op = false;
if(begin && parm)
{
set_op = true;
parm->setValue(t, UT_String("begin"), CH_STRING_LITERAL);
}
bool active = (event->reason == UI_VALUE_ACTIVE ||
event->reason == UI_VALUE_PICKED);
if (active && parm)
{
if(set_op)
{
sop->getCookedGeo(context);
}
set_op = true;
parm->setValue(t, UT_String("active"), CH_STRING_LITERAL);
}
if (event->reason == UI_VALUE_CHANGED ||
event->reason == UI_VALUE_PICKED)
{
if(parm)
{
if(set_op)
{
sop->getCookedGeo(context);
}
parm->setValue(t, UT_String("end"), CH_STRING_LITERAL);
sop->getCookedGeo(context);
parm->setValue(t, UT_String("nop"), CH_STRING_LITERAL);
}
endDistributedUndoBlock();
}
updateBrush(x, y);
}
myLastCursorX = x;
myLastCursorY = y;
return 1;
}
void
MSS_CustomBrushState::doRender(RE_Render *r, short, short, int ghost)
{
if (!isPreempted() && myIsBrushVisible)
{
r->pushMatrix();
r->multiplyMatrix(myBrushCursorXform);
GR_DisplayOption dopt;
if(ghost)
{
dopt.wireColor() = UT_Color(UT_RGB, 0.625, 0.4, 0.375);
}
else
{
dopt.wireColor() = UT_Color(UT_RGB, 1, 0.1, 0);
}
GR_Detail rgdp;
rgdp.wireDraw(&myBrushCursor, *r, getViewportLOD(), 1 ,
0 , &dopt, false);
r->popMatrix();
}
}
void
MSS_CustomBrushState::updatePrompt()
{
showPrompt("LMB to apply stroke. MMB to erase. Shift-LMB to adjust radius.");
}
void
MSS_CustomBrushState::updateBrush(int x, int y)
{
getViewportItransform(myBrushCursorXform);
UT_Vector3 forward = rowVecMult(UT_Vector4(0, 0, -1, 0), myBrushCursorXform);
UT_Vector3 rayorig, dir;
mapToWorld(x, y, dir, rayorig);
UT_Vector3 delta(1.0 / dot(dir, forward) * dir);
myBrushCursorXform.translate(delta.x(), delta.y(), delta.z());
myBrushCursorXform.prescale(myBrushRadius, myBrushRadius, 1);
myIsBrushVisible = true;
redrawScene();
}