Jump to content

  • Log In with Google      Sign In   
  • Create Account

Explicity undeclared yet implicitly defined ramble

RTF Logging

Posted by , 11 December 2006 - - - - - - · 212 views

Based upon the RTF article hosted HERE on GameDev, I have created a simple log interface and RTF logging implementation. I'm experimenting with different types of logging and wanted to keep a unified interface. This interface can be expanded into HTML, XML, or other formats fairly easy.

The current incarnation supports rudimentary logging thresholds based on message types and supports macro interfaces. Any constructive criticism and suggestions is welcome.

Download zip of source, or check it out below.

app_log.h <Log_Interface>

#ifndef APP_LOG_H
#define APP_LOG_H

/*
*****************************************************************************
* LOG INTERFACE OBJECT *
* Written by: Jonathan Bender *
* Last update: December 11, 2006 *
* *
* Usage: This is the interface object for logging objects. You can *
* implement custom logging objects on top of this interface to *
* provide a common interface for you projects to plug in different *
* types of logging systems. *
* *
* Disclaimer: This code is free for use in personal projects for *
* learning purposes. This code is not intended for *
* commercial use and is not final nor free of bugs. This code *
* is provided as is and maintains no warranty. If you have any *
* questions, contact Daishim @ GameDev.net. *
*****************************************************************************
*/


#include <string> // STL strings

using std::string;

// Functionality
#define LOG_ERRORS 1
#define LOG_WARNINGS 1
#define LOG_TRACES 1
#define LOG_INFOS 1
#define LOG_DEBUGS 1
//

// Type def UInt if not already
#ifndef UINT_DEF
typedef unsigned int UInt;
#endif
//

// Log pointer alias
#define LOG_ALIAS Logger
//

// Log object
class App_Log; // Class definition is coming
extern App_Log *LOG_ALIAS; // Pointer to log is external
//

// Macro functions for quicker use
#ifdef LOG_ERRORS
#define LOG_ERROR(Message) LOG_ALIAS->Insert(GLMT_ERROR, __FUNCTION__, Message)
#else
#define LOG_ERROR(Message)
#endif

#ifdef LOG_WARNINGS
#define LOG_WARNING(Message) LOG_ALIAS->Insert(GLMT_WARNING, __FUNCTION__, Message)
#else
#define LOG_WARNING(Message)
#endif

#ifdef LOG_TRACES
#define LOG_TRACE(Message) LOG_ALIAS->Insert(GLMT_TRACE, __FUNCTION__, Message)
#else
#define LOG_TRACE(Message)
#endif

#ifdef LOG_INFOS
#define LOG_INFO(Message) LOG_ALIAS->Insert(GLMT_INFO, __FUNCTION__, Message)
#else
#define LOG_INFO(Message)
#endif

#ifdef LOG_DEBUGS
#define LOG_DEBUG(Message) LOG_ALIAS->Insert(GLMT_DEBUG, __FUNCTION__, Message)
#else
#define LOG_DEBUG(Message)
#endif

#define LOG_INIT(File) LOG_ALIAS->Initialize(File)
//

/*
#define Log(Type, Message) LOG_ALIAS->Insert(Type, __FUNCTION__, Message)
#define LogNO(Type, Message) LOG_ALIAS->Insert(Type, Message)
#define LogV(Type, Message, Value) LOG_ALIAS->InsertV(Type, __FUNCTION__, Message, Value)
#define LogVNO(Type, Message, Value) LOG_ALIAS->InsertV(Type, Message, Value)
*/

//

// Log message types
enum App_Log_Type {GLMT_REGULAR, GLMT_TRACE, GLMT_ATTEMPT, GLMT_SUCCESS, GLMT_ERROR, GLMT_WARNING, GLMT_INFO, GLMT_DEBUG};
//

