Thursday, May 31, 2012

BlackBerry GamePlay3D 2

The previous build is boring. Let’s kang some code from SDK sample 1. And by kang, I mean replace virtually all of the code.



Rubber Ducky, you’re the one!
Riveting. That number in the upper left is the FPS display. I don’t like it. Let’s mess with it.


Now if you have built the sample application you’ll notice that my build looks a bit different than yours. I did some work on automatic recognition of the target system, the build environment, and so on. This is relatively easy, but I made it difficult (three header files that will recognize pretty much every processor/build environment since 1980). I split the previous drawFrameRate function into two, making a new function drawText that drawFrameRate calls. I then added a new function, drawBuildString. All preprocessor definitions are strings (automatically selected, but I’m not posting that) with the exception of GAMEVERSION, which is a float. In any case, the relevant code looks like this:


// Debug vs Release
#ifdef _DEBUG
#define BUILD "Debug"
#else
#define BUILD "Release"
#endif

#define BUILDSTRINGPARAMS GAMENAME, GAMEVERSION, GAMESUBVERSION, __DATE__, __TIME__
, BUILD, OS_NAME, HW_PROCESSOR_NAME
#define BUILDSTRINGFORMAT "%s %1.2f%s.%s.%s.%s %s %s"

void Core::render(long elapsedTime)
{
   //…

   // Draw compile info
   drawBuildString(5, 1);

   // Draw the fps
   //drawFrameRate(_font, Vector4(0, 0.5f, 1, 1), 5, 10, getFrameRate());
   drawFrameRate(5, 15);
}

void Core::drawFrameRate(unsigned int x, unsigned int y)
{
   char buffer[16];
   sprintf(buffer, "%u FPS", getFrameRate());
   drawText(_font, Vector4(0, 0.5f, 1, 1), x, y, buffer);
}

void Core::drawBuildString(unsigned int x, unsigned int y)
{
   char buffer[128];
   sprintf(buffer, BUILDSTRINGFORMAT, BUILDSTRINGPARAMS);
   drawText(_font, Vector4(0, 0.5f, 1, 1), x, y, buffer);
}

void Core::drawText(Font* font, const Vector4& color, unsigned int x, unsigned int y, char* buffer)
{
   font->begin();
   font->drawText(buffer, x, y, color, 20);
   font->end();
}

Now we have a baseline to build a real application. My first concern is file system/network streaming. I want a system where I can precache octree nodes and stream them regardless of data source (the application does not know or care whether the data comes from the file system or the interwebs). Additionally, I want each level of the octree to contain a mipmap of the combined sublevels. By this I mean that if I load the top level of the octree, that level contains a lo-res version of all necessary data for rendering. Each subdivision (leaf) of the octree contains increased detail. We will see how well this goes.


Prior to that, I need to deal with file formats. Gameplay includes a proprietary bundle format (.gpb) that requires a proprietary tool to build (think Doom WADs back in the day - http://doom.wikia.com/wiki/WAD). It is a well thought out, comprehensive format. What seems to be lacking is support for compression and encryption. For this, I will use the public domain LZMA SDK that can run on ARM, x86 (actually 386 onward!!!), PowerPC, and so on. The question is how to do this. If I were designing this from scratch, I’d say that I would want to compress the data within the container. I want to minimize my impact on the SDK, and avoid unnecessary rewriting of the toolchain, so I don’t think that is the way to go.


What I think I need to build is a comprehensive Resource Management system. The Bundle code does a lot of this already; however, it appears to be path dependent. By this, I mean that assets are represented in code by their string path. If I want my rubber ducky model, I load it:


// Load mesh/scene from file
Bundle* bundle = Bundle::create("res/duck.gpb");
_scene = bundle->loadScene();
SAFE_RELEASE(bundle);

Then every time I want it I attempt:
// Get the duck node
_modelNode = _scene->findNode("duck");

This code isn’t checked, so if “duck” doesn’t exist in the scene, I crash. I don’t want to hardcode “duck” all over the place. And, I don’t want to crash if “duck” doesn’t exist. So what can I do with this?


So, to sum up, I need to build a resource manager with the following properties:

  • Transparent streaming from either local or remote file system
  • Precache capability
  • Intelligent unload capability
  • Mipmaps/Clipmaps/Level of detail (ideally the lowest level of detail will be compact enough to always be in memory)
  • Compression/Encryption support
  • Null (default, placeholder) models/objects
  • Ability to load resources individually or by node/cell
  • Centralized media manager not reliant on path or object strings
  • Reference counting for automatic release of memory?
A tall order, for sure. Many headaches loom ahead…

No comments:

Post a Comment