Elysian Shadows

LibGyro2.0 API is Born!

[b]System subsystem[/b]

There's not really a whole lot of functionality in the system subsystem currently. It's mostly for initializing/manipulating the big-picture hardware.

typedef enum _GY_DEBUG_LEVEL { GY_DEBUG_OFF, GY_DEBUG_CRITICAL, GY_DEBUG_VERBOSE } GY_DEBUG_LEVEL; int gySysInit(); int gySysUninit(); void gySysDebugEnable(GY_DEBUG_LEVEL level, void(*func)(const char *)); void gyPrintf(const char *const str, ...); unsigned int gySysStatsVram(); unsigned int gySysStatsRam(); 

System also offers debugging functionality through the registration of a user-defined printf-type function. The default (gyPrintf()) is used if nothing is supplied. There are also two levels of debugging: GY_DEBUG_CRITICAL and GY_DEBUG_VERBOSE. Critical will only bitch during errors. Verbose will log any major event. 

gyPrintf() uses the platform's preferred stdout method. That's dbglog() on the DC.

The statistics functions are self explanatory.

[b]Video subsystem[/b]

typedef enum _GY_PRIMITIVE { GY_QUADS, GY_SPRITES } GY_PRIMITIVE; int gyVidInit(); int gyVidUninit(); void gyVidSceneBegin(); void gyVidSceneEnd(); void gyVidPrimBegin(const GY_PRIMITIVE prim); void gyVidPrimEnd(); void gyVidVertex(const float x, const float y, const float z); void gyVidTexCoord(const float u, const float v); void gyVidColor(const float a, const float r, const float g, const float b); 

At the moment, two primitives are supported. GY_QUADS is a general quadrilateral, and GY_SPRITES is a specialty, hardware-optimized (at least on the DC) quadrilateral. In the future (aka next few days), I'll be adding things like GY_TRIANGLE_STRIPS.

All rendering happens between gyVidSceneBegin() and gyVidSceneEnd(), which set up the rendering lists. All primitives are to be rendered between gyVidPrimBegin() and gyVidPrimEnd(), which send strip headers and blast vertices from the queues into the display lists.

gyVidVertex(), gyVidTexCoord(), and gyVidColor() work exactly as they did in OpenGL. gyVidVertex() submits a vertex that is transformed by the internal MODEL_VIEW matrix of the gyMath subsystem. 

[b]Input subsystem[/b]

#if GYRO_PLATFORM == GYRO_DREAMCAST # include "dc/gyro_input_dc.h" #endif int gyInputInit(); int gyInputUninit(); void gyInputPoll(GYInput *const input); 

Input has always been a little different as a subsystem, because every platform has completely different buttons, mice, keyboards, and input devices. The only commonality is the Init(), Uninit(), and Poll() functions. LibGyro then includes the proper header file defined for each piece of hardware, which may declare its own structures and public methods. Here's the Dreamcast's:

typedef enum _GY_BUTTON { GY_CONT_BUT_A, GY_CONT_BUT_B, GY_CONT_BUT_X, GY_CONT_BUT_Y, GY_CONT_BUT_START, GY_CONT_DPAD_L, GY_CONT_DPAD_R, GY_CONT_DPAD_U, GY_CONT_DPAD_D, GY_CONT_LTRIG, GY_CONT_RTRIG, GY_CONT_LAST } GY_BUTTON; typedef struct _GYInput { char analog1X, analog1Y; char analog2X, analog2Y; unsigned char butDown[GY_CONT_LAST]; unsigned char butTapped[GY_CONT_LAST]; } GYInput; 

This is especially necessary for IOS, where we will need a custom touchscreen API *cough*MDK*cough*. :D

For the actual engine utilizing the libGyro input subsystem, the gyInput structure should never be accessed without the use of preprocessor directives:

#if GYRO_PLATFORM == GYRO_DREAMCAST input.butTapped[GY_CONT_DPAD_L]; #else if GYRO_PLATFORM == GYRO_PSP input.butTapped[GY_CONT_TRIANGLE]; #else if GYRO_PLATFORM == x86 input.keyTapped[GY_KEY_Y]; #endif 

In ES, we have been mapping keys to a common internal structure, so that the engine is ignorant of the actual key implementations... but the users of the library are free to do whatever they please.

