Skip to content

Instantly share code, notes, and snippets.

@lukpazera
Created March 18, 2020 09:12
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save lukpazera/ab2bdd536927ecaf264b3a415ebe939f to your computer and use it in GitHub Desktop.
Save lukpazera/ab2bdd536927ecaf264b3a415ebe939f to your computer and use it in GitHub Desktop.
Tool with handle example. Lifted from sample code posted by Mario Baldi on MODO's #coding Slack channel. I have not tested this code, saving for future reference.
#include <lx_tool.hpp>
#include <lx_toolui.hpp>
#include <lx_vmodel.hpp>
#include <lx_vector.hpp>
#include <lxu_attributes.hpp>
#include <lx_plugin.hpp>
#include <lx_layer.hpp>
#include <lx_mesh.hpp>
#include <lx_log.hpp>
#include <lxu_vector.hpp>
#include <lx_draw.hpp>
#include <lx_handles.hpp>
#include <string>
using namespace std;
#define MB_TOOL_SERVER_NAME "mb.toolTest"
#define ATTRs_FACTOR "factor"
#define ATTRa_FACTOR 0
#define ATTRa_TOTAL 1
// OGL PART ID
#define MBTOOL_HANDLE_DRAG 10000
//
#define HANDLE_DRAG_SIZE 20
namespace SimpleTool {
enum ePacketOffset
{
ePacketOffset_ViewEvent, // Viewport
ePacketOffset_Input, // Input Event
ePacketOffset_Screen, // Screen Event
ePacketOffset_Event, // Event Trans
ePacketOffset_Subject2, // Gives access to the layerScan
ePacketOffset_MAX
};
class BrushTool :
public CLxImpl_Tool,
public CLxImpl_ToolModel,
public CLxDynamicAttributes
{
public:
BrushTool();
static void initialize();
void tool_Reset() LXx_OVERRIDE;
LXtObjectID tool_VectorType() LXx_OVERRIDE;
const char * tool_Order() LXx_OVERRIDE;
LXtID4 tool_Task() LXx_OVERRIDE;
void tool_Evaluate(ILxUnknownID vts) LXx_OVERRIDE;
unsigned tmod_Flags() LXx_OVERRIDE;
void tmod_Initialize(ILxUnknownID vts, ILxUnknownID adjust, unsigned flags) LXx_OVERRIDE;
// Handles
void tmod_Draw(ILxUnknownID vts, ILxUnknownID stroke, int flags) LXx_OVERRIDE;
void tmod_Test(ILxUnknownID vts, ILxUnknownID stroke, int flags) LXx_OVERRIDE;
LxResult tmod_Down(ILxUnknownID vts, ILxUnknownID adjust) LXx_OVERRIDE;
void tmod_Move(ILxUnknownID vts, ILxUnknownID adjust) LXx_OVERRIDE;
void tmod_Up(ILxUnknownID vts, ILxUnknownID adjust) LXx_OVERRIDE;
void drawHandle(ILxUnknownID vts, ILxUnknownID stroke, bool hittest);
CLxUser_LayerService s_layer;
CLxUser_VectorType v_type;
unsigned mode_select;
//static LXtTagInfoDesc descInfo[];
private:
unsigned int mPacketOffsets[ePacketOffset_MAX];
LXtVector m_handle_root; // Dummy var used by epkt.HitHandle()
double m_handle_root_init_x, m_handle_root_init_y; // This stores the initial position of the handle
double m_handle_root_x, m_handle_root_y; // This stores the current position of the handle.
};
/*
* On create we add our one tool attribute. We also allocate a vector type
* (which doesn't seem to need anything in it!), the falloff packet offset
* and select mode mask.
*/
BrushTool::BrushTool()
{
CLxUser_PacketService sPkt;
CLxUser_MeshService sMesh;
// Add Attributes
// In this example, this is just a dummy attribute to be able
// to re-draw the handle while moving the mouse.
dyna_Add(ATTRs_FACTOR, LXsTYPE_PERCENT);
// Set Default
attr_SetFlt(ATTRa_FACTOR, 0.0); // factor
// Mode Select
mode_select = sMesh.SetMode("select");
// Packet Offsets
for (unsigned int i = 0; i < ePacketOffset_MAX; i++)
{
mPacketOffsets[i] = 0;
}
CLxUser_PacketService pktSvc;
if (true == sPkt.NewVectorType(LXsCATEGORY_TOOL, v_type))
{
if (LXx_OK(sPkt.AddPacket(v_type, LXsP_TOOL_VIEW_EVENT, LXfVT_GET)))
mPacketOffsets[ePacketOffset_ViewEvent] = sPkt.GetOffset(LXsCATEGORY_TOOL, LXsP_TOOL_VIEW_EVENT);
if (LXx_OK(sPkt.AddPacket(v_type, LXsP_TOOL_INPUT_EVENT, LXfVT_GET)))
mPacketOffsets[ePacketOffset_Input] = sPkt.GetOffset(LXsCATEGORY_TOOL, LXsP_TOOL_INPUT_EVENT);
if (LXx_OK(sPkt.AddPacket(v_type, LXsP_TOOL_SCREEN_EVENT, LXfVT_GET)))
mPacketOffsets[ePacketOffset_Screen] = sPkt.GetOffset(LXsCATEGORY_TOOL, LXsP_TOOL_SCREEN_EVENT);
if (LXx_OK(sPkt.AddPacket(v_type, LXsP_TOOL_EVENTTRANS, LXfVT_GET)))
mPacketOffsets[ePacketOffset_Event] = sPkt.GetOffset(LXsCATEGORY_TOOL, LXsP_TOOL_EVENTTRANS);
if (LXx_OK(pktSvc.AddPacket(v_type, LXsP_TOOL_SUBJECT2, LXfVT_GET)))
mPacketOffsets[ePacketOffset_Subject2] = pktSvc.GetOffset(LXsCATEGORY_TOOL, LXsP_TOOL_SUBJECT2);
}
}
/*
* Reset sets the attributes back to defaults.
*/
void
BrushTool::tool_Reset()
{
attr_SetFlt(ATTRa_FACTOR, 0.0); // factor
}
/*
* Boilerplate methods that identify this as an action (state altering) tool.
*/
LXtObjectID
BrushTool::tool_VectorType()
{
return v_type.m_loc; // peek method; does not add-ref
}
const char *
BrushTool::tool_Order()
{
return LXs_ORD_ACTR;
}
LXtID4
BrushTool::tool_Task()
{
return LXi_TASK_ACTR;
}
unsigned
BrushTool::tmod_Flags()
{
// http://modo.sdk.thefoundry.co.uk/wiki/Vmodel_(lx-vmodel.hpp)#.2811.29_SDK:_Declarations
// Enable Draw and Test functions, in screen coords.
// For 3D coords, use LXfTMOD_DRAW_3D
return LXfTMOD_DRAW_PIXEL |
// Enable Left and Right click,
// thus enabling calls to tmod_Down, tmod_Move, and tmod_Up
LXfTMOD_I0_INPUT | LXfTMOD_I1_INPUT;
}
void
BrushTool::tmod_Initialize(
ILxUnknownID vts,
ILxUnknownID adjust,
unsigned int flags)
{
CLxUser_AdjustTool at(adjust);
// Initialize our handle position
m_handle_root_x = 100;
m_handle_root_y = 100;
m_handle_root_init_x = 100;
m_handle_root_init_y = 100;
}
LxResult
BrushTool::tmod_Down(
ILxUnknownID vts,
ILxUnknownID adjust)
{
CLxUser_AdjustTool at(adjust); // Not used in this example.
CLxUser_VectorStack vec(vts);
CLxUser_EventTranslatePacket
epkt;
LXpToolInputEvent *ipkt;
// The Input tool packet is useful to know which key has been pressed,
// or what part (i.e. handle) is below the cursor.
ipkt = (LXpToolInputEvent *)vec.Read(mPacketOffsets[ePacketOffset_Input]);
// The event translation interface performs the translation of
// 'raw' 2D input events to 'cooked' 3D events.
vec.ReadObject(mPacketOffsets[ePacketOffset_Event], epkt);
// Check that the drawn part below the mouse is our handle.
if (ipkt->part == MBTOOL_HANDLE_DRAG) {
// Left Mouse Click check
if (ipkt->input == 0) {
LXtVector pos;
// The client establishes the handle that will be manipulated with this function,
// usually during the mouse down event.
// To be honest, I'm not sure I need to do this, but I'm following these instructions:
// https://community.foundry.com/discuss/topic/120358/c-sdk-problem-with-offset-of-mouse-cursor-not-updating-during-tool-interaction
// With or without this instruction though, I still have the bug where the mouse pops back at the screen borders
epkt.HitHandle(vts, m_handle_root);
}
}
// If I want to continue the event and pass to tmod_Move,
// I need to return LXe_TRUE
return LXe_TRUE;
}
void
BrushTool::tmod_Move(
ILxUnknownID vts,
ILxUnknownID adjust)
{
CLxUser_AdjustTool at(adjust);
CLxUser_VectorStack vec(vts);
CLxUser_EventTranslatePacket
epkt;
LXtVector pos, delta, vec1;
double scl;
// The Input tool packet is useful to know which key has been pressed,
// or what part (i.e. handle) is below the cursor.
LXpToolInputEvent *ipkt;
ipkt = (LXpToolInputEvent *)vec.Read(mPacketOffsets[ePacketOffset_Input]);
// Screen positions packet, useful to get mouse coords.
LXpToolScreenEvent *spkt;
spkt = (LXpToolScreenEvent *)vec.Read(mPacketOffsets[ePacketOffset_Screen]);
// The CLxUser_EventTranslatePacket translates screen inputs to 3d positions
vec.ReadObject(mPacketOffsets[ePacketOffset_Event], epkt);
// Handle Part hittest
if (ipkt->part == MBTOOL_HANDLE_DRAG) {
// Left Mouse Click check
if (ipkt->input == 0) {
LXtVector pos;
// This function lets the client find the handle position on mouse move events.
// Not sure if I need it at all, but following these instructions:
// https://community.foundry.com/discuss/topic/120358/c-sdk-problem-with-offset-of-mouse-cursor-not-updating-during-tool-interaction
epkt.GetNewPosition(vts, pos);
m_handle_root_x = spkt->fcx - spkt->fpx + m_handle_root_init_x;
m_handle_root_y = spkt->fcy - spkt->fpy + m_handle_root_init_y;
// It is also necessary to modify a modo attribute to redraw the handle.
// In this case this tool isn't doing anything, so this attribute is just a dummy.
at.SetFlt(ATTRa_FACTOR, m_handle_root_x);
}
}
}
void
BrushTool::tmod_Up(
ILxUnknownID vts,
ILxUnknownID adjust)
{
// Update the init position of our handle
m_handle_root_init_x = m_handle_root_x;
m_handle_root_init_y = m_handle_root_y;
}
void
BrushTool::tmod_Draw(
ILxUnknownID vts,
ILxUnknownID stroke,
int flags)
{
drawHandle(vts, stroke, false);
}
/*
* "Test" is called to hit test the item.
* If "Test" is not implemented, "Draw" method is called for hit testing.
*/
void
BrushTool::tmod_Test(
ILxUnknownID vts,
ILxUnknownID stroke,
int flags)
{
drawHandle(vts, stroke, true);
}
// Custom function to draw the handle
void BrushTool::drawHandle(
ILxUnknownID vts,
ILxUnknownID stroke,
bool hittest)
{
CLxUser_VectorStack vec(vts);
CLxUser_View view(stroke);
CLxUser_StrokeDraw draw(stroke);
// This is a check to filter calls to the draw/test functions.
// It is probably unneeded in this case, but this is a good example to show how to distinguish
// between calls made in 3d coords (LXi_VIEWTYPE_3D) and screen coords (LXiVIEWv_PIXEL)
LXpToolViewEvent *viewEvent = (LXpToolViewEvent *)vec.Read(mPacketOffsets[ePacketOffset_ViewEvent]);
if (!viewEvent || (viewEvent->type != LXiVIEWv_PIXEL))
return;
// cyan color on "draw"
LXtVector color = { 0.4, 1.0, 1.0 };
if (hittest) {
// yellow color on "test"
color[0] = 1.0;
color[1] = 1.0;
color[2] = 0.4;
};
// Draw a simple quad, and set its part.
// The part of the drawn handle is very important, as it will be used in the down/move/up methods
// to filter various handles you may have.
draw.SetPart(MBTOOL_HANDLE_DRAG);
LXtVector2 outline_a = { m_handle_root_x - HANDLE_DRAG_SIZE, m_handle_root_y - HANDLE_DRAG_SIZE * 0.5 };
LXtVector2 outline_b = { m_handle_root_x - HANDLE_DRAG_SIZE, m_handle_root_y + HANDLE_DRAG_SIZE * 0.5 };
LXtVector2 outline_c = { m_handle_root_x , m_handle_root_y + HANDLE_DRAG_SIZE * 0.5 };
LXtVector2 outline_d = { m_handle_root_x , m_handle_root_y - HANDLE_DRAG_SIZE * 0.5 };
draw.BeginW(LXiSTROKE_QUADS, color, 1.0, 0.0);
draw.Vert(outline_a[0], outline_a[1], LXiSTROKE_ABSOLUTE);
draw.Vert(outline_b[0], outline_b[1], LXiSTROKE_ABSOLUTE);
draw.Vert(outline_c[0], outline_c[1], LXiSTROKE_ABSOLUTE);
draw.Vert(outline_d[0], outline_d[1], LXiSTROKE_ABSOLUTE);
}
/*
* NOT USED.
*/
void
BrushTool::tool_Evaluate(
ILxUnknownID vts)
{
}
/*
* Export tool server and its log info block.
*/
void
BrushTool::initialize()
{
CLxGenericPolymorph *srv;
srv = new CLxPolymorph<BrushTool>;
srv->AddInterface(new CLxIfc_Tool <BrushTool>);
srv->AddInterface(new CLxIfc_ToolModel <BrushTool>);
srv->AddInterface(new CLxIfc_Attributes<BrushTool>);
srv->AddInterface(new CLxIfc_AttributesUI<BrushTool>);
//srv->AddInterface(new CLxIfc_StaticDesc<BrushTool>); // Enables descInfo. You'll need this for a tool operator.
thisModule.AddServer(MB_TOOL_SERVER_NAME, srv);
}
}
void initialize() {
SimpleTool::BrushTool::initialize();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment