Aug
09
2009
0

Catalyst

Catalyst Title Screen

Whilst working on animation support for Athena for Courage, I began working on a game for the iPhone called Catalyst last weekend.

I was thinking about a game I had played about a month or two before called Polygonal Fury, and realised it would port nicely onto the iPhone.

Catalyst - A circle explodesSo I started a OpenGL ES project (and discovered the new version of the iPhone supports shaders), and started with just making some circles bounce around.

Since the collision of a circle is easy, they seems the easiest to setup for interaction with touching the screen.

Catalyst - A chain reactionInitially I just made them “die” if tapped, but later I added the ability of shapes to spawn reactions.

All sprites within the game are triangle fans, but I will probably change them to quads with a texture due to the break that occasionally appears in the geometry, and alpha glow around the edge.

Catalyst - Still three "clicks" leftIt is not much, but it is a start, I plan to add some more particle types, and maybe a few different upgrades than the ones giving in the Flash version.

The background needs changing, I was thinking of something going on in the background like in another Flash game I played, Spewer, with maybe cut scenes like the Nintendo DS game Contact had.

I’ll probably request for it to be added to the app store in a week or two, free to download of course.

Written by Kaluriel in: Catalyst | Tags: , , ,
Jul
01
2009
0

Athena: Wiimote and Mac OS X

Wiimote USB DrivesSince solving the problem of the rumbling on the XBox 360 pad, I’ve grown bored of it, and decided to branch out trying to get more controllers working in my engine.

So a few days ago I started with the Wiimote, I had planned to try to the PS3 controller but I didn’t have one, so borrowing one of my housemates Wiimotes I set to work.

There is quite a lot of documentation on the Wiimote at the moment, whether it is the correct way to do things is questionable, since there are cases like “1? – it is set to 1 by the Wii” and “set this to 0×55 at memory address X and set to 0×00 at memory address Y to enable it”.

I started with website called The Wiimote Project, but as their documentation grew scarce I found a better site (which it seemed to copy it all from anyways) called WiiBrew. The WiiBrew wiki is quite comprehensive and within a few minutes I already had the LEDs flashing.

I won’t go into a lot of detail, I’ll just show the basics that I’ve got to grips with from the documentation on WiiBrew – LEDs + Motor, Buttons, and Battery.

Connecting the Wiimote

Connecting the Wiimote to a Mac is far simpler than it is for XBox 360, for a start its a Bluetooth device that operates using the normal protocols and it doesn’t require a key to activate. To connect, first turn on Bluetooth on your Mac, and then select “Set up Bluetooth Device…“.

Follow on the onscreen instructions, and when it gets to the point where it is searching for nearby Bluetooth devices, press 1 + 2 to soft synchronize if the Wiimote isn’t bound to another device, or the sync button to hard synchronize (unbinds the Wiimote from anything it remembers).

When it appears, select it and click the “Passkey Options…” button. Select the “Do not use a passkey with this device” option and then OK. Continue on and the device will be setup and connected. You can’t actually tell since the LEDs are still blinking, but if you have done it correctly, the Bluetooth icon should be different, or the menu will have a disconnect option for the device just setup.

Written by Kaluriel in: Athena, Code, Tutorials | Tags: , , , , , , , ,

Pages: 1 2 3 4


Jun
27
2009
0

Athena: XBox 360 Pad and Mac OS X (cont further)

Steampunk LincolnThe past couple of week’s I’ve really gotten into writing code that communicates with gaming devices (the Mac is limited in this part of itself).

One annoyance I had was with getting rumbling working on the XBox 360 pad, eventually after essentially pinging all messages, I found and worked out the format for the rumble message, which was annoyingly nothing like the others listed.

Well after that I got kinda bored with the 360 and decided to turn towards another console controller, the Wiimote. And I thought I’d write my own driver.

Long story short, when I was having trouble getting my driver working, I looked at the code for the driver for Tattiebogle’s XBox 360 pad driver, only to find out that it has redefined the rumble message to it’s own format, namely the 4 byte one, only to forward on the settings to the 360 pad.

Well I’m glad I figured out why the message was different, kinda annoying that wasn’t in his FAQ. Hopefully others will find this post and go “ohhhhhhhh” like I did when seeing the code.

As for the Wiimote, writing a driver for a Bluetooth device I’ve decided doesn’t make sense, the device is already designed to communicate with another bluetooth device, so I made a class instead, using a Bluetooth API and documentation I found on WiiBrew.