// Log interface object
class App_Log
{
protected:
App_Log(void) { /* Empty */ } // Constructor
~App_Log(void) { /* Empty */ } // Destructor

public:
// External initialization/shutdown methods
virtual bool Initialize(const string &Filename) = 0; // Open the log file
virtual void Close(void) = 0; // Close the log file
//

// External insertion methods
virtual void Insert(string &Text) = 0; // Insert line of text into log
virtual void Insert(const char *Text) = 0; // Insert line of text into log

virtual void Insert(App_Log_Type Type, string &Text) = 0; // Insert line text of type with origin
virtual void Insert(App_Log_Type Type, const char *Text) = 0; // Insert line text of type with origin

virtual void Insert(App_Log_Type Type, string &Origin, string &Text) = 0; // Insert line of text into log with origin
virtual void Insert(App_Log_Type Type, const char *Origin, const char *Text) = 0; // Insert line of text into log with origin

virtual void InsertV(App_Log_Type Type, string &Text, int Value) = 0; // Insert line of text with signed integer value
virtual void InsertV(App_Log_Type Type, const char *Text, int Value) = 0; // Insert line of text with signed integer value

virtual void InsertV(App_Log_Type Type, string &Origin, string &Text, int Value) = 0; // Insert line of text with origin and signed integer value
virtual void InsertV(App_Log_Type Type, const char *Origin, const char *Text, int Value) = 0; // Insert line of text with origin and signed integer value

virtual void InsertV(App_Log_Type Type, string &Text, UInt Value) = 0; // Insert line of text with unsigned integer value
virtual void InsertV(App_Log_Type Type, const char *Text, UInt Value) = 0; // Insert line of text with unsigned integer value

virtual void InsertV(App_Log_Type Type, string &Origin, string &Text, UInt Value) = 0; // Insert line of text with origin and unsigned integer value
virtual void InsertV(App_Log_Type Type, const char *Origin, const char *Text, UInt Value) = 0; // Insert line of text with origin and unsigned integer value

virtual void InsertV(App_Log_Type Type, string &Text, string &Value) = 0; // Insert line of text with string value
virtual void InsertV(App_Log_Type Type, const char *Text, const char *Value) = 0; // Insert line of text with string value

virtual void InsertV(App_Log_Type Type, string &Origin, string &Text, string &Value) = 0; // Insert line of text with origin and string value
virtual void InsertV(App_Log_Type Type, const char *Origin, const char *Text, const char *Value) = 0; // Insert line of text with origin and string value
//
};

#endif /* APP_LOG_H */



app_log_rtf.h

#ifndef APP_LOG_RTF_H
#define APP_LOG_RTF_H

/*
*****************************************************************************
* RTF LOG OBJECT *
* Written by: Jonathan Bender *
* Last update: December 11, 2006 *
* *
* Usage: This object extends the App_Log object to provide an rich text *
* logger. Include the source and header, along with the interface *
* header, in your project. You may then use this log object with *
* the macros in the interface object header. *
* *
* Desctription: This log was designed for functionality and formatted *
* output text, not speed. For a faster logging system, use a *
* plain text logger (not included yet). *
* *
* Disclaimer: This code is free for use in personal projects for *
* learning purposes. This code is not intended for *
* commercial use and is not final nor free of bugs. This code *
* is provided as is and maintains no warranty. If you have any *
* questions, contact Daishim @ GameDev.net. *
*****************************************************************************
*/


#include <fstream> // STL file streams

#include "app_log.h" // Log interace

using std::ofstream;

