gameInputDevice/gameInputDevice.cpp

gameInputDevice/gameInputDevice.cpp
//-
// ==========================================================================
// Copyright 2011 Autodesk, Inc. All rights reserved.
//
// Use of this software is subject to the terms of the Autodesk
// license agreement provided at the time of installation or download,
// or which otherwise accompanies this software in either electronic
// or hard copy form.
// ==========================================================================
//+
// Description:
// This example demonstrates how to use left pad X and Y from
// a game input device to modify the translate attribute of a node. The
// MEL example code demonstrates how a poly cube can be moved
// in X and Y.
//
// There is an attribute updateTranslateXZ that controls if
// the example will map game input(x,y) to Maya(x,0,z) or
// Maya(x,y,0).
//
// NOTE: Windows only example that requires the Direct X
// SDK to be installed in order to build. In addition, the
// game input device driver must be installed also.
//
/*
// MEL:
loadPlugin gameInputDevice;
string $node = `createNode gameInputDevice`;
string $cube[] = `polyCube`;
connectAttr ( $node + ".outputTranslate" ) ( $cube[0] + ".translate" );
setAttr ( $node + ".live" ) 1;
*/
#ifdef _WIN32
#ifndef IPV6STRICT
#define IPV6STRICT
#endif
#include <winsock2.h>
#include <windows.h>
#include <XInput.h>
#pragma comment(lib, "XInput.lib")
#endif // _WIN32
#include <maya/MFnPlugin.h>
#include <maya/MTypeId.h>
#include <api_macros.h>
#include <maya/MIOStream.h>
#include <maya/MPlug.h>
#include <maya/MDataBlock.h>
#include <maya/MFnNumericAttribute.h>
#include <maya/MPxThreadedDeviceNode.h>
class gameInputDeviceNode : public MPxThreadedDeviceNode
{
public:
gameInputDeviceNode();
~gameInputDeviceNode() override;
void postConstructor() override;
MStatus compute( const MPlug& plug, MDataBlock& data ) override;
void threadHandler() override;
void threadShutdownHandler() override;
static void* creator();
static MStatus initialize();
public:
static MObject outputTranslate;
static MObject outputTranslateX;
static MObject outputTranslateY;
static MObject outputTranslateZ;
// Boolean attribute for deciding if we are going
// to update XZ or XY. Only two inputs from the
// gameInput in this example
static MObject updateTranslateXZ;
static MTypeId id;
private:
};
MTypeId gameInputDeviceNode::id( 0x00081053 );
MObject gameInputDeviceNode::outputTranslate;
MObject gameInputDeviceNode::outputTranslateX;
MObject gameInputDeviceNode::outputTranslateY;
MObject gameInputDeviceNode::outputTranslateZ;
MObject gameInputDeviceNode::updateTranslateXZ;
gameInputDeviceNode::gameInputDeviceNode()
{}
gameInputDeviceNode::~gameInputDeviceNode()
{
destroyMemoryPools();
}
void gameInputDeviceNode::postConstructor()
{
MObjectArray attrArray;
attrArray.append( gameInputDeviceNode::outputTranslate );
setRefreshOutputAttributes( attrArray );
// we'll be reading one set of translate x,y, z's at a time
createMemoryPools( 24, 3, sizeof(double));
}
#ifdef _WIN32
bool checkController(XINPUT_STATE& state)
{
DWORD dwResult;
ZeroMemory( &state, sizeof(XINPUT_STATE) );
dwResult = XInputGetState( 0, &state );
if( dwResult == ERROR_SUCCESS )
{
return true;
}
return false;
}
#endif
void gameInputDeviceNode::threadHandler()
{
#ifdef _WIN32
MStatus status;
setDone( false );
while ( ! isDone() )
{
if ( ! isLive() )
continue;
MCharBuffer buffer;
status = acquireDataStorage(buffer);
if ( ! status )
continue;
XINPUT_STATE state;
if ( ! checkController( state ) )
{
releaseDataStorage(buffer);
continue;
}
beginThreadLoop();
{
float changeX = 0.0, changeY = 0.0;
// Making sure we are not in the deadzone, we find
// relative movement values. A very simple approach
float leftThumbX = state.Gamepad.sThumbLX;
float leftThumbY = state.Gamepad.sThumbLY;
if ( leftThumbX > +XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE ) changeX = +1;
if ( leftThumbX < -XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE ) changeX = -1;
if ( leftThumbY > +XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE ) changeY = +1;
if ( leftThumbY < -XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE ) changeY = -1;
double* doubleData = reinterpret_cast<double*>(buffer.ptr());
doubleData[0] = changeX ; doubleData[1] = changeY; doubleData[2] = 0.0;
pushThreadData( buffer );
}
endThreadLoop();
}
#endif // _WIN32
setDone( true );
}
void gameInputDeviceNode::threadShutdownHandler()
{
setDone( true );
}
void* gameInputDeviceNode::creator()
{
return new gameInputDeviceNode;
}
MStatus gameInputDeviceNode::initialize()
{
MStatus status;
outputTranslateX = numAttr.create("outputTranslateX", "otx", MFnNumericData::kDouble, 0.0, &status);
MCHECKERROR(status, "create outputTranslateX");
outputTranslateY = numAttr.create("outputTranslateY", "oty", MFnNumericData::kDouble, 0.0, &status);
MCHECKERROR(status, "create outputTranslateY");
outputTranslateZ = numAttr.create("outputTranslateZ", "otz", MFnNumericData::kDouble, 0.0, &status);
MCHECKERROR(status, "create outputTranslateZ");
outputTranslate = numAttr.create("outputTranslate", "ot", outputTranslateX, outputTranslateY,
outputTranslateZ, &status);
MCHECKERROR(status, "create outputTranslate");
updateTranslateXZ = numAttr.create( "updateTranslateXZ", "uxz", MFnNumericData::kBoolean);
CHECK_MSTATUS( numAttr.setKeyable(true) );
CHECK_MSTATUS( numAttr.setStorable(true) );
CHECK_MSTATUS( numAttr.setHidden(false) );
CHECK_MSTATUS( numAttr.setDefault(true) );
ADD_ATTRIBUTE(outputTranslate);
ADD_ATTRIBUTE(updateTranslateXZ);
ATTRIBUTE_AFFECTS( live, outputTranslate);
ATTRIBUTE_AFFECTS( frameRate, outputTranslate);
ATTRIBUTE_AFFECTS( updateTranslateXZ, outputTranslate);
return MS::kSuccess;
}
MStatus gameInputDeviceNode::compute( const MPlug& plug, MDataBlock& block )
{
MStatus status;
if( plug == outputTranslate || plug == outputTranslateX ||
plug == outputTranslateY || plug == outputTranslateZ )
{
// Find the type of translation we will be doing
bool xzUpdate = block.inputValue( updateTranslateXZ ).asBool();
// Access the data and update the output attribute
MCharBuffer buffer;
if ( popThreadData(buffer) )
{
// Relative data coming in
double* doubleData = reinterpret_cast<double*>(buffer.ptr());
MDataHandle outputTranslateHandle = block.outputValue( outputTranslate, &status );
MCHECKERROR(status, "Error in block.outputValue for outputTranslate");
double3& outputTranslate = outputTranslateHandle.asDouble3();
if ( xzUpdate )
{
// XZ
outputTranslate[0] += doubleData[0];
outputTranslate[1] += doubleData[2];
outputTranslate[2] -= doubleData[1];
}
else
{
// XY
outputTranslate[0] += doubleData[0];
outputTranslate[1] += doubleData[1];
outputTranslate[2] += doubleData[2];
}
block.setClean( plug );
releaseDataStorage(buffer);
return ( MS::kSuccess );
}
else
{
return MS::kFailure;
}
}
return ( MS::kUnknownParameter );
}
MStatus initializePlugin( MObject obj )
{
MStatus status;
MFnPlugin plugin(obj, PLUGIN_COMPANY, "3.0", "Any");
status = plugin.registerNode( "gameInputDevice",
gameInputDeviceNode::id,
gameInputDeviceNode::creator,
gameInputDeviceNode::initialize,
if( !status ) {
status.perror("failed to registerNode gameInputDeviceNode");
}
return status;
}
MStatus uninitializePlugin( MObject obj )
{
MStatus status;
MFnPlugin plugin(obj);
status = plugin.deregisterNode( gameInputDeviceNode::id );
if( !status ) {
status.perror("failed to deregisterNode gameInputDeviceNode");
}
return status;
}