I’ll hopefully post my Xcode project for testing the 360 pad in a few days. And I’ll probably write up my Wiimote stuff once I get it working the way I want. I’m really tempted to buy lots of cheap USB and Bluetooth devices on eBay now just to see if I can write drivers for them.

Jun
26
2009
0

Athena: XBox 360 Pad and Mac OS X (cont)

XBox360 Actuator RespondsIn my previous entry, Athena: XBox 360 Pad and Mac OS X, I began work on getting the XBox 360 pad working in my game engine. I also said that I’d post how to change the LEDs and get rumble working when I figured it out.

I had no trouble with getting the LEDs flashing, but for some reason the rumble actuators just would not respond to my message with success.

I had searched at all other documentation on the pad on the internet, but although I found two variations of rumbling, neither worked.

It has taken my a week or two, but then I got the inspiration to just fire all possible messages to the pad, nullified apart from the header, and see responds with success. So I wrote some code to send 255 different type ids, with 255 different lengths.

//
//
void HIDInput::TestDevice( IOHIDDeviceRef inDeviceRef )
{
unsigned char reportData[255];

// Nullify Report
memset( reportData, 0, sizeof( unsigned char ) * 255 );

// Loop through most possible messages
for( unsigned char reportType = 0; reportType < 255; ++reportType )
{
for( unsigned char reportLength = 0; reportLength < 255; ++reportLength )
{
// Setup Header
reportData[0] = reportType;
reportData[1] = reportLength;

// Send Report to Pad
if( DeviceSendReport( inDeviceRef, reportType, reportData, reportLength ) )
{
printf( "Success - Type(%u), Length(%u)\n", reportType, reportLength );
}
else
{
printf( "Failed - Type(%u), Length(%u)\n", reportType, reportLength );
}
}
}

//
printf( "Finished\n" );
}

I’ll explain show the function body for DeviceSendReport in a minute. After running this, only two reports returned with success, one was the LED report, and the other was of Type 0 with length 4.

Since the header take two bytes, I guessed the last two must be Actuator A and Actuator B. And after setting one of them to 0xff, I successfully managed to get one of the actuators rumbling, and then the other actuator with the other byte.

Now for the code for sending reports to the pad.

//
//
bool HIDInput::DeviceSendReport( IOHIDDeviceRef inDeviceRef, const unsigned char inType, const unsigned char * inReportData, const unsigned char inReportSize )
{
return IOHIDDeviceSetReport( inDeviceRef, kIOHIDReportTypeOutput, inType, inReportData, inReportSize ) == kIOReturnSuccess;
}

As you can see it takes a parameter called inDeviceRef, this is the value that was passed to DeviceMatchingCallback in the previous entry. And now here is the code to get the LED flashing, there are a few status values for it, so I’ve created an enum with some comments explaining what each do.

//
//
enum LEDStatus
{
kLED_AllOff = 0x00, // All LEDs are turned off
kLED_AllBlink = 0x01, // All LEDs repeatedly blink on and off
kLED_P1_FlashOn = 0x02, // Player 1 LED flashes then stays on
kLED_P2_FlashOn = 0x03, // Player 2 LED flashes then stays on
kLED_P3_FlashOn = 0x04, // Player 3 LED flashes then stays on
kLED_P4_FlashOn = 0x05, // Player 4 LED flashes then stays on
kLED_P1_On = 0x06, // Player 1 LED turns on
kLED_P2_On = 0x07, // Player 2 LED turns on
kLED_P3_On = 0x08, // Player 3 LED turns on
kLED_P4_On = 0x09, // Player 4 LED turns on
kLED_Rotating = 0x0a, // LEDs flash on in sequence
kLED_CurrentBlink = 0x0b, // Currently active LED blinks
kLED_SlowBlink = 0x0c, // All LEDs blink slowly
kLED_Alternating = 0x0d, // Player 1 & 4 alternate blink with Player 2 & 3
};

//
//
void HIDInput::FlashLED( IOHIDDeviceRef inDeviceRef, const LEDStatus inStatus )
{
//
const unsigned char kReportType = 0x01;
const unsigned char kReportSize = 0x03;
const unsigned char kReportData[kReportSize] =
{
kReportType, kReportSize, static_cast<Core::byte>( inStatus )
};

//
DeviceSendReport( inDeviceRef, kReportType, kReportData, kReportSize );
}

As you can see, it is fairly simple to send a message, all you have to remember is that the first two bytes contain a header (one byte for type, and one type for the size of the report including the header size). And finally for rumbling the motors.