[b]Timer subsystem[/b]

Simplest subsystem... but it's necessary for framerate-independent actions within an engine:

unsigned long int gyTimerTicks();

[b]Math/Matrix subystem(s)[/b]

These are a little different. They have default implementations located in source/soft and include/soft. These are software implementations for platforms where 1) we are either too lazy to SIMD/hardware accelerate them or 2) there is none available. The Matrix and Math subsystems use link-time polymorphism to toggle on and off hardware acceleration (this is kind of a novelty, but I can get some cool, hard numbers so that we can flex our cocks over how much we sped the math up by).

#elif GYRO_PLATFORM == GYRO_PLATFORM_DC #define GYRO_ENDIAN GYRO_ENDIAN_LITTLE #ifndef GYRO_MATH_SOFTWARE #   define GYRO_MATH_ACCELERATION #endif #ifndef GYRO_MATRIX_SOFTWARE #   define GYRO_MATRIX_ACCELERATION #endif 

If the user defines GYRO_MATH_SOFTWARE or GYRO_MATRIX_SOFTWARE, the default implementations will be used. Otherwise, each platform will have its own implementation. The implementations go in source/dc/ and header/dc (or whatever platform's directory). If it is not an inline implementation, it should be a .c file. However, this will generally be inlined, in which case you need to add a preprocessor directive to the header files like so:

#if GYRO_PLATFORM == GYRO_DREAMCAST #   if GYRO_MATRIX_ACCELERATION #       include "dc/gyro_matrix_dc.inl" #   endif #else void gyMatLoad(const GYMatrix4 *const mat); void gyMatStore(GYMatrix4 *const mat); void gyMatApply(const GYMatrix4 *const mat); void gyMatMultVec(const GYVector4 *const src, GYVector4 *const dest, const unsigned int count); #endif 

You're essentially trading the prototypes in the header for inlined versions. As we all know, this is necessary, because inline functions need to be declared within header files...

[b]Texture Management[/b]

I ported over the old texture bank/linked structure from libGyroC++. It's actually very pretty in C as well. LibGyro keeps an internal bank of every texture allocated. Every call to gyVidTexturePNG() or any other texture function first searches the list for an entry and either returns it if found, or allocates a new one. This is nice, so that you don't have to worry about sharing texture pointers for enemies or items using the same texture or deleting the texture while another enemy is still using it. It uses an internal reference counter that frees the textures when they are no longer being referenced.

The texture bank also has support for sprite sheets. When you texture an image, for example, you use:

GYTexture *gyVidTexCreate(const char *const filename, const unsigned int sheetWidth,  	const unsigned int sheetHeight, const unsigned int spriteWidth, const unsigned int spriteHeight); 

libGyro will automagically split your sheet up and maintain a table of UV coordinates (that is also stored internally within a bank, so that two 256x256 textures are sharing the same table). This will allow me to create helper functions like

gyVidTexCoordFrame(FRAME NUMBER)

where the UV coordinates of sprite sheets are handled internally for you. Instead of having to supply UV coords for each vertex, you just supply a sprite frame.

[b]Finishing the DC build[/b]

I need to either utilize Tvspelsfreak's DC strip header library, or at least offer the same functionality for submitting quads and sprites (with and without textures). 

Along with this, I need to implement the libGyro equivalents of

glBindTexture(texture); glTexCoord(u, v); 

Oh, and I need to do audio, but that's really a 2 second job.

So yeah, the code has all been checked into the repo. I don't think it compiles just yet (but it may). I was going to wait to sort out the header submission before doing that. There's also a demo directory where I will be storing a bunch of cross-platform demos that will be distributed with libGyro (and featured on the site) to showcase its functionality.

I'm very pleased with the transition from C++ to C for this API. It's actually FAR more intuitive, far easier to implement, and far more powerful as a C API than it ever was in C++. I also am not asserting any sort of object-oriented design pattern upon any users of the API. They are free to structure their software and encapsulate this API as they please.

Falco Girgis
Falco Girgis is the founder and lead software architect of the Elysian Shadows project. He was previously employed in the telecom industry before taking a chance on Kickstarter and quitting his job to live the dream. He is currently pursuing his masters in Computer Engineering with a focus on GPU architecture.