class App_Log_RTF : public App_Log
{
private:
// Log data
string m_Filename; // Log filename
ofstream *m_File; // File stream of log file
//

// File output methods
void Write_To_Log(const string &Text); // Commit a string of text to the log file
void Write_Header(void); // Write the RTF file header
//

// Internal insertion methods
void Insert_Value(string &Text, int Value); // Insert signed integer value into string
void Insert_Value(string &Text, UInt Value); // Insert unsigned integer value into string
void Insert_Value(string &Text, string &Value); // Insert string value into string
//

// Formatting methods
void Swap_Tags(string &Text, const string &Clean_Tag, const string &RTF_Tag); // Swaps clean tags for RTF tags
void Format(string &Text); // Formatting instructions
//

public:
App_Log_RTF(void); // Constructor
~App_Log_RTF(void); // Destructor

// External initialization/shutdown methods
bool Initialize(const string &Filename = "log.rtf"); // Open the log file
void Close(void); // Close the log file
//

// External insertion methods
void Insert(string &Text); // Insert line of text into log
void Insert(const char *Text); // Insert line of text into log

void Insert(App_Log_Type Type, string &Text); // Insert line text of type with origin
void Insert(App_Log_Type Type, const char *Text); // Insert line text of type with origin

void Insert(App_Log_Type Type, string &Origin, string &Text); // Insert line of text into log with origin
void Insert(App_Log_Type Type, const char *Origin, const char *Text); // Insert line of text into log with origin

void InsertV(App_Log_Type Type, string &Text, int Value); // Insert line of text with signed integer value
void InsertV(App_Log_Type Type, const char *Text, int Value); // Insert line of text with signed integer value

void InsertV(App_Log_Type Type, string &Origin, string &Text, int Value); // Insert line of text with origin and signed integer value
void InsertV(App_Log_Type Type, const char *Origin, const char *Text, int Value); // Insert line of text with origin and signed integer value

void InsertV(App_Log_Type Type, string &Text, UInt Value); // Insert line of text with unsigned integer value
void InsertV(App_Log_Type Type, const char *Text, UInt Value); // Insert line of text with unsigned integer value

void InsertV(App_Log_Type Type, string &Origin, string &Text, UInt Value); // Insert line of text with origin and unsigned integer value
void InsertV(App_Log_Type Type, const char *Origin, const char *Text, UInt Value); // Insert line of text with origin and unsigned integer value

void InsertV(App_Log_Type Type, string &Text, string &Value); // Insert line of text with string value
void InsertV(App_Log_Type Type, const char *Text, const char *Value); // Insert line of text with string value

void InsertV(App_Log_Type Type, string &Origin, string &Text, string &Value); // Insert line of text with origin and string value
void InsertV(App_Log_Type Type, const char *Origin, const char *Text, const char *Value); // Insert line of text with origin and string value
//
};

#endif /* APP_LOG_RTF_H */



app_log_rtf.cpp

/*
*****************************************************************************
* RTF LOG OBJECT *
* Written by: Jonathan Bender *
* Last update: December 11, 2006 *
* *
* Usage: This object extends the App_Log object to provide an rich text *
* logger. Include the source and header, along with the interface *
* header, in your project. You may then use this log object with *
* the macros in the interface object header. *
* *
* Desctription: This log was designed for functionality and formatted *
* output text, not speed. For a faster logging system, use a *
* plain text logger (not included yet). *
* *
* Disclaimer: This code is free for use in personal projects for *
* learning purposes. This code is not intended for *
* commercial use and is not final nor free of bugs. This code *
* is provided as is and maintains no warranty. If you have any *
* questions, contact Daishim @ GameDev.net. *
*****************************************************************************
*/


#include <fstream> // STL file streams
#include <string> // STL strings
#include <sstream> // STL string streams

#include "app_log_rtf.h" // App log object

using std::string;
using std::ofstream;
using std::stringstream;

// Log object
App_Log_RTF RTF_Log;
App_Log *Logger = reinterpret_cast<App_Log *>(&RTF_Log);
//

// Constructor, set log file and
App_Log_RTF::App_Log_RTF(void)
{
// Initialize all members to NULL/0
m_File = NULL;
}

// Destructor
App_Log_RTF::~App_Log_RTF(void)
{
// If log is open
if(m_File)
Close(); // Close it
}

/////////////////////////
// File output methods //
/////////////////////////

// Commit a string of text to the log file
void App_Log_RTF::Write_To_Log(const string &Text)
{
// Write text to file
m_File->write(Text.c_str(), (std::streamsize) Text.length());
m_File->flush();
}

