Nova’s Achilles heel
Nova’s Achilles heel is the math and a lot needs to be improved there. Instead of attempting to improve I thought I would use a high quality math library instead (in this case vmmlib). Using SVN external I added it so that it would automatically fetch the latest version from their SVN and be done with it. Problem is that I’m using DirectX which uses row major matrices and vmmlib was written to be used with OpenGL which uses column major matrices. So after a huge rewrite and removing the old math headers I tried to run Brick and see if it worked, I had hoped that by setting the effect files to use column major matrices I would let it use one system. Seeing the result, it either didn’t work or something else had gone wrong.
I don’t know what exactly went wrong (besides the obvious) but I guess that instead of using another library I just have to finish and improve my own math library. It’s a good thing SVN allows me to revert.
A good math library is essential for a game developer. It has to be easy to use and clear but I also want answers on the following questions when I’m using it:
- When I define a quaternion is it written as W-X-Y-Z or as X-Y-Z-W?
- Does a multiplication operator between two matrices do a matrix multiplication or an unit multiplication?
- Also how safe are the functions?
- How do I prevent hidden cost?
- And how easy is it to cast from one to another? Vector3<float> to Vector3<int> might seems obvious but it requires code to support it. And I could also cast Vector3 to Vector2.
Then there are the bigger question about optimization, like do we use SIMD or not.
The biggest problem is of course to ensure that all math is correct. Most of the time I use it by instinct, but in this case I actually have to relearn all the math again, double check, ensure that I’m using the right version, triple check. Obviously I experience it as a pain, but it as they say:
No pain, no gain!
Brick4 in week 10
Well, the major glitches in the building creation system are finished. The graphics also look nice now. Anyway a picture says more than a thousand words in this case.