//
//
void HIDInput::SetRumble( IOHIDDeviceRef inDeviceRef, const unsigned char inBigSpeed, const unsigned char inLittleSpeed )
{
//
const unsigned char kReportType = 0x00;
const unsigned char kReportSize = 0x04;
const unsigned char kReportData[kReportSize] =
{
kReportType, kReportSize, inBigSpeed, inLittleSpeed
};

//
DeviceSendReport( inDeviceRef, kReportType, kReportData, kReportSize );
}

I had some minor problems with the rumble not changing after the first call to SetRumble originally, however it turned out that I had just accidently set the report data variable to be static, so the initialisation was only setup with the first values passed.

Other Rumble Reports

As I mentioned before, I’ve found other message formats for rumbling the motors, I don’t know if its a version thing, or something, but here are the other two I’ve found if you have trouble with mine.

Version 1 (Tattiebogle)

//
//
void HIDInput::SetRumble( IOHIDDeviceRef inDeviceRef, const unsigned char inBigSpeed, const unsigned char inLittleSpeed )
{
//
const unsigned char kReportType = 0x00;
const unsigned char kReportSize = 0x08;
const unsigned char kReportData[kReportSize] =
{
kReportType, kReportSize, 0x00, inBigSpeed, inLittleSpeed, 0x00, 0x00, 0x00
};

//
DeviceSendReport( inDeviceRef, kReportType, kReportData, kReportSize );
}

Version 2 (http://euc.jp/periphs/xbox-controller.ja.html)

//
//
void HIDInput::SetRumble( IOHIDDeviceRef inDeviceRef, const unsigned char inBigSpeed, const unsigned char inLittleSpeed )
{
//
const unsigned char kReportType = 0x00;
const unsigned char kReportSize = 0x06;
const unsigned char kReportData[kReportSize] =
{
kReportType, kReportSize, 0x00, inBigSpeed, 0x00, inLittleSpeed
};

//
DeviceSendReport( inDeviceRef, kReportType, kReportData, kReportSize );
}
Written by Kaluriel in: Athena, Code, Tutorials | Tags: , , , , , , , ,
Jun
12
2009
0

Athena: XBox 360 Pad and Mac OS X

XBox_Pad_with_MacOSXTonight I began the process of writing some using the HID APIs to be able to use an XBox 360 controller on my MacBook Pro for the Athena engine.

As usual, my Courage game played host to testing this new method of control.

First of all, the XBox 360 pad uses a 0xff device class (Vendor specific), so normal HID drivers (they use a device class of 0×03) will not attach to it, so I needed to download a driver for MacOSX that could handle this. Also I didn’t have a Microsoft Wireless Gaming Receiver so I bought a wired pad instead.

I tried the Tattiebogle driver, and at first it worked, but after I logged into VMWare Fusion and used by pad in Vista it stopped working on MacOSX. Or it could have been that I got the Wacom Bamboo pad installed recently.

So after a bit of searching I found another driver, which although didn’t work, sparked Tattiebogle’s to life. This one is known as XBox HID Driver for Mac OS X.

Once it was all up and running, getting code to listen for the 360 pad was a breeze, and Apple has a nice section on your website for it Technical Note TN2187: New HID Manager APIs for Mac OS X version 10. I’ll summarise the basics needed to connect to the pad.

None of this code cleans up properly, like I said I only just started with the HID manager, so I’ll probably post an example App at some point that does clean up properly when shutting down.

Setting up HID Manager

The following code is quite straight forward, it just sets up two callbacks, one for when a device matches what we are looking for, and one for when a device is removed.

The IOKit.framework is required.

I also have two global variables, one for the HID manager, and another the device we going to be reading (this code can only do one device since we only have one buffer variable).

//
//
#include <IOKit/hid/IOHIDLib.h>
#include <IOKit/hid/IOHIDUsageTables.h>

//
//
namespace HIDInput
{
//
void DeviceMatchingCallback( void * inContext, IOReturn inResult, void * inSender, IOHIDDeviceRef inIOHIDDeviceRef );
void DeviceRemovalCallback( void * inContext, IOReturn inResult, void * inSender, IOHIDDeviceRef inIOHIDDeviceRef );
void DeviceIOHIDReportCallback( void * inContext, IOReturn inResult, void * inSender, IOHIDReportType inType, uint32_t inReportID, uint8_t * inReport, CFIndex inReportLength );
Boolean Device_GetLongProperty( IOHIDDeviceRef inDeviceRef, CFStringRef inKey, long * outValue );
long Device_GetVendorID( IOHIDDeviceRef inIOHIDDeviceRef );
long Device_GetProductID( IOHIDDeviceRef inIOHIDDeviceRef );

//
IOHIDManagerRef l_IOHIDManagerRef;
uint8_t * l_pReportBuf = 0;
}

//
//
int argc, char * argv[] )
{
// Create a HID manager with a default allocator
l_IOHIDManagerRef = IOHIDManagerCreate( kCFAllocatorDefault, kIOHIDOptionsTypeNone );
assert( IOHIDManagerRef );

// Look for all device and open the manager
IOHIDManagerSetDeviceMatching( l_IOHIDManagerRef, NULL );
IOHIDManagerOpen( l_IOHIDManagerRef, kIOHIDOptionsTypeNone );

// Register Callbacks to be run with application loop
IOHIDManagerRegisterDeviceMatchingCallback( l_IOHIDManagerRef, HIDInput::DeviceMatchingCallback, NULL );
IOHIDManagerRegisterDeviceRemovalCallback( l_IOHIDManagerRef, HIDInput::DeviceRemovalCallback, NULL );
IOHIDManagerScheduleWithRunLoop( l_IOHIDManagerRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode );

//
while( true )
{
// loop application
}

return 0;
}