// Write the RTF file header
void App_Log_RTF::Write_Header(void)
{
// Write RTF header
Write_To_Log("{\\rtf1\\ansi\\deff0{\\fonttbl{\\f0 Times New Roman;}}\\fs20\n");

// Write color table
Write_To_Log("{\\colortbl;\n"); // Black
Write_To_Log("\\red255\\green255\\blue255;\n"); // White
Write_To_Log("\\red128\\green128\\blue128;\n"); // Grey
Write_To_Log("\\red255\\green0\\blue0;\n"); // Red
Write_To_Log("\\red0\\green255\\blue0;\n"); // Green
Write_To_Log("\\red0\\green0\\blue255;\n"); // Blue
Write_To_Log("\\red0\\green128\\blue0;\n"); // Dark green
Write_To_Log("\\red255\\green128\\blue0;\n"); // Orange
Write_To_Log("\\red0\\green200\\blue200;\n"); // Blue-green
Write_To_Log("}\n");

// Write logging started message
Insert("== Start Log ==\n\n");

// Blank line spacer
Insert("\n");
}

////////////////////////////////
// Internal insertion methods //
////////////////////////////////

// Insert signed integer value into string
void App_Log_RTF::Insert_Value(string &Text, int Value)
{
unsigned int Position = 0;
string Text_Value;
stringstream Stream;

Stream << Value;
Text_Value = Stream.str();

if((Position = (unsigned int) Text.find("%i", 0)) != string::npos)
Text.replace(Position, 2, Stream.str());
}

// Insert unsigned integer value into string
void App_Log_RTF::Insert_Value(string &Text, UInt Value)
{
unsigned int Position = 0;
string Text_Value;
stringstream Stream;

Stream << Value;
Text_Value = Stream.str();

if((Position = (unsigned int) Text.find("%i", 0)) != string::npos)
Text.replace(Position, 2, Stream.str());
}

// Insert string value into string
void App_Log_RTF::Insert_Value(string &Text, string &Value)
{
unsigned int Position = 0;

if((Position = (unsigned int) Text.find("%s", 0)) != string::npos)
Text.replace(Position, 2, Value.c_str());
}

////////////////////////
// Formatting methods //
////////////////////////

// Swaps clean tags for RTF tags
void App_Log_RTF::Swap_Tags(string &Text, const string &Clean_Tag, const string &RTF_Tag)
{
unsigned int Position = 0;

// Find each occurance of the clean tag
while((Position = (unsigned int) Text.find(Clean_Tag, (size_t) Position)) != string::npos)
{
Text.replace(Position, Clean_Tag.length(), RTF_Tag); // Replace clean tag with RTF tag
Position += (unsigned int) (Clean_Tag.length() > RTF_Tag.length() ? Clean_Tag.length() + 1 : RTF_Tag.length()); // Set position to after the clean tag
}
}

// Formatting instructions
void App_Log_RTF::Format(string &Text)
{
// Special formatting
Swap_Tags(Text, "\\", "\\\\"); // Convert slash to single slash
Swap_Tags(Text, "{", "\\{"); // Convert open brace to RTF open brace
Swap_Tags(Text, "}", "\\}"); // Convert close brace to RTF close brace

// Text coloring
Swap_Tags(Text, "[BLACK]", "\\cf0 "); // Black
Swap_Tags(Text, "[WHITE]", "\\cf1 "); // White
Swap_Tags(Text, "[GREY]", "\\cf2 "); // Grey
Swap_Tags(Text, "[RED]", "\\cf3 "); // Red
Swap_Tags(Text, "[GREEN]", "\\cf4 "); // Green
Swap_Tags(Text, "[BLUE]", "\\cf5 "); // Blue
Swap_Tags(Text, "[DKGREEN]", "\\cf6 "); // Dark green
Swap_Tags(Text, "[ORANGE]", "\\cf7 "); // Orange
Swap_Tags(Text, "[CYAN]", "\\cf8 "); // Blue-green

// Type formatting
Swap_Tags(Text, "[B]", "\\b "); // Bold start
Swap_Tags(Text, "[/B]", "\\b0 "); // Bold end
Swap_Tags(Text, "[I]", "\\i "); // Italicized start
Swap_Tags(Text, "[/I]", "\\i0 "); // Italicized end

// Object formatting
Swap_Tags(Text, "<", "\\b <"); // Convert object open carrot to bold object open carrot
Swap_Tags(Text, ">", ">\\b0 "); // Convert object close carrot to not bold then close carrot

// General formatting
Text.insert(0, "{\\pard "); // Prepend line starter

if(Text[Text.length() - 1] == '\n')
{
Text.replace(Text.length() - 1, 6, "\\par}\n"); // Append line ender with newline
//Text.insert(Text.length(), "\\par}\n");
}
else
{
Text.insert(Text.length(), "}\n"); // Append line ender without newline
}
}

