VR Dev – Part 2: Integrate Razer Hydra into UDK

Posted: February 24, 2013 in Hydra support in UDK, VR Dev
Tags: , , , ,

Okay, now UDK and Notepad++ are set up. The next step is to create and compile some scripts to get the Hydra integrated!

This section was pieced together from reading the Sixense UDK Dev Forums and a couple of posts from the Beyond Unreal forums.

First, we need to a script to bind the Sixense DLL to the UDK project. Within the ../Development/Src/HydraGame/Classes/ folder, create a file named Sixense.uc with the following code:

*Note: If copy/pasting any of the following code, you will receive an error because the ( ‘ ) and ( ” ) characters are screwed up by WordPress. You will need to be find/replace them in all of these source files. I am trying to find a solution for this issue.

Sixense.uc

class Sixense extends Object
DLLBind(sixense);

//layout of the basis vectors
//unreal space X axis = forward Y axis = Right Z axis = up
//sixense space X axis = Right Y axis = Up Z axis = back
// Sx2Un X = -Z Y = X Z = Y
//Un2Sx X = Y Y = Z Z = -X

// Bit masks. 0x01 >> 2^x = y
const SIXENSE_BUTTON_BUMPER = 128; //(0x01<<7)
const SIXENSE_BUTTON_JOYSTICK = 256; //(0x01<<8)
const SIXENSE_BUTTON_1 = 32; //(0x01<<5);
const SIXENSE_BUTTON_2 = 64; //(0x01<<6);
const SIXENSE_BUTTON_3 = 8; //(0x01<<3);
const SIXENSE_BUTTON_4 = 16; //(0x01<<4);
const SIXENSE_BUTTON_START = 1; //(0x01<<0);

// Not sure where this is used… log files?
const SIXENSE_SUCCESS = 0;
const SIXENSE_FAILURE = -1;

// 4 controllers at once? Wicked!
const SIXENSE_MAX_CONTROLLERS = 4;

// 3D position, rotation matrices, joystick, trigger, and button data
struct sixenseControllerData {
var float pos[3];
var float rot_mat[9];
var float joystick_x;
var float joystick_y;
var float trigger;
var int buttons;
var byte sequence_number;
var float rot_quat[4];
var byte firmware_revision[2];
var byte hardware_revision[2];
var byte packet_type[2];
var byte magnetic_frequency[2];
var int enabled;
var int controller_index;
var byte is_docked;
var byte which_hand;
var byte hemi_tracking_enabled;
};

//Sets up controller data for all controllers?
struct sixenseAllControllerData {
var sixenseControllerData controller[4];
};

// declares an object that you’ll use later when you want to display data in ClientMessage
var sixenseAllControllerData TheControllerData;

// importing functions from sixense.dll
dllimport final function int sixenseInit( );
dllimport final function int sixenseExit( );

dllimport final function int sixenseGetMaxBases();
dllimport final function int sixenseSetActiveBase( int i );
dllimport final function int sixenseIsBaseConnected( int i );

dllimport final function int sixenseGetMaxControllers( );
dllimport final function int sixenseIsControllerEnabled( int which );
dllimport final function int sixenseGetNumActiveControllers( );

dllimport final function int sixenseGetHistorySize();

dllimport final function int sixenseGetData( int which, int index_back, out sixenseControllerData data );
dllimport final function int sixenseGetAllData( int index_back, out sixenseAllControllerData data );
dllimport final function int sixenseGetNewestData( int which, out sixenseControllerData data );
dllimport final function int sixenseGetAllNewestData( out sixenseAllControllerData data );

dllimport final function int sixenseSetHemisphereTrackingMode( int which_controller, int state );
dllimport final function int sixenseGetHemisphereTrackingMode( int which_controller, out int state );

dllimport final function int sixenseAutoEnableHemisphereTracking( int which_controller );

dllimport final function int sixenseSetHighPriorityBindingEnabled( int on_or_off );
dllimport final function int sixenseGetHighPriorityBindingEnabled( out int on_or_off );

dllimport final function int sixenseTriggerVibration( int controller_id, int duration_100ms, int pattern_id );

dllimport final function int sixenseSetFilterEnabled( int on_or_off );
dllimport final function int sixenseGetFilterEnabled( out int on_or_off );

dllimport final function int sixenseSetFilterParams( float near_range, float near_val, float far_range, float far_val );
dllimport final function int sixenseGetFilterParams( out float near_range, out float near_val, out float far_range, out float far_val );

dllimport final function int sixenseSetBaseColor( byte red, byte green, byte blue );
dllimport final function int sixenseGetBaseColor( out byte red, out byte green, out byte blue );

/*NOTE
The C++ DLL stores it’s matrix data as a 2D Array but Unreal must address it with one index. Use the macro below to address it more naturally

EX: Memory layout of 3×3 rotation matrices
C++: rot_mat[0][0], rot_mat[0][1], rot_mat[0][2], rot_mat[1][0], rot_mat[1][1], rot_mat[1][2], rot_mat[2][0], rot_mat[2][1], rot_mat[2][2]
Unreal: 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8
******************/
// mathematical conversion: 3x + y = U
//macro for accessing a particular 3×3 matrix element using two indices
//column major
// what does ` do? It is the grave accent (the one below tilde)? Necessary for log output, other operations.
`define m33el(x, y) `y + `x * 3

In the same folder, create the game file scripts. “HydraGame” can be replaced with whatever the name of your game is. Filenames and code below!

HydraGame.uc

//standard bare bones “MyGame” file

class HydraGame extends FrameworkGame;

defaultproperties
{
PlayerControllerClass=class’HydraGamePlayerController’
bDelayedStart=false
}

HydraGamePlayerController.uc

class HydraGamePlayerController extends GamePlayerController;

var sixense TheSixense;

defaultproperties

{
InputClass=class’HydraGame.HydraGamePlayerInput’
}

simulated event PostBeginPlay()
{
super.PostBeginPlay();
TheSixense = new class’Sixense’;
TheSixense.sixenseInit();
TheSixense.sixenseGetAllNewestData(TheSixense.TheControllerData);
}

simulated event PlayerTick(float DeltaTime)
{
super.Tick(DeltaTime);
TheSixense.sixenseGetAllNewestData(TheSixense.TheControllerData);
}

HydraGamePlayerInput.uc

//setup in-game client messages that will verify Hydra input. Use ini to bind this function to “H”

class HydraGamePlayerInput extends PlayerInput within HydraGamePlayerController;

simulated exec function TestFunction1()
{
ClientMessage(“Sixense L button value: “$TheSixense.TheControllerData.controller[0].buttons);
ClientMessage(“Sixense L position 0: “$TheSixense.TheControllerData.controller[0].pos[0]);
ClientMessage(“Sixense L position 1: “$TheSixense.TheControllerData.controller[0].pos[1]);
ClientMessage(“Sixense L position 2: “$TheSixense.TheControllerData.controller[0].pos[2]);

}

Okay, basic script files are all done! A couple more things to do before compiling scripts and diving into the UDK Editor.

.ini file editing

Open ../UDKGame/Config/DefaultInput.ini, and add the following code to ‘Primary default bindings’

.Bindings=(Name=”H”,Command=”TestFunction1″)

Open ../UDKGame/Config/DefaultEngine.ini, and add the following code to the [UnrealEd.EditorEngine] section:

+EditPackages=HydraGame

Open ../UDKGame/Config/DefaultGame.ini, and add the following code to the [Engine.GameInfo] section:

DefaultGame=HydraGame.HydraGame
DefaultServerGame=UDKBase.SimpleGame
PlayerControllerClassName=HydraGame.HydraGamePlayerController
GameDifficulty=+1.0
MaxPlayers=32
DefaultGameType=”UTGame.UTDeathmatch”;
+DefaultMapPrefixes=(Prefix=”DM”,bUsesCommonPackage=FALSE,GameType=”UTGame.UTDeathmatch”)
+DefaultMapPrefixes=(Prefix=”CTF”,bUsesCommonPackage=FALSE,GameType=”UTGameContent.UTCTFGame_Content”)
+DefaultMapPrefixes=(Prefix=”VCTF”,bUsesCommonPackage=FALSE,GameType=”UTGameContent.UTVehicleCTFGame_Content”)
+DefaultMapPrefixes=(Prefix=”HG”,bUsesCommonPackage=FALSE,GameType=”HydraGame.HydraGame”)

Now “HydraGame” will be set up as a game type, map file names prefaced with “HG-” will be recognized as HydraGame maps, and pressing “H” during gameplay brings up some data about the Hydra.

Delete the files UDKGame.ini, UDKEngine.ini, and UDKInput.ini. They will be recreated when the UDKEditor is started up.

Compile Scripts, Open the Editor!

For easy access, I made desktop shortcuts for the UDKEditor and the UnrealFrontEnd – I end up opening and closing these two programs quite a bit. First we need UFE (UnrealFrontEnd.exe), which is located in the Binaries folder. Open it up, click on the Scripts icon and select “compile scripts.” F4 is the keyboard shortcut.

Once the scripts are compiled, we can finally get into the Editor!  Create a shortcut from ../Binaries/Win32/UDK.exe. Add “Editor -log” to the command line in the shortcut properties. Name the shortcut “HydraGame UDK Editor”. Open it.

As a test, save the default level as “HG-Level01.udk” to ../UDKGame/Content/HydraGame/Levels. Build the level (Build>Build All).

Finally, we need to make sure that the game type for the PIE (Play-In-Editor) is “HydraGame”. Gotta go to World Properties (View>World Properties) and expand the “Game Type” section. Change Game Type for PIE and Default Game Type to HydraGame.

Play the game in the editor (Alt+F8) and repeatedly press H to get data from the Hydra. I move the left controller around and press all the buttons… Success!

Now to learn more about the Editor and connect the Hydra to the UDK Pawn! I think I will continue with the tutorials from The New Boston, and peruse more posts from Nathaniel3W!

Advertisements
Comments
  1. Ideo says:

    Hi, first i want to thank you for this tutorial, but i have a problem when i want to compile Scripts
    i have this error message :

    E:\UDK\UDK-2012-07\Development\Src\HydraGame\Classes\HydraGamePlayerController.uc(9) : Error, ‘new’: Class ‘HydraGamePlayerController’ is an Actor subclass, use ‘Spawn’ instead
    Compile aborted due to errors.
    Warning/Error Summary

    PS : I change every ‘ and “

  2. Rowan de Graaf says:

    Hey Cymatic,

    Scrolling trough the blog as I saw this.

    whenver you post in WordPress HTML view instead of the easy editor, you can give an html code for the proper “and ‘ and not the crooked ones.

  3. Anonymous says:

    There is an Error in your PlayerTick event that cost me a while. It should be:

    simulated event PlayerTick(float DeltaTime)
    {
    super.PlayerTick(DeltaTime);
    TheSixense.sixenseGetAllNewestData(TheSixense.TheControllerData);
    }

    Regards,
    Tom

  4. Tom says:

    There is an Error in your PlayerTick event that cost me a while. It should be:

    simulated event PlayerTick(float DeltaTime)
    {
    super.PlayerTick(DeltaTime);
    TheSixense.sixenseGetAllNewestData(TheSixense.TheControllerData);
    }

    Regards,
    Tom

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s