The call to IOHIDManagerSetDeviceMatching() can have a dictionary passed to it to look only for devices matching certain criteria, but for simplicity, I’ll just be checking all devices in this example, so I passed NULL as a second argument.

Checking for XBox360 Pad

With the code setup for calling back when a device is found that matches (or all devices in our case), we now need to check the device vendor id and product id to be sure they are what we are looking for.

//
//
Boolean HIDInput::Device_GetLongProperty( IOHIDDeviceRef inDeviceRef, CFStringRef inKey, long * outValue )
{
CFTypeRef tCFTypeRef = IOHIDDeviceGetProperty( inDeviceRef, inKey );
Boolean result = FALSE;

if( tCFTypeRef )
{
// If this is a number get its value
if( CFNumberGetTypeID( ) == CFGetTypeID( tCFTypeRef ) )
{
result = CFNumberGetValue( ( CFNumberRef ) tCFTypeRef, kCFNumberSInt32Type, outValue );
}
}

return result;
}

//
//
long HIDInput::Device_GetVendorID( IOHIDDeviceRef inIOHIDDeviceRef )
{
long result = 0;
Device_GetLongProperty( inIOHIDDeviceRef, CFSTR( kIOHIDVendorIDKey ), &result );
return result;
}

//
//
long HIDInput::Device_GetProductID( IOHIDDeviceRef inIOHIDDeviceRef )
{
long result = 0;
Device_GetLongProperty( inIOHIDDeviceRef, CFSTR( kIOHIDProductIDKey ), &result );
return result;
}

//
//
void HIDInput::DeviceMatchingCallback( void * inContext, IOReturn inResult, void * inSender, IOHIDDeviceRef inIOHIDDeviceRef )
{
if( !inIOHIDDeviceRef )
{
return;
}

const long kVendor_Microsoft = 0x045e;
const long kProduct_XBox360Pad = 0x028e;

const long kVendorID = Device_GetVendorID( inIOHIDDeviceRef );
const long kProductID = Device_GetProductID( inIOHIDDeviceRef );

if( kVendorID == kVendor_Microsoft && kProductID == kProduct_XBox360Pad )
{
// Assert if we're trying to create a second XBox 360 pad device
assert( l_pReportBuf );

// Get the size of the report for the device and allocate a buffer. The
// report size property should be right, however the packet header
// seems to want 20 bytes, so I'm allocating 20 bytes to be safe.
CFIndex reportSize = 20;
//IOHIDDevice_GetLongProperty( inIOHIDDeviceRef, CFSTR( kIOHIDMaxInputReportSizeKey ), &reportSize );
l_pReportBuf = static_cast<uint8_t *>( malloc( reportSize ) );

// Register a report callback for this device
IOHIDDeviceRegisterInputReportCallback( inIOHIDDeviceRef, l_pReportBuf, reportSize, DeviceIOHIDReportCallback, 0 );

printf( "XBox Pad Connected.\n" );
}
}

//
//
void HIDInput::DeviceRemovalCallback( void * inContext, IOReturn inResult, void * inSender, IOHIDDeviceRef inIOHIDDeviceRef )
{
// Deallocate report buffer
free( l_pReportBuf );
l_pReportBuf = 0;

//
printf( "XBox Pad Disconnected.\n" );
}