//////////////////////////////////////////////
// External initialization/shutdown methods //
//////////////////////////////////////////////

// Open the log file
bool App_Log_RTF::Initialize(const string &Filename)
{
bool Success = true;

m_Filename = Filename; // Copy filename to local container

// Attempt to create new file stream
m_File = new ofstream(Filename.c_str());

if(!(*m_File))
{
// Failed to open file
Success = false;

// Destroy the file stream
delete m_File;
m_File = NULL;
}
else
{
// Write log file header
Write_Header();
}

return Success;
}

// Close the log file
void App_Log_RTF::Close(void)
{
// If file is open
if(m_File)
{
Insert("\n"); // Write blank line as spacer
Insert("== End Of Log ==\n"); // Write end of log file message
Write_To_Log("}"); // Write RTF footer

// Close file
m_File->close();

// Destroy file stream
delete m_File;
m_File = NULL;
}
}

////////////////////////////////
// External insertion methods //
////////////////////////////////

// Insert text into log
void App_Log_RTF::Insert(string &Text)
{
// If file is open
if(m_File)
{
Format(Text); // Format incoming text RTF style
Write_To_Log(Text); // Write text to file
}
}

// Insert text into log
void App_Log_RTF::Insert(const char *Text)
{
string SText(Text);

// If file is open
if(m_File)
{
Format(SText); // Format incoming text RTF style
Write_To_Log(SText); // Write text to file
}
}

// Insert line of text of type into log (newline after text)
void App_Log_RTF::Insert(App_Log_Type Type, string &Text)
{
// If file is open
if(m_File)
{
if(Type == GLMT_TRACE)
{
Text.insert(0, "[GREY]"); // Trace messages are grey
}
else if(Type == GLMT_ATTEMPT)
{
Text.insert(0, "[BLUE]"); // Code attempt messages are blue

if(Text[Text.length() - 1] != '\n')
Text.insert(Text.length(), ": ");
}
else if(Type == GLMT_SUCCESS)
{
Text.insert(0, "[B][BLUE]"); // Success messages are bolded blue
}
else if(Type == GLMT_ERROR)
{
Text.insert(0, "[RED]"); // Error messages are red
}
else if(Type == GLMT_WARNING)
{
Text.insert(0, "[ORANGE]"); // Warning messages are orange
}
else if(Type == GLMT_INFO)
{
Text.insert(0, "[DKGREEN]"); // Info messages are dark green
}
else if(Type == GLMT_DEBUG)
{
Text.insert(0, "[CYAN]"); // Debug messages are blue-green
}
else
{
Text.insert(0, "[BLACK]"); // Plain/no type messages are black
}

Format(Text); // Format incoming text RTF style
Write_To_Log(Text); // Write text to file
}
}

// Insert line of text of type into log (newline after text)
void App_Log_RTF::Insert(App_Log_Type Type, const char *Text)
{
string SText(Text);

// If file is open
if(m_File)
{
if(Type == GLMT_TRACE)
{
SText.insert(0, "[GREY]"); // Trace messages are grey
}
else if(Type == GLMT_ATTEMPT)
{
SText.insert(0, "[BLUE]"); // Code attempt messages are blue

// Attempt message follows with : and then the message
if(SText[SText.length() - 1] != '\n')
SText.insert(SText.length(), ": ");
}
else if(Type == GLMT_SUCCESS)
{
SText.insert(0, "[B][BLUE]"); // Success messages are bolded blue
}
else if(Type == GLMT_ERROR)
{
SText.insert(0, "[RED]"); // Error messages are red
}
else if(Type == GLMT_WARNING)
{
SText.insert(0, "[ORANGE]"); // Warning messages are orange
}
else if(Type == GLMT_INFO)
{
SText.insert(0, "[DKGREEN]"); // Info messages are dark green
}
else
{
SText.insert(0, "[BLACK]"); // Plain/no type messages are black
}

Format(SText); // Format incoming text RTF style
Write_To_Log(SText); // Write text to file
}
}

