Created
March 18, 2020 09:12
-
-
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| |
#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