We have two constants in the device matching callback, one that is the vendor id for Microsoft, and the other which is the product id for the XBox 360 pad.

Since this code only supports the one pad, I’ve added an assert which will be triggered if a second pad is attempted to be matched that is a 360 pad (it checks to see if the report buffer has already been allocated).

Once a device has been removed, I free the memory for the report buffer. I think I’m suppose to unregister the input report callback by passing null as the callback, but I’m not 100% sure yet.

All the other helper functions were found on the Apple website technical note.

Reading Pad Status

XBox 360 pad updates come through on a report type of 0×00 and are 14 bytes long (though I have read on other sites of people getting reports of 20 bytes, this just seems to be padding).

I have skipped the checking of the report type in this code, but it should probably be checked, as well as the report length.

//
//
void HIDInput::DeviceIOHIDReportCallback( void * inContext, IOReturn inResult, void * inSender, IOHIDReportType inType, uint32_t inReportID, uint8_t * inReport, CFIndex inReportLength )
{
// Output hex for each byte in the report (always 2 characters long, making up the rest with 0)
for( int i = 0; inReportLength; ++i )
{
printf( "%02x", inReport[i] );
}

//
printf( "\n" );
}

That sourcecode allows you to read just the actual reports being sent from the pad, however if you want to be able to use it as a 360 pad. You’ll get a message something like this…

0014000000002efcc70792ff9a06

The 0014 at the beginning is a packet header, first byte being the command, second being the size of the packet. The other sections are split up as follows…

0014aaaabbccddddeeeeffffgggg

a is an unsigned short of button flags

b is an unsigned char representing the left trigger (LT)

c is an unsigned char representing the right trigger (RT)

d is a signed short representing the Left Axis X

e is a signed short representing the Left Axis Y

f is a signed short representing the Right Axis X

g is a signed short representing the Right Axis Y

This can be put into a function like so…

//
//
void HIDInput::DeviceIOHIDReportCallback( void * inContext, IOReturn inResult, void * inSender, IOHIDReportType inType, uint32_t inReportID, uint8_t * inReport, CFIndex inReportLength )
{
unsigned char leftTrigger, rightTrigger;
short rightAxisX, rightAxisY;
short leftAxisX, leftAxisY;
unsigned short buttonFlags;

// Copy bytes to their respective variables (incase they are not aligned correctly)
memcpy( &buttonFlags, &inReport[2], sizeof( unsigned short ) );
memcpy( &leftTrigger, &inReport[4], sizeof( unsigned char ) );
memcpy( &rightTrigger, &inReport[5], sizeof( unsigned char ) );
memcpy( &leftAxisX, &inReport[6], sizeof( short ) );
memcpy( &leftAxisY, &inReport[8], sizeof( short ) );
memcpy( &rightAxisX, &inReport[10], sizeof( short ) );
memcpy( &rightAxisY, &inReport[12], sizeof( short ) );

// Output pad stats
printf( "Buttons %u\n", buttonFlags );
printf( "LTrigger: %u\n", leftTrigger );
printf( "RTrigger: %u\n", rightTrigger );
printf( "LAxis: %d, %d\n", leftAxisX, leftAxisY );
printf( "RAxis: %d, %d\n", rightAxisX, rightAxisY );
printf("\n");
}

I don’t know what the 2048 button flag is, it is possibly the synchronization button that is on the wireless controller. So here is a list of all the other button flags and their bit number.

//
//
enum XBox360_ButtonFlags
{
XBOX360PAD_DPAD_UP = 1,
XBOX360PAD_DPAD_DOWN = 2,
XBOX360PAD_DPAD_LEFT = 4,
XBOX360PAD_DPAD_RIGHT = 8,
XBOX360PAD_START = 16,
XBOX360PAD_BACK = 32,
XBOX360PAD_LAXIS = 64,
XBOX360PAD_RAXIS = 128,
XBOX360PAD_LB = 256,
XBOX360PAD_RB = 512,
XBOX360PAD_XBOX = 1024,
XBOX360PAD_A = 4096,
XBOX360PAD_B = 8192,
XBOX360PAD_X = 16384,
XBOX360PAD_Y = 32768,
};

There is still a lot more to cover, so I’ll probably make a follow up entry tomorrow or the day after for sending messages to the pad to make it vibrate, and how to clean up after you’re finished properly. As well as post an example application.

I also found a few new variables to add to my list of debugging preprocessor. I’ll explain on the next page since this one is getting rather long.

Written by Kaluriel in: Athena, Code, Tutorials | Tags: , , , , , , , ,