// Insert line of text into log with origin
void App_Log_RTF::Insert(App_Log_Type Type, string &Origin, string &Text)
{
Origin.insert(0, "<");
Origin.insert(Origin.length(), "> ");

Text.insert(0, Origin);

Insert(Type, Text);
}

// Insert line of text into log with origin
void App_Log_RTF::Insert(App_Log_Type Type, const char *Origin, const char *Text)
{
string SOrigin(Origin);
string SText(Text);

SOrigin.insert(0, "<");
SOrigin.insert(SOrigin.length(), "> ");

SText.insert(0, SOrigin);

Insert(Type, SText);
}

// Insert line of text with signed integer value
void App_Log_RTF::InsertV(App_Log_Type Type, string &Text, int Value)
{
Insert_Value(Text, Value);
Insert(Type, Text);
}

// Insert line of text with signed integer value
void App_Log_RTF::InsertV(App_Log_Type Type, const char *Text, int Value)
{
string SText(Text);

Insert_Value(SText, Value);
Insert(Type, SText);
}

// Insert line of text with origin and signed integer value
void App_Log_RTF::InsertV(App_Log_Type Type, string &Origin, string &Text, int Value)
{
Insert_Value(Text, Value);
Insert(Type, Origin, Text);
}

// Insert line of text with origin and signed integer value
void App_Log_RTF::InsertV(App_Log_Type Type, const char *Origin, const char *Text, int Value)
{
string SOrigin(Origin);
string SText(Text);

Insert_Value(SText, Value);
Insert(Type, SOrigin, SText);
}

// Insert line of text with unsigned integer value
void App_Log_RTF::InsertV(App_Log_Type Type, string &Text, UInt Value)
{
Insert_Value(Text, Value);
Insert(Type, Text);
}

// Insert line of text with unsigned integer value
void App_Log_RTF::InsertV(App_Log_Type Type, const char *Text, UInt Value)
{
string SText(Text);

Insert_Value(SText, Value);
Insert(Type, SText);
}

// Insert line of text with origin and unsigned integer value
void App_Log_RTF::InsertV(App_Log_Type Type, string &Origin, string &Text, UInt Value)
{
Insert_Value(Text, Value);
Insert(Type, Origin, Text);
}

// Insert line of text with origin and unsigned integer value
void App_Log_RTF::InsertV(App_Log_Type Type, const char *Origin, const char *Text, UInt Value)
{
string SOrigin(Origin);
string SText(Text);

Insert_Value(SText, Value);
Insert(Type, SOrigin, SText);
}

// Insert line of text with string value
void App_Log_RTF::InsertV(App_Log_Type Type, string &Text, string &Value)
{
Insert_Value(Text, Value);
//Format(Text);
Insert(Type, Text);
}

// Insert line of text with string value
void App_Log_RTF::InsertV(App_Log_Type Type, const char *Text, const char *Value)
{
string SText(Text);
string SValue(Value);

Insert_Value(SText, SValue);
//Format(Text);
Insert(Type, SText);
}

// Insert line of text with origin and string value
void App_Log_RTF::InsertV(App_Log_Type Type, string &Origin, string &Text, string &Value)
{
Insert_Value(Text, Value);
//Format(Text);
Insert(Type, Origin, Text);
}

// Insert line of text with origin and string value
void App_Log_RTF::InsertV(App_Log_Type Type, const char *Origin, const char *Text, const char *Value)
{
string SOrigin(Origin);
string SText(Text);
string SValue(Value);

Insert_Value(SText, SValue);
//Format(Text);
Insert(Type, SOrigin, SText);
}




Procedural Terrain Texturing

Posted by , 23 October 2006 - - - - - - · 212 views

I've been working on this terrain engine for quite some time. It has also been a long learning project for me. I started the engine with basically knowing only how to draw some simple shapes to the screen using OpenGL, and now I'm generating terrain textures procedurally and in a fresh new engine architecture.

