I've just started playing StarCraft 2 yesterday with my friend. Multi player is definitely more fun than single player mode. I am still learning the tactics and strategies to win a game. Currently, I'm using Terran and starting to get familiar with the game. I'm quite excited now!
When you create a point light in Maya 2010, there isn't any option to fine-tune the point light decay rate. Maya provided several options for decay rate: None, Linear, Quadratic, Cubic. I believe they have some reason to do this but this is actually quite annoying for developing games.

In game development, we often want to control the decay rate's radius of the point light. I spent some time looking around the Internet for the solution. And, I've finally found it! There's a script in creativecrash.com called rampLight (http://www.creativecrash.com/maya/downloads/scripts-plugins/rendering/misc/c/ramplight) that basically does this. It creates a point light with a sphere manipulator attached. You can then just scale the light, it will scale the sphere radius and display correctly! Really useful script!

In the last post, I argued how OOP focuses on interface an abstraction. OOP can be very useful for flexibility and maintenance. This time round, let's focus on other kind of paradigm: Data Oriented Programming (DOP) to squeeze out more performance out of your code.

Data Layout
As I hinted in the last post, with deep pipelines and cache system of current processor design, it is more beneficial if we layout the data contiguously in memory. So, instead of having array of pointer to object, we will layout the data as array of objects. The problem with that is we have different types of objects and each object can have varying size. Thank God, C/C++ allow us to solve this problem with union!

Here's the code that defines the data layout:
class Shape
{
public:
    enum ShapeType
    {
        CIRCLE,
        RECTANGLE
    };
private:
    struct Circle
    {
        float radius;
    };
    struct Rectangle
    {
        float width;
        float height;
    };

    ShapeType m_type;
    union
    {
        Circle circle;
        Rectangle rect;
    } m_data;

public:  
    ShapeType GetType() const { return m_type; }

    void CreateCircle(float radius)
    {
        m_type = CIRCLE;
        m_data.circle.radius = radius;
    }

    void CreateRectangle(float width, float height)
    {
        m_type = RECTANGLE;
        m_data.rect.width = width;
        m_data.rect.height = height;
    }

    void Update()
    {
        switch (GetType())
        {
        case CIRCLE: ...
        case RECTANGLE: ...
        }
    }
};
If you pay attention, there are several things that we need to take care when transforming inheritance hierarchy into union:
  1. Instance Type
    Since we are basically flattening the hierarchy, we now have to embed the type of the instance in the class. This is simply done by creating type enums.
  2. Instance Data as Structs
    For each instance type, we have to declare the instance data as structs. This allow us to easily put the data together in union.
  3. Union for Flattening Instance Data
    This is C/C++ language feature that's useful for interpreting a block of memory as different types.
  4. Factory Methods
    We need to create at least one different factory methods for each instance type. You can think this as flattening the constructors.
  5. Switch Statement for Polymorphism MethodThe effect of flattening virtual functions is having a switch statement. We now have to manually choose the appropriate actions depending of the type of the instance.
Class Size
Another implication we have to pay attention is the size of the class. It is actually: sizeof(m_type) + sizeof(union). This is actually the downside of using union to flatten out inheritance hierarchy: i.e. more space required. The more varied each type's size, the more space we are wasting. For example, if we have a type that only have 1 float and another type that has 10 floats, we can potentially waste 9 floats for each instance.

Conclusion
From one side, it seems these conflicting paradigm is again the battle between space vs time. The most important thing is actually understanding the hardware. By making sure the objects are contiguous in memory, we can take advantage of modern CPU cache system. Some people may argue that the programming paradigm described above is not elegant; just look how we have to have "type" and "switch" statements and adding a new type looks painful (because we have to change several stuffs). However, if you really want to squeeze out performance, you might consider the above technique. The choice depends on the problem you are tackling. And again, the choice is in YOUR hand!