Pages: 1 2


Apr
16
2009
0

XCode 3.1.3 Source Control (SVN)

Spending my last three days of toil this week, thus giving me a five day weekend, it would be nine if you count Easter but since I went into work yesterday, I decided to add another entry into my guide for using OpenGL with Cocoa. During the process of this, I discovered XCode had source management built into it, so I decided to investigate further, and finally got my project synchronized with a server.

CVSDude - Creating a ModuleSo I decided to write this guide to using XCode with SVN. The SVN provider I’m using for this is CVSDude, they’re cheap and very reliable. But any service provider should work with this guide.

First of all, log into CVSDude, goto the “Settings” menu at the top, then “Modules” on the menu to the right, and then click “Add“. Give a name to your new module, I chose ‘CocoaGL’, which is the name of the project used in the other guides, and click “Add new“.

XCode - Configuration of RepositoryYou now will have a new repository, the address of which will be something like “https://<username>-svn.cvsdude.com/<module>”. If you are unsure or you are having problems, see the “Welcome” page when logged into your CVSDude account.

Now open up a copy of XCode. Before opening a project, goto the “SCM” menu and select “Configure SCM Repositories…“.

Click the “+” button to add a new repository to the list, now give it a name, yet again I chose ‘CocoaGL’, and instead of “CVS” chose “Subversion” from the dropdown list. Now click “OK“.

XCode - Naming the RepositoryThe repository locally now needs to be configured, within the “URL” box, enter the url to your source, in my case it was “https://kaluriel-svn.cvsdude.com/CocoaGL“.

This will automagically fill out most of the other text boxes, apart from username and password. If the path is okay, you should get a red circle with an error something like “Error: 170001 (Authorization failed) Description:”.

XCode - AuthenticatedWithin the username field box enter your account username, and within the password field box, your password. After a second the red circle should change to green with the text “Authenticated” to let you know it has successfully logged in. Click “OK” to close the window and return the main XCode environment.

Written by Kaluriel in: Tutorials | Tags: , , ,

Pages: 1 2


Apr
15
2009
0

OpenGL with Cocoa: Part 3

Another part to my series of guides for programming in OpenGL with Cocoa. Yesterday I showed you how to actively render in OpenGL with Cocoa: Part 2.

CocoaGL - New Header Functions and AttributesThis entry however will show you how to make the OpenGL view the first responder to ‘messages’, so that you can get key input and control turning of the triangle. All of the new functions with the exception of “keyAction” are overloaded and so a part of the Cocoa view system.

The new attribute “m_keys” will be used for storing the status of the key being pressed, true if it is pressed, false if it isn’t.

CocoaGL - Reset Key StatusNow when the application is started, we need to reset the values for “m_keys” to false so that we don’t have uninitialized data. We can do this within the functions “awakeFromNib” or “initWithCoder” but I have chose to do it within “initWithFrame“.

With that done we need to implement our new functions. For “acceptsFirstResponder“, “becomeFirstResponder” and “resignFirstResponder” we return “YES” so that the OpenGL view class we are using gets the messages first instead of the main window.

CocoaGL - Handling Input and ResponseFor “keyDown” and “keyUp” we check to see if there is any input and then forward the information onto one of our own classes, “keyAction” with a value which specifies if the key is pressed or not. This is they set to the Boolean array a the appropriate index.

We could just use the “keyAction” function to control the triangle, however we would be limited in update cycles depending on the repeat rate of messages during a key press.

CocoaGL - Checking for InputNow during the “drawRect” function, we check the status of key presses within the Boolean array. If the key ‘A’ is pressed, we rotate the triangle left by one degree per frame. However if ‘D’ is pressed, it will rotate the triangle right by one degree per frame. And if neither are pressed, nothing will happen.

As before we are not using glLoadIdentity() so the resultant matrix from the last frame is being modified, not a new one which we would have to store and increment a rotational value for.

CocoaGL - Controlling the TriangleWith the new changes, build and run the application, and pressed ‘A’ and ‘D’ to watch the triangle rotate differently. In the previous guide it rotated constantly, however now it requires key input of specific keys to rotate it, and you have control over the direction of rotation.

Thats all for OpenGL with Cocoa for the moment. Still working out some of the bugs in my own applications as I convert them from GLUT, so hopefully the next guide will show you how to switched between window and fullscreen modes.

Download Source: CocoaGL Source (Part 3)
Written by Kaluriel in: Code, Tutorials | Tags: , , , ,
Apr
14
2009
0