I'm using a fairly simple algorithm that is based off of Trent Polack's implementation as defined in his book "3D Terrain Programming". The algorithm simply iterates through each triangle row and column and interpolates the height of the of surface at that point and determines its pixel from the textures that affect that height. For example, if we have 3 textures, grass, dirt/rock, and snow that affect the following height ranges:

=Grass=
Optimal: 20
Min: -15
Max: 50

=Dirt/Rock=
Optimal: 115
Min: 20
Max: 175

=Snow=
Optimal: 175
Min: 115
Max: 255

Pseudo overview


Loop(through each element row of the map)
{
Loop(through each element column of the map)
{
Loop(through each texel of the corresponding destination texture area)
{
Loop(through each layer)
{
Calculate blend percentage of layer based on height
Calculate blended RGB value from source texel
Add blended RGB to overall texel RGB
}

Set texel RGB in destination texture
}
}
}






This algorithm has generated the following images from my terrain editor (work in progress [wink]).

Shot 1
terrain1

Shot 2
terrain1

Shot 3
terrain1

There is no detail texture yet, and I have disabled lighting from my previous work to help simplify the new engine architecture implementation testing and the procedural texture generation. I plan to do some more research into procedural texture generation and implement slope variation and other variables into the algorithm to produce a more natural looking terrain. That's all for now.


Engine Architecture Postmortem

Posted by , 23 October 2006 - - - - - - · 170 views

I've been fairly busy working on my terrain engine which is coming along pretty well. I've done a lot of restructuring of the engine, mostly as it applys to data transfer through the engine. My original architecture was to separate every object from any other object, except it's dependencies (pretty much only inherited objects or global types). Objects just contained their data, knew some basic operations of that data, and knew how to give the data out. The renderer knew how to draw basic things. The engine core was left up to getting the data from the object and then handing it to the renderer to be drawn.

For encapsulation and data management in a large project (like a game engine), this seemed like a good idea. However, this didn't turn out to be the case. Every time I wanted to draw something, per say the terrain, I had to strip together indices for rendering the patches. This required prompting the terrain map object to generate these indices every frame, which got kind of expensive (even though it was mostly just a fetch of patch indices and then a degenerate triangle to stitch them together). This also made it very difficult to implement level of detail, blending, and all those nifty graphical tricks since objects didn't know how to draw themselves. The engine had to get the texture token(s) from the terrain map, vertex array/buffer token(s), bind them, enable blending, retrieve the indices and perform the draw. The engine was constantly busy trafficking data back and forth to various components of the engine, and it got insanely cluttered really fast.

Unfortunately, I couldn't really find a light at the end of the tunnel for this architecture and it was just getting out of hand. So I took a few steps back and reevaluated some things that I was doing. I had somewhat neglected the use of interfaces in my engine, mostly due to my ignorance on their extreme usefulness even for some of the simplest of objects. So, I ran back down the tunnel to the fork of architecture type decision and took a different branch.

I wrapped a lot of my components with interfaces and allowed components to know about the interfaces to other components that they communicate with. This allows me to simply invoke a function and pass the object to communicate with to the component that needs to do the communicating and the interfaces take care of keeping GUI objects from knowing about OpenGL and other things they really don't need to know specifically about. This allows me to keep the code cleaner and I can completely rewrite the engine components using different APIs or rolling my own without having much, if any, of an effect on the rest the engine.

As far as performance goes, I've increased the efficiency of the engine and it's overall feel of interaction to something more befitting of an actual game engine. Frame rates have increased and I have greatly increased my capability of performing graphical effects much easier since objects control their own rendering. I've alleviated the core of the engine from playing messenger and having to decipher and translate and leaving it up the the objects and components to handle that, and leaving the core to simply orchestrate the interaction.

The biggest beneficiary of this switch, is probably my eyes. They aren't screaming at me any more for having to read through the cluttered code anymore ;-).


Basic Terrain Rendering

Posted by , 19 May 2006 - - - - - - · 176 views

I've finally gathered the time to get back to working on my engine, now that the semester is over. My current dilemma with the engine is what model and terrain/world geometry formats I should use to get accomplished what I need. After doing some research, it looks like I'm going to be rolling my own terrain/world geometry format, which is okay by me. I've actually got some basic functionality of a heightmap terrain renderer up and running as demonstrated in the following screenshots.