Smart pointers: A dumb idea?
A while ago I wrote a smart pointer for Nova and I have been wondering on whether or not I should use it. And if I did choose to use it to what degree.
Smart pointers are useful, I use the boost variant often enough, but like everything you shouldn’t fully depend on it. Pointers are in many cases good enough. The only thing why smart pointers are so useful is that you don’t have to worry about the lifetime of the object (when the object is destroyed).
In many cases the only time you worry about is when data needs to be shared, for example a texture, as you don’t want to load the same data twice. But another simple technique is the IUnknown interface which creates an object that will delete itself when it has been released as many times as it was created. You still don’t have to worry about the lifetime of the object, you only have to remind you to call release when you no longer need an object. If nothing requires that object the call to release will cause it to delete itself.
Here is the basic code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | /* IBaseObject.h */ class IBaseObject{ private: unsigned int m_ReferenceCounter; protected: IBaseObject(); public: virtual ~IBaseObject() = 0; unsigned int AddRef(); unsigned int Release(); }; /* IBaseObject.cpp */ #include "IBaseObject.h" IBaseObject::IBaseObject() : m_ReferenceCounter(1) { } IBaseObject::~IBaseObject() {} unsigned int IBaseObject::AddRef(){ return ++m_ReferenceCounter; } unsigned int IBaseObject::Release() { --m_ReferenceCounter; if(m_ReferenceCounter == 0) { delete this; } return m_ReferenceCounter; } |
After some thought on the issue I have decided that IUnknown is far superior to a smart pointer. The majority of the smart pointers I have encountered and written work on the same basic principle. Each smart pointer has a pointer to the object and also a pointer to an integer in which you store the amount of copies. That means that if you look at the code it will look something like this:
1 2 3 4 5 6 | template <typename T> class smart_ptr { T* m_Data; unsigned int* m_Copies; }; |
When you think about how smart_ptr really works, you will think how stupid the idea is. For every copy of the object you will have two extra pointers, assuming 32 bit system, that would be 8 bytes extra for every copy.
The second issue is a bit more hidden but if you have this function:
1 2 | class CObject; // Just an object void accidental_copy(smart_ptr<CObject> copyParam) { /** does something **/ } |
You will be making an copy of the object, which means that you will have an extra construct and destruct function to take in account. Which means that a smart pointer not only increases the amount of memory needed, but also reduces the speed. With a pointer you won't have the above problems.
There is however one huge advantage that a smart pointer has but IUnknown doesn't: Smart pointers can work with virtually any type while to use IUnknown the object needs to be derived from it. However since I'm writing the code for Nova I can just make sure that all objects derive from IUnknown.
To add an conclusion, I have decided to before using smart pointers I should first try and see if there is another solution.
Nova – Alpha 1
I have given Nova, my graphics engine, a milestone named “Alpha 1”. This means that the engine will need to fulfill certain conditions at a certain time.
The good news is that I might release it to the public for free (including source) if I find the quality good enough.
I have been using Nova for quite sometime now for my graduation project and except for a few small issues (the math part is not to my liking) I have absolute no issues with it. This is rare since I tend to over criticize my work (I once argued with my teacher after getting the highest possible score because I felt the need to point out the mistakes I made).
If your expecting a fully fledged engine with scene graphs, model loaders and million of other features I think I will have to disappoint you. Nova was intended to be an engine that was in the first place easy to use and in the second place was flexible. At the current state it is a perfect engine for rapid prototyping and highly specialized low level work. But that might change with later iterations. After all this is only the first release (an alpha to boot).
Anyway I will post more information about the possible release over time. The milestone for now has been set on 1 may of 2010.
Brick4 @ 90%
Guess the title says a lot. Brick4 (my graduation project) is nearly finished. Actually I wanted to say it was completely finished but as things goes there are quite a few minor things that need to be done. Also there is a good chance that while writing the paper (which will have my main focus besides the graphical improvement) I come across some new ideas that I feel I must implement. Even as I’m writing this entry there are quite a few ideas that I’m thinking of however finish the paper takes a higher priority.
Another course at school has recently contacted me about the graduation party and although I don’t often go to parties, I’m looking forward to this one. Also because there is a good chance I will be holding a small speech. I'm not shy about being in the spotlight. I know that some people don’t like it but personally I always find it enjoying. The only thing I worry about is the fact that I talk too fast
Maybe the Dutch proverb “Good things come fast” applies to me, after all, I’m currently way ahead of my schedule
“Ordered Programming” Technique
When it comes to programming there many, many techniques. One of the techniques I tried is ordered programming. The idea behind it is that quality is assured and nothing can be forgotten. While I was rewriting Brick I decided to use this technique. What it basically does is that you write a “TODO” in the code, for example: “Load configuration file”. And when you run the application in debug mode, it will break when it arrives at that point.
As you can see this has one huge disadvantage and that is that in order to run the entire program all the “TODO” have to be resolved. There is also one huge advantage which is that after you have done something, you will change “TODO” in to “DONE”. The next time you go trough your application, you will notice that the code has now been documented. This is a huge advantage when you write large pieces of code, which you will rarely though in the near future.
However the disadvantage, having to program in the order that the program runs might be too big in some cases. For example some features will be added later as they are not needed now. For example you have written a OBJ loader that handles triangles, but not quads, in this case you might not want to write the quad loading code just yet as you have to focus on the rendering part. For that reason I have decided to add levels, the lower the level the higher the priority. You can think of it in terms as first todo, second todo, etc. If you reach a stage in your development, where you have done all first todo’s, you just increase the number and see where it breaks then.
Here is an example of how it looks when you are writing todo’s.
1 2 3 4 5 6 7 8 9 | #include "OrderProgramming.hpp" int main(int argc, const char** argv) { ORDER_PROG_TODO("Setup Memory Checkpoint", 0); ORDER_PROG_TODO("Initialize graphics engine", 0); ORDER_PROG_TODO("Run the game", 0); ORDER_PROG_TODO("Shutdown the graphics engine", 0); ORDER_PROG_TODO("Check if there are memory leaks", 0); } |
And here how it looks at a later stage
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | #include <crtdbg.h> #include "OrderProgramming.hpp" class IGraphics; IGraphics* InitGraphicsEngine(); void GameFunction(IGraphics* graphics); int main(int argc, const char** argv) { _CrtMemState memstate; _CrtMemCheckpoint(&memstate); ORDER_PROG_DONE("Setup Memory Checkpoint", 0); IGraphics* myGraphics = InitGraphicsEngine(); ORDER_PROG_DONE("Initialize graphics engine", 0); GameFunction(myGraphics); ORDER_PROG_DONE("Run the game", 0); if(myGraphics) delete myGraphics; ORDER_PROG_DONE("Shutdown the graphics engine", 0); _CrtMemDumpAllObjectsSince(&memstate); ORDER_PROG_DONE("Check if there are memory leaks", 0); } |
And here is the header you will need to include:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 | // OrderProgramming.hpp // Generic functions can be defined before inclusion // - ORDER_PROG_DEBUGBREAK: Breaks the process if possible // - ORDER_PROG_ASSERT: Tries to assert a function // - ORDER_PROG_OUTPUT: Debug output function #ifndef __ORDER_PROGRAMMING_HPP__ #define __ORDER_PROGRAMMING_HPP__ #define ORDER_PROG_STRINGIFY(x) #x #define ORDER_PROG_TOSTRING(x) ORDER_PROG_STRINGIFY(x) #define ORDER_PROG_FILE __FILE__ #define ORDER_PROG_LINE ORDER_PROG_TOSTRING(__LINE__) #define ORDER_PROG_HERE ORDER_PROG_FILE"("ORDER_PROG_LINE") : " #ifndef ORDER_PROG_DUMMY # define ORDER_PROG_DUMMY() {(void)0;} #endif // Lower than this level will cause the order to be taken in account #ifndef ORDER_PROG_LEVEL # define ORDER_PROG_LEVEL 1 #endif #ifndef ORDER_PROG_DEBUGBREAK # if defined(_WIN32) # include <intrin.h> # define ORDER_PROG_DEBUGBREAK __debugbreak(); // # elif (???) # else # define ORDER_PROG_DEBUGBREAK {__asm{int 3};} # endif #endif // Platform independent (most of the time) #ifndef ORDER_PROG_ASSERT # include <cassert> # define ORDER_PROG_ASSERT(expression) assert(expression); #endif // Platform dependent #ifndef ORDER_PROG_OUTPUT # if defined(_WIN32) # include <windows.h> # define ORDER_PROG_OUTPUT(output) OutputDebugStringA((output)); //# elif (???) //# define ORDER_PROG_OUTPUT(output) ::std::clog << (output); # else # define ORDER_PROG_OUTPUT(output) ORDER_PROG_DUMMY() # endif #endif #ifdef ORDER_PROG_NOBLOCK # define ORDER_PROG_BLOCK(reason) ORDER_PROG_DUMMY(); #else # define ORDER_PROG_BLOCK(reason) ORDER_PROG_ASSERT(0 && (reason)) #endif #define ORDER_PROG_TODO(reason, level) { if((level) < ORDER_PROG_LEVEL){ ORDER_PROG_DEBUGBREAK; ORDER_PROG_BLOCK(reason) } } #define ORDER_PROG_TODO0(reason) ORDER_PROG_TODO(reason, 0); #define ORDER_PROG_DONE(reason, level) { if( (level) < ORDER_PROG_LEVEL) { static bool _once=true; if(_once) { ORDER_PROG_OUTPUT(ORDER_PROG_HERE); ORDER_PROG_OUTPUT(reason); ORDER_PROG_OUTPUT("\n"); _once = false; } } } #define ORDER_PROG_DONE0(reason) ORDER_PROG_DONE(reason, 0) #endif // __ORDER_PROGRAMMING_HPP__ |
Learning Java
Recently I had a small discussion with a friend about what language he should choose for a personal project of him. He was doubting between Visual Basic and C#. I believe he decided to go with C# as that would be compatible with Mono (a cross platform variant of C#) and well, an other reason was that is another language the he normally used.
Personally I don’t like working in VB as it has some, not quirks, but behavior issues that don’t match my style of programming (Comments are done different for one).
I do like C# a lot as it looks like C++ (call me shallow if you want), but I have worked in that language for quite some time at my previous job and I found that the Graphical User Interface used by Windows (.NET components) to be a little bit lacking. Now I could buy a professional components (like Devexpress) but I’m not that rich and frankly my experience with it has not yet been enjoyable, most likely because I’m still getting used working with (at my job).
Recently my interest in Linux has been rekindled thanks to Linux Mint and I decided that if I’m going to write software I don’t wish to be limited to one platform. By the way, I only share this view as a software developer and not as a game developer.
After some thinking I decided to try out Java. The criticism that I had about Java 6 years ago have all been gone, so I decided to check it out again.
I have been playing around with it that past three days (1~2 hours each day) and I’m enjoying. Eclipse is an awesome IDE and the only thing I really find lacking is the fact that I can’t drag and drop components as it is done in VS2008. Not that I mind because creating a form (a shell in Java) is quite easily done. I still have a lot of thinking to do about the application I want to write (it’s one of those applications you try to write from time to time) and see if I can actually get it to work.
The programmer IDE
Everyone in the game industry knows the console wars (also because it is a recurring war
) but not many know about the editor war. The editor war was a war that played between two major editors (mostly available on Linux) which were vi and Emacs.
When I look at how code is written I always find myself a bit annoyed as the IDE (Integrated development environment) is basically nothing more than notepad with some extra features. Nothing wrong with notepad or the many IDE’s out there.
But I have always thought that code has one major weakness: Code is always written in a linear fashion. This is not inherent to only code, but a facet of how language works. In the majority of the languages we write from left to right and then from top to bottom. Some languages write in a different order, but always in one direction and then in the other.
I have always wondered why this has to be. Ok, I admit the fact that code is often a language (Like for example C++) but most languages have features like branching (if-else for example), and sub functions.
Recently I came across “Code Bubbles” and honestly I love it, it’s because I’m not a Java programmer pure sang (I did Java a long time ago), but this wants to me to make Java my primary language. Anyway here is a screenshot which demonstrates and I think any developer would applaud this.
Marketing and fuzzy distribution
I have mentioned fuzzy logic and random distribution before but I think that it is also being applied in some marketing strategies.
A supermarket chain in our country is giving away collectibles in the form of images of football players (soccer for the Americans). For every, Oh… I don’t know, € 5 , you spend you will get 5 random pictures.
At lunch my mother, who collects them, was going over a list of friends seeing if they have a picture she doesn’t have or the other way around and she noted that certain images nobody seems to have.
If you think about it from a marketing perspective it makes a lot sense why she doesn’t have certain images. The marketing strategy is all about making you willing to pay a certain amount or attract customers because you want those pictures. Once you have them, the marketing strategy will no longer have affect on you. It is in interest of the marketing company to try and keep you as long as possible under the effect of the marketing strategy.
One way of doing it by doing an imbalanced distribution: Certain pictures will not be distributed in certain locations. That way the customer under the effect of marketing will keep buying. Of course this won’t hold in the long run, so what you do is slowly slide the distribution.
Sliding random distribution:
A distribution whose content is randomized will only distribute a small subset which moves over the entire distribution so that in the end the entire content was equally distributed, however if a moment in time was used only a small subset would have been available.
If you have trouble understanding think of it as traveling from the north pole to the south pole visiting every restaurant you can find. At every stop you will select one random soup on the menu. Each place has different flavors and as you go more and more south you will encounter different flavors. However because your are “sliding” you will often encounter the same soup.
Anyway if that wasn’t clear here is it in code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 | #include <iostream> #include <algorithm> #include <iomanip> #define MAX_AMOUNT 100 #define SLIDER_SIZE 5 /* Uncomment the next line to see the values in random order */ #define USE_RANDOM_ORDER class SlidingDistribution{ int m_Values[MAX_AMOUNT]; int m_SliderBegin; int m_SubPos; public: SlidingDistribution() : m_SliderBegin(0), m_SubPos(0) { for(int i =0; i < MAX_AMOUNT; ++i) { m_Values[i] = i; } #ifdef USE_RANDOM_ORDER std::random_shuffle(m_Values, m_Values + MAX_AMOUNT); #endif } int GetRandomNumber(){ m_SubPos += 1; if(m_SubPos % SLIDER_SIZE == 0) { m_SliderBegin = (m_SliderBegin + 1) % MAX_AMOUNT; m_SubPos = 0; } return m_Values[(m_SliderBegin + (m_SubPos % SLIDER_SIZE))%MAX_AMOUNT]; } }; int main(int argc, const char** argv) { SlidingDistribution distribution; int amounts = 0; do{ int alignCounter = 0; while(amounts) { std::cout << std::setfill(' ') << std::setw(3) << distribution.GetRandomNumber(); alignCounter++; if(alignCounter >= 14) { std::cout << "\n"; alignCounter = 0; }else { std::cout << " "; } amounts--; } std::cout << "\n" << "How many values do you wish to see? (type 0 to quit)\nLoops: "; std::cin >> amounts; } while(amounts); return 0; } |
Brick3 has become Brick4
Brick3, my graduation project, will be no longer developed and instead I will be working on Brick4.
The reason I abandon Brick3 is because the system has become too hard to maintain and there are too many pieces of legacy code around for me to quickly improve it. On top of that Brick3 was the first incarnation of the Brick project series in 3D with some advanced features, the new way of memory management (a 37 MB instead of 2 GB). So I had expected I would need to rewrite it, even though I wanted to avoid it.
Brick4 will have unlike its predecessor cleaner code a new way to some things (XML instead of custom file formats) and it will make heavy use of the command and strategy patterns. This will allow me to developer faster and safer in the long run, although I fear that rewriting the application will have an heavy cost and that any advantage I gain will be lost to that.
The good is that this will allow me to revisit some off the old features and make some notes and see if I can improve it.
If I’m lucky everything should be rewritten at the end of this week and with even some more luck I will also have implemented a new feature. Guess time will tell.