OpenGL with Cocoa: Part 2

Following on from yesterdays entry, OpenGL with Cocoa: Part 1, this entry will expand upon that.

CocoaGL - New Header FunctionsFirst of all, we need to add some new functions to our OpenGL view class, as well as a few attributes. “m_rectView” will be used to store the current window rectangle, whilst “m_timer” will be used for updating the window for redraw.

The “initWithFrame” function is called before the OpenGL context is setup, it is used to set the pixel format conditions for the context. The other functions are called by our own code.

CocoaGL - New Source FunctionsI’ve image to the right shows the source for the new functions. Within the “initWithFrame” you can see we’re requesting a format with a double buffer, accelerated hardware, 32 bit colour buffer, with 24 bit depth buffer and an 8 bit stencil buffer. We then call the same function within the NSOpenGLView class we’re inheriting off.

The “setupAnimationTimerWithInterval” function sets up a timer object that fires “timerUpdate” at a specified interval. That function calls “setNeedsDisplay” to make the window redraw. The framerate can be killed if we draw in both the “timerUpdate” function and the “drawRect” function.

CocoaGL - More New Source ChangesWithin the “prepareOpenGL” function, we call the “setupAnimationTimerWithInterval“. Now within the “drawRect” function, we call “resizeGL” which if the rectangle is different to a previously stored one, we make a call to glViewport() to update the destination bounds.

We also change the glFlush() call to a call to a call to “flushBuffer” call from the current OpenGL context, this is because we are now rendering with a double buffer instead of a single buffer.

CocoaGL - Final Output Window

You may have also noticed there is a call to glRotated(), since we don’t call glLoadIdentity() every frame, this will rotate our rectangle, showing that our application is running and updating consistently.

If all goes well, you should have an output like the one in the right image (well at some point since it is rotating).

Well that is all for using OpenGL with Cocoa for the moment, the next part will cover basic keyboard input.

Download Source: CocoaGL Source (Part 2)
Written by Kaluriel in: Code, Tutorials | Tags: , , , ,
Apr
13
2009
0

OpenGL with Cocoa: Part 1

CocoaGL - Creating an Application

In a previous entry I said I would show how to setup an OpenGL application using Cocoa. This guide will be slightly more thorough than the one written on the Apple Developer Site. The version of XCode I’m using is 3.1.3, but I’ve included lots of pictures, which will hopefully work around problems with changes in versions.

CocoaGL - Project is Setup

First off, this is an extremely basic program, single buffer which is only redrawn when the window’s rectangle is dirty. To start off, open up a copy of XCode and create a new “Cocoa Application” and give it a name. I named mine “CocoaGL“.

Once created you should have something that looks kinda like the image on the right.

CocoaGL - Adding Framework

Now you have setup a basic Cocoa application, if you click “Build and Go” you will have a Cocoa window being displayed, however if you click the Red button to close the window, the debugger is still running because the application is still active.

But we’ll get around to fixing that later. Now for using OpenGL, we’ll need the OpenGL framework (a framework is a special folder that contains headers, resources, libraries, etc for a particular third party library).

CocoaGL - Select OpenGL Framework

To add a framework to an application, right click on any folder in the project (I use an already created one), select “Add” and then “Existing Frameworks…“. Now to find the OpenGL framework, navigate to “/System/Library/Frameworks/“. Now search through the folder and select “OpenGL.framework“, and select “Add“, and “Add” again.

CocoaGL - Add Files

With the OpenGL library now linked to the application, we need an Objective-C class for our OpenGL window to be associated with. To do this, we’ll need to inherit off NSView or NSOpenGLView (with NSView we’ll need setup OpenGL ourselves, so for now we’ll use NSOpenGLView). Right click on a folder in the project again, but this time select “Add” and then “New File…“.

CocoaGL - Cocoa Class Source File

Select “Objective-C class” from the list. You can also “Objective-C NSView subclass“, which will create a class with the functions “initWithFrame” and “drawRect“, as well as inheriting off NSView. But I’ll be providing the code for the class so any Objective-C class will be fine.

CocoaGL - Name Source and Header