Really simple color terrain

Really simple color terrain2

It still needs a LOT of work, but in its basic form, its working. I need to add texturing, smoothing, LOD, and much much more. I'm planning to combine this with a BSP format also, which I'm still working out the details of.

The next half of my dilemma is a model format. I have a very basic (again) static mesh rendering function in place that uses Wavefront OBJ files. I have been researching the MD# model formats and have found them to be interesting at the least. I haven't had much time to spend in this area yet to do much research, so this will probably be my next focus. I'm still not sure which format is going to win out in the end, or if I'm going to end up rolling my own. I'll have to experiment with a few and see what works out best for what I need. The only things I know for sure so far is that I will need a skeletally animated format.

I still need to finish implementing lighting into the engine. I may start implementing shaders for some more advanced detailing as I start to get more features up and running.

I have managed to obtain the Ageia PhysX physics SDK. This will probably take up some of my model researching time to make sure that I pick a format, adapt one, or roll my own that will easily work with the physics code. Many more things to come still... I'm kind of flying by the seat of my pants at the moment, experimenting with all kinds of things, but hopefully I can bring some sane order to it once I'm done playing with things and lock down a solid design and implement it.


The beginnings of a powertrain

Posted by , 03 April 2006 - - - - - - · 162 views

So, I have begun design and implementation of an engine of my own. I have decided to create my own engine for several reasons. The first and most important of those reasons, is because I want to better understand and appreciate what an engine is. Simply using another engine just doesn't quite give you the same appreciation. I also want the experience of having created a large scale project, while creating a dynamic engine that has the abilities and features that I think are important. My last reason is that I want to help revolutionize the gaming industry, and not just repeat the already repeated.

I have broken the design and implementation into several parts. First, I will begin to write the renderer. I have chosen to write this first because it is something that is not necesarilly dependant on the rest of the engine. I have decided to implement the renderer as the baseline and the rest of the engine to interface with the renderer. The renderer will be implemented using OpenGL initially. Once the OpenGL renderer is finished and the rest of the engine is under way, I may toy with implementing a parallel Direct 3D renderer to plug in as well. However, I'm not holding my breath on this one.

As for the audio subsystem, I'm going to go with OpenAL. The OpenAL seems to be a very powerful audio API that is very similar in API to OpenGL. OpenAL also allows for EAX extensions and has been used in some large scale commercial products. I am shooting to implement a full surround sound environment with sound physics, which I will discuss later on as I learn more about the physics SDK and OpenAL.

I would like to attach a physics engine to the engine structure as well. I have been quite attracted to Aegia's PhysX physics SDK by the good word that has been going around about it and their quite astonishing hardware physics processor that is now on the market. If I remember correctly, a modern CPU can support full life-like physics for approximately 10-15 objects simultaneously, whereas the Aegeia PhysX processor is capable of handling 40,000 objects. I vaguely remember this number, so I'd take it with a grain of salt. If you want an idea of what I'm talking about, refer to the Cell Factor demo video.

As for the core of the engine itself, this is still somewhat up in the air. I would like to implement a threaded architecture to support multi-core systems. I would like to seperate the I/O systems, such as the renderer, input, audio from intensive all CPU functions such as physics, AI, and resource loading.

I would like to also write in support for heightmaps for wide open terrain as well as BSP support for tight high resolution and polygon areas. Perhaps merging the two into a unique format for crossing the boundries between tight close quarters or wide open areas.

I have earnestly begun work on the renderer, and have a good chunk in place currently. I have the ability to cache vertex arrays in the renderer, with the option of using vertex buffers to store vertex array data in video RAM. The renderer has basic texturing capabilities and a quite half-assed very very basic lighting implementation just to test it. I will discuss the renderer in a later post, I just wanted to get some stuff out of my head and in writing.





December 2016 »

S M T W T F S
    123
456 7 8910
11121314151617
18192021222324
25262728293031

Recent Entries

Recent Comments

Recent Entries

Recent Comments