Upon clicking “Next” you’ll be presented with a screen similar to the one on the left. Choose a filename, for my code I used the name “OpenGLView” (this will also determine the class name. Make sure a header is created with this source file, one nice thing about XCode compared to Visual Studio is that it gives you this option.

CocoaGL - Default Objective-C Class

Click “Finish” and your source and header files will be created for you, opening the header for you to view straight away. I’ve shown what is given by default, this can be used for normal window associated stuff, but it lacks an OpenGL context as well as setting up the pixel format information, that will allow us to use OpenGL.

CocoaGL - New Class Header

Change the inherited class to NSOpenGLView (this does all the default stuff for us). Now add two addition functions, “prepareOpenGL” and “drawRect“, check the header for NSOpenGLView or the Cocoa documentation for more information. The image to the left shows what parameters and return value the functions use.

CocoaGL - New Class Source

Now open the source file (Objective-C uses the extension .m, while Objective-C++ uses .mm). Within the “prepareOpenGL” function, this is where we setup our OpenGL context, I’ve set the clear color and I’ve set the swap interval to be synchronized with the vertical refresh (vblSynch value needs to be set to 1, 0 is as immediate).

CocoaGL - Selecting a XIB

And the “drawRect” function is used for, as you may have guessed, redrawing our screen. For now I’ve added code to clear the screen, draw a yellow triangle and flush the buffer (glFlush is used for a single buffer, rather than double).

With that done we need to link a window to the class we’ve just created. So find the XIB file within the project, and double-click it to open it up Interface Builder.

CocoaGL - Adding a Custom Object

Within the Library window of Interface Builder, do a search for “Custom View“, then drag and drop the found object onto the window, and resize it if you want to be the same size as the window. There is also an “OpenGL View” object that gives you more control over the default settings, however I find it more restrictive to use.

CocoaGL - Sub Window Resize

Now open the “Size Inspector” window for the object we’ve just created, if it isn’t already open, goto the “Tools” menu and select “Size Inspector“. Click all arrows within “Autosizing” so that the object will automatically resize when the parent window is resized. See the image to the left for more information.

CocoaGL - Setting Sub Window ClassNow open up the “Identity Inspector“, if it isn’t open already you can access it from the “Tools” menu as well. Within the “Class Identity” section, set the “Class” to the name of the OpenGL view class we created, the auto-complete will help you find it. Once this is done, goto the “File” menu and select “Build and Go in XCode” – this will exit Interface Builder, build the project and then start it running.

CocoaGL - Build and Run

If all has gone well, you’ll see a window like the one on the left image. However, if you try to resize it, it will remain in the same place (this is because we didn’t change the viewport size when the window is resized, this will be covered in the next part of this guide to using OpenGL in Cocoa).

One other thing you’ll notice is that if you close the window the application will still be running as mentioned earlier.

CocoaGL - Setup a Delegate Class Header

To fix this, we need to setup an object to handle delegations from the application, one of which is whether the application should end if a window is closed. So as before when we created our “OpenGLView” class, create a new class, I’ve called mine “AppDelegate“.

When it is created, add the function “applicationShouldTerminateAfterLastWindowClosed“.

CocoaGL - Setup a Delegate Class Source

Now add the function to the source file as well, returning “YES” so that the application will end when a window is closed.

Now find the XIB file again and double-click to open it up with Interface Builder again.

CocoaGL - Add an Object to use as a Delegate

Now search for “Object“, and drag and drop the NSObject (the blue cube on its own, not the blue cube within the green circle) into the XIB window. Open up the “Identity Inspector” for the Object just created and then set the Class to the application delegate class we just created.

CocoaGL - Set Object Class

Now we just need to link the application delegate to this newly created Object associated with our delegate class. To do this, select “Application” from the XIB window, then open the “Connections Inspector“, available from the “Tools” menu.

CocoaGL - Set Object as Application Delegate

Now drag from the “delegate” selection circle, to the delegate object. If done successfully, “delegate” will have a shaded box around it and the name of your delegate class you created on the right.

Go to the “File” menu and select “Build and Go in XCode“, you should now have a basic OpenGL application made in Cocoa, with a window that will shutdown the application if the window is closed.

The next guide will cover rendering with a double buffer, handling resizing, and a render loop of 60 frames per second.

Download Source: CocoaGL Source (Part 1)
Written by Kaluriel in: Code, Tutorials | Tags: , , , ,
Jan
31
2009
0

XCode File / Project Templates

I just found where to put project and file templates for XCode today, I keep forgetting where it is, so I thought I’d wrote a blog entry to let others know as well.

/Users/<USER>/Library/Application Support/Developer/Shared/Xcode/

I’ve tried other ones, but had no luck with them, maybe they changed with different versions of XCode or maybe I was just doing it wrong.

Written by Kaluriel in: Code | Tags: , ,

Theme: TheBuckmaker.com Blog Themes | The best Webhosting Plans, Eigenes Internet Radio