Tuesday, 21 February 2012

A bit of Parsing

In yesterdays post I taked about some updates to the ShaderLib to allow registering of the uniforms, and as I was driving into work I decided to add some more code to parse the shader source and look for any lines containing the word uniform and automatically add it to the uniforms list.

The glsl spec contains a full list of the grammar for glsl, and we can break down the declaration of uniforms to the following
// easy case
// uniform type name;
uniform vec3 position;

// more difficult case
// uniform type name[ ];

#define numLights 10
uniform Lights lights[numLights];

//or

uniform Lights lights[3];

The simple case of a single uniform is easy to parse and can be accomplished by reading the shader source and looking for lines containing the uniform keyword.

The array version however is more complex as we need to determine if we have one of two different cases. Either a hard coded array value or a constant defined by a #define statement.

The lucky thing is that the #define must always be executed before it is used, so we can store these values and do a lookup on them.

The downside of this is the fact that each element of the array must be registered.

This is further compounded by the fact that the data type may be a structure. I'm not going to worry about the structure side of things and only worry with accessing normal arrays of the type uniform vec3 points[3] for example.

Lets get a boost++
In general C++ string processing is not that good, however there are several really good libraries in boost that allow loads of different string processing. For this example I'm going to use boost::tokenizer ,boost::formatboost::split and boost::lexical_cast, all of these are header only templated classes so don't require external compiled libs and all work well with the usual stl containers.

Overview of the process
I've created a new method for the ShaderProgram class which will loop for each attached shader and grab the shader source code string (the whole shader is stored as a single std::string ) this is then split based on the tokens \n\r and a space.
Each line is then searched to find the following keywords "uniform" and "#define". The code to do this is as follows.
void ShaderProgram::autoRegisterUniforms()
{

  unsigned int size=m_shaders.size();
  const std::string *source;
  std::vector<std::string> lines;

  boost::char_separator<char> sep(" \t\r\n");
  typedef boost::tokenizer<boost::char_separator<char> > tokenizer;

  for(unsigned int i=0; i<size; ++i)
  {
   /// first grab all of the shader source for this program
    source=m_shaders[i]->getShaderSource();
    // and split on new lines
    boost::split(lines, *source, boost::is_any_of("\n\r"));

    // now we loop for the strings and tokenize looking for the uniform keyword
   // or the #define keyword
   std::vector<std::string>::iterator start=lines.begin();
    std::vector<std::string>::iterator end=lines.end();
    std::map<std::string,int> defines;

    while(start!=end)
    {
      if(start->find("#define") !=std::string::npos)
      {
        int value;
        std::string define;
        if( parseHashDefine(*start,define,value) )
         {
          defines[define]=value;
        }
      }
      // see if we have uniform in the string
      else if (start->find("uniform") !=std::string::npos)
      {
        parseUniform(*start,defines);
      }
      // got to the next line
      ++start;
     }
  }
}
To find if the string contains our keywords we use the std::string find method which will return a value not equal to std::string::npos if found (basically the index but we don't need that).

If this is found we can process the respective values.
Parsing #define
To store any #define values I'm going to use a std::map<std::string, int>  to store the value, and also make the assumption that we will always have the form #define name [int]. This is a very safe assumption as array subscripts must always be positive and in the case of glsl most implementations only allow very small arrays.

The overall process is quite simple, I tokenise the string as above, however now we copy this to a std::vector <std::string>  using the assign method and then use the subscript to access the elements we want, as shown below

bool ShaderProgram::parseHashDefine(
                                    const std::string &_s,
                                    std::string &o_name,
                                    int &o_value
                                    ) const
{
  // typedef our tokenizer for speed and clarity
  typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
  // these are the separators we are looking for (not droids ;-)
  boost::char_separator<char> sep(" \t\r\n");
  // generate our tokens based on the separators above
  tokenizer tokens(_s, sep);
  // now we will copy them into a std::vector to process
  std::vector <std::string> data;
  // we do this as the tokenizer just does that so we can't get size etc
  data.assign(tokens.begin(),tokens.end());
  // we are parsing #define name value so check we have this format
  // we should as the glsl compiler will fail if we don't but best to be sure
  if(data.size() !=3)
  {
    return false;
  }
  else
  {
    //            data [0]     [1]   [2]
    // we are parsing #define name value
    o_name=data[1];
    o_value=boost::lexical_cast<int> (data[2]);
    // all was good so return true
    return true;
  }
}
You will notice that the boost::lexical_cast is used to convert the string into an integer, and that both the values are returned to the main program and stored in the defines map ready to be passed into the next function to process the uniforms.
ParseUniform method
The parseUniform method is split into two sections, we first check to see if the string has a [ or not as this will depend upon the parse method. If no [] is present we can process in a similar way to above, however if there is an array we need to further refine the parse, and then loop and register all the uniforms. For example the following glsl code
#define numIndices 4
int indices[numIndices];
would need all the uniforms registered so we would register
indices[0]
indices[1]
indices[2]
indices[3]
The following code shows the complete method
void ShaderProgram::parseUniform(
                                  const std::string &_s,
                                  const std::map <std::string,int> &_defines
                                )
{
 typedef boost::tokenizer<boost::char_separator<char> > tokenizer;

 // first lets see what we need to parse
 if(_s.find("[") ==std::string::npos)
 {
   boost::char_separator<char> sep(" \t\r\n;");
   // generate our tokens based on the seperators above
   tokenizer tokens(_s, sep);
   // now we will copy them into a std::vector to process
   std::vector <std::string> data;
   // we do this as the tokenizer just does that so we can't get size etc
   data.assign(tokens.begin(),tokens.end());
   // uniform type name
   // we should as the glsl compiler will fail if we don't but best to be sure
   if(data.size() >=3)
   {
     registerUniform(data[2]);
   }
 }
 else
 {
   boost::char_separator<char> sep(" []\t\r\n;");
   // generate our tokens based on the separators above
   tokenizer tokens(_s, sep);
   // now we will copy them into a std::vector to process
   std::vector <std::string> data;
   // we do this as the tokenizer just does that so we can't get size etc
   data.assign(tokens.begin(),tokens.end());
   // uniform type name
   // we should as the glsl compiler will fail if we don't but best to be sure
   if(data.size() >=3)
   {
     // so in this case data[3] is either a number or a constant
     int arraySize=0;
     // so we try and convert it if it's not a number
     try
     {
       arraySize=boost::lexical_cast<int> (data[3]);
     }
     // catch and lookup in the uniform array
     catch(boost::bad_lexical_cast)
     {
       std::map <std::string, int >::const_iterator def=_defines.find(data[3]);
       if(def !=_defines.end())
       {
         arraySize=def->second;
       }
     } // end catch
    // now loop and register each of the uniforms
     for(int i=0; i<arraySize; ++i)
     {
       // convert our uniform and register
       std::string uniform=boost::str(boost::format("%s[%d]") %data[2] %i);
       registerUniform(uniform);
     }
   }

 }
}
Most of the code is similar to the other parse methods, however we use the fact that boost::lexical_cast throws and exception to check to see if we have a number or a string as the size of the array, first I try the cast, if this throws a boost::bad_lexical_cast exception we look to see if we have the value stored in our defines list.
Conclusions and Future work
This works quite well and is fairly robust (as the glsl compiler will fail if the code is wrong anyway), however as mentioned above it doesn't cope with registering uniforms which have structures. I may refine this at a later date to cope with these but it would require storing each structure and element of that structure and registering each element.

This will eventually be made redundant due to the use of the new glsl Uniform Buffer Objects however at present my mac doesn't support them so will have to wait.

I may also just write a full glsl parser using the excellent boost::spirit library one day as an exercise in  parser writing (as I quite enjoy writing parsers)

This code has been integrated into ngl and the latest source tree has been updated to inclue it all.

Monday, 20 February 2012

ngl::ShaderLib update

Just a quick post to explain some updates to the ngl::ShaderLib sub-system. First of all this doesn't break any existing code, but has in some situations increased frame rate by 10-15%.

First a bit of background, all of the shader lib functions use the getUniformLocation function to query the location of the uniform in the current program and use this GLuint offset to load the values to the uniform in the shader.

This works ok, and generally this only called at the start of the program so doesn't cause too much of a bottleneck. However in most of the demos we load the Model/View/Projection matrix to the Shader each time we move something, and the MVP value will be loaded using the following code (from the ngl:: source)

void ShaderProgram::setUniformMatrix4fv(
                                          const char* _varname,
                                          size_t _count,
                                          bool _transpose,
                                          const float* _value
                                        ) const
{
  glUniformMatrix4fv(getUniformLocation(_varname),_count,_transpose,_value);
}
Here the getUniformLocation is called and doesn't need to be if we pre-cache the value. To do this ngl::ShaderProgram has a new attribute added to it as follows
std::map <std::string, GLuint> m_registeredUniforms;
To use this, once the shader is loaded, compiled and linked we must set it to be active and we can then register the uniform as follows (this will usually be done in the GLWindow::initalizeGL method)
(*shader)["MultipleLights"]->use();
shader->setShaderParam1f("Normalize",1);
shader->registerUniform("MultipleLights","MVP");
shader->registerUniform("MultipleLights","MV");
shader->registerUniform("MultipleLights","M");
shader->registerUniform("MultipleLights","normalMatrix");
shader->registerUniform("MultipleLights","viewerPos");

This will register the uniforms, now when we load the matrix values to the shaders, we can use the "registered" versions of the functions which will look up the values in the map and use the uniform's stored there. This will work unless the shader source is changed and re-compiled. In that case they will need to be re-registered.

void GLWindow::loadMatricesToShader(
                                     ngl::TransformStack &_tx
                                   )
{
  ngl::ShaderLib *shader=ngl::ShaderLib::instance();
  (*shader)["MultipleLights"]->use();
  ngl::Matrix MV;
  ngl::Matrix MVP;
  ngl::Mat3x3 normalMatrix;
  ngl::Matrix M;
  M=_tx.getCurrentTransform().getMatrix();
  MV=_tx.getCurrAndGlobal().getMatrix()*m_cam->getViewMatrix() ;
  MVP=MV*m_cam->getProjectionMatrix();
  normalMatrix=MV;
  normalMatrix.inverse();
  normalMatrix.transpose();
  shader->setRegisteredUniformFromMatrix("MV",MV);
  shader->setRegisteredUniformFromMatrix("M",M);
  shader->setRegisteredUniformFromMatrix("MVP",MVP);
  shader->setRegisteredUniformFromMat3x3("normalMatrix",normalMatrix);
  shader->setRegisteredUniformVec3("viewerPos",m_cam->getEye().toVec3());
}

Operator Overloading in C++ Part 4

previously we have discussed the different types of operator overloading and the differences between the Member operators and Free operators. In this part we are going to implement a basic * Scalar operator which will multiply each of the Vec3 components by a floating point scalar value. In the .h file we can implement the following definition for the operator
/// @brief * float operator
Vec3  operator*(float _rhs) const;
and for the .cpp file the following
/// @brief * float operator
Vec3 Vec3::operator*(float _rhs) const
{
  return Vec3(m_x*_rhs,m_y*_rhs,m_z*_rhs);
}
Testing this using the following code results in
std::cout<<"b*2== "<<b*2<<"\n";
b*2== [4,6,8] 
However if we try to write the following
std::cout<<"2*a ="<<2*a<<" b*2== "<<b*2<<"\n";
We get the following compiler error error: no match for 'operator*' in '2 * a' This is due to the fact that we have no operator for scalar * Vec3. To solve this problem we can generate a Free function version of the operator and a scale method to scale the vector by a scalar value.
/// @param[in} _s the value to scale components by
Vec3 scale(float _s) const;

Vec3 operator *(float _lhs, const Vec3 &_rhs);

/// in the .cpp file
Vec3 operator *(float _lhs, const Vec3 &_rhs)
{
  return _rhs.scale(_lhs);
}
In most cases it will be best to implement both operators as free functions so we would have the following extra methods
Vec3 operator *(float _lhs, const Vec3 &_rhs)
{
  return _rhs.scale(_lhs);
}

Vec3 operator *(const Vec3 &_lhs,float _rhs)
{
  return _lhs.scale(_rhs);
}
As you can see both methods require the extra scale method, however if we use the free function version we can remove the member operator. In some cases this will not be required (for example if the operands are of the same type)

Operator Overloading in C++ Part 3

In the previous post we looked at the mandated member operators, for this part I'm going to look at the different types of operators we can generate and provide examples of each.

For ease I will split this section into relational operators and arithmetic operators. We can represent the relational operators using the following diagram


These operators will always return a boolean value which will be the result of some form of comparison. 


Arithmetic operators will return an object of some type which depending upon the context may be either the same object or a new type of object.  Later we will also add the += style operators which will mutate the current object rather than returning a value.
Equality Operators
In the case of the Vec3 class there are only two equality operator which make sense to implement, these are the == and != operators. Also we must ensure that we are doing correct comparison as we are storing floating point values. ( see here for an in depth discussion of floating point comparison ). For this example I'm going to generate a very simple FCompare macro which will be used to do a comparison via a simple epsilon error value.


//----------------------------------------------------------------------------------------------------------------------
/// @brief define EPSILON for floating point comparison
//----------------------------------------------------------------------------------------------------------------------
#ifndef EPSILON
  const float EPSILON = 0.001f;
#endif

//----------------------------------------------------------------------------------------------------------------------
/// @brief FCompare macro used for floating point comparison functions
//----------------------------------------------------------------------------------------------------------------------
  #define FCompare(a,b) \
      ( ((a)-EPSILON)<(b) && ((a)+EPSILON)>(b) )
In the first example I will produce a "Member operator" for the equality operator, first in the .h file we declare the operator
bool Vec3::operator==(const Vec3 &_rhs)
{
  return FCompare(m_x,_rhs.m_x) &&
         FCompare(m_y,_rhs.m_y) &&
         FCompare(m_z,_rhs.m_z);
}
As you can see in this case we do a component wise FCompare with the current object and the rhs value. We can test this using the following code from the previous examples
if(b==c)
 std::cout<<"b==c is true\n";
else
 std::cout<<"b!=c \n";
if(a==fromFloat)
 std::cout<<"a==fromFloat\n";
else
 std::cout<<"a!=fromFloat\n";
Which gives the following output
testing equality operators 
b==c is true
a!=fromFloat
To generate the != method we do the following

/// in .h
/// @brief != operator
bool operator!=(const Vec3 &_rhs);
/// in .cpp
bool Vec3::operator!=(const Vec3 &_rhs)
{
  return !(*this==_rhs);
}
For the free function operator we have to write some additional code again to allow the free function access to the attributes. In this case we are going to write a method called isEqual ( note that my coding standard says this kind of method should be formed as a question as it returns a bool)
/// @brief function to test equality
bool isEqual(const Vec3 &_rhs) const;
The actual code for this class method will use the same FCompare macro as above and looks like this
bool Vec3::isEqual(const Vec3 &_rhs) const
{
  return FCompare(m_x,_rhs.m_x) &&
         FCompare(m_y,_rhs.m_y) &&
         FCompare(m_z,_rhs.m_z);
}
The free function definitions of the == and != methods are now declared outside of the class but still in the .h file as follows
/// @brief free operator for equality testing
bool operator ==(const Vec3 &_lhs, const Vec3 &_rhs);
/// @brief free operator for equality testing
bool operator !=(const Vec3 &_lhs, const Vec3 &_rhs);
Finally we write the main function code, not that as they are no longer class methods it is no longer valid to mark the methods as const (in fact you get the following error with g++ error: non-member function 'bool operator==(const Vec3&, const Vec3&)' cannot have cv-qualifier )
bool operator ==(const Vec3 &_lhs, const Vec3 &_rhs) const
{
  return _lhs.isEqual(_rhs);
}

bool operator !=(const Vec3 &_lhs, const Vec3 &_rhs)
{
  return !_lhs.isEqual(_rhs);
}

Friday, 17 February 2012

Operator Overloading in C++ Part 2

In the previous post I discussed some of the basics of operator overloading and the difference between "Free Operators" and "Member Operators" and we overloaded the << insertion operator so we could use our Vec3 class with the std::cout method, in this section I'm going to start with the assignment operator which is one of the operators which must be a Member operator.

The assignment Operator
The assignment operator must be a member operator to ensure that they receive an lvalue (basically an expression that refers to the object) as the first operand, also if we follow the "rule of three" we should define a copy constructor as well, in this case we could get away without writing an assignment operator as we don't create any dynamic memory in the class, however good practice dictates it's best to always do this. If we did any dynamic allocation in this class we would need to do a "deep copy". The following code is added to both version of the project code to add the copy constructor and assignment operator.

/// @brief a copy ctor
Vec3(const Vec3 &_v);

/// @brief assignment operator
Vec3 & operator=(const Vec3 &_rhs);
In the body code I've explicitly made calls to print out which method is being called so we can see the order of the operations
Vec3::Vec3(const Vec3 &_v)
{
  std::cout<<"copy ctor called\n";
  m_x=_v.m_x;
  m_y=_v.m_y;
  m_z=_v.m_z;
}

Vec3 & Vec3::operator=(const Vec3 &_rhs)
{
  std::cout<<"assignment operator called\n";
  m_x=_rhs.m_x;
  m_y=_rhs.m_y;
  m_z=_rhs.m_z;
  return *this;
}
We can now run the following code and see them being run
Vec3 a(1,2,3);
Vec3 b;
Vec3 c(2,3,4);
std::cout<<"a "<<a<<" b "<<b<<" c "<<c<<"\n";
std::cout<<"testing the assignment operator\n";
b=c;
std::cout<<b<<"\n";
std::cout<<"testing the copy ctor\n";
Vec3 copy(a);
std::cout<<copy<<"\n";
Which gives the following output
a [1,2,3] b [0,0,0] c [2,3,4]
assignment operator called
[2,3,4]
copy ctor called
[1,2,3]
We can also create more than one assignment operator passing in different types to the right hand side parameter, for example if we had a 4x4 matrix class we could call Mat4x4 v=1.0 to set the values to the identity matrix. The following example will set all of the Vec3 components to the floating point value passed in
/// @brief assignment operator from float
Vec3 & operator=(float _rhs);

in .cpp

Vec3 & Vec3::operator=(float _rhs)
{
  std::cout<<"assignment operator from float called\n";
  m_x=_rhs;
  m_y=_rhs;
  m_z=_rhs;
  return *this;
}
We can now use this to assign our Vec3 from a single float value as shown
Vec3 fromFloat;
fromFloat=0.5;
std::cout<<fromFloat<<"\n";
fromFloat=0.0;
std::cout<<fromFloat<<"\n";
Which gives the following output
assignment operator from float called
[0.5,0.5,0.5]
assignment operator from float called
[0,0,0]
Obviously caution should be applied to this approach in your design, however it is quite common in graphical applications to be able to do this (for example renderman tuple types and glsl Vec types both support this type of assignment so I have used it in ngl)
The [] operator
The [] subscript operator is a useful operator to overload as it will allow assignment and access to the internal elements of the class. This does however have the side effect of exposing these private attributes and break encapsulation.

To access the data we really need to have an array as well as the individual m_x style elements, to do this we can use a union however this will effectively make things public anyway (see this post about how g++ does this but not clang ).  The following code will modify the class so we have a union

union
{
 struct
 {
  float m_x;
  float m_y;
  float m_z; 
 };
 float m_array[3];
};
To write the subscript operator we use the following operator syntax
/// @brief [] operator
float & operator[](unsigned int _index);
And in the .cpp file we add
/// @brief [] operator
float & Vec3::operator[](unsigned int _index)
{
  assert(_index<3 );
  return m_array[_index];
}
You will notice in the above example the use of the assert function (from #include <cassert>) this will abort the program if the index is out of range, in this example as we are using an unsigned int for the index we only need to check for the higher bound as the index can never be negative. However our client code could use an int for the index which we should also check the negative bound, to make a fully safe version we should really write both [int] and [unsigned int] versions of the code. Testin this code with the following program
std::cout<<"testing the  [] operator \n";
fromFloat[1]=99.0;
fromFloat[0]=2;
fromFloat[2]=3;
std::cout<<fromFloat[0]<<" "<<fromFloat[1]<<" "<<fromFloat[2] <<"\n";
fromFloat[5]=2;
Which will give the following output (and abort due to the assert)
testing the  [] operator 
2 99 3
Assertion failed: (_index<3), function operator[], file Vec3.cpp, line 42.
The program has unexpectedly finished.
Note : it is also possible to write the same operator without the union, by using the following code
 return &m_x[_index];
Whilst this will work it does make several assumptions on how the data is packed in your header,in this case we assume packed alignment and that m_x m_y and m_z are contiguous, while this is generally the case it is not the safest way of accessing the data and the union should always ensue that the correct data packing is in place.

Thursday, 16 February 2012

Operator Overloading in C++ Part 1

We introduced the concept of operator overloading today in the first year lecture / lab session and there was quite a lot of confusion, so I've decided to do a more in depth article about it here.

There is a wealth of knowledge online about operator overloading and a good starting point is this article as well as the venerable c++ faq, however I'm going to use the excellent book by Martin Reddy "API Design for C++" as he discusses some interesting differences between the ways we can implement Operator Overloading.

All of the code mentioned in this article can be downloaded from here

So what is Operator Overloading
The main reason to overload operators in your own class design is to make them behave like the built in types (intrinsic types). It can also make things more intuitive when writing our own classes, especially if they are representing something mathematical. For example imagine the following class

We have three attributes of type float in the private section, and (assuming a constructor has been written )  we could write the following code
Vec3 a,b,c;
add(add(mul(a,b), mul(c,d)), mul(a,c))
But that is almost unreadable and the actual mathematic being implemented is $$ a*b+c*d+a*c $$ which is much more readable, and when trying to implement a mathematical function from a paper much easier to check when debugging.

So we can use operator overloading to make our classes work like mathematical data types even when they are not.

However, it is important to only overload operators which seem natural to do so and follow the semantic you would expect. For example it is natural to overload the plus + operator to meen addition or for string based objects concatenation.

Vec3 Class
For the purpose of this discussion I will be using a simple floating point Vec3 class which represents a three tuple vector and will be used to perform mathematical calculations and comparison operations. The basic header file we will be using is
#ifndef VEC3_H_
#define VEC3_H_

class Vec3
{
  public :
  Vec3(
        float _x=0.0f,
        float _y=0.0f,
        float _z=0.0
      ) :
          m_x(_x),
          m_y(_y),
          m_z(_z){;}

  private :
    /// @brief the x element
    float m_x;
    /// @brief the y element
    float m_y;
    /// @brief the z element
    float m_z;
};

#endif
This will allow us to create simple Vec3 classes like this
#include "Vec3.h"

int main()
{
  Vec3 a(1,2,3);
  Vec3 b;
  // use the compilers built in assignment operator
  b=a;

}
In the above example we construct a Vec3 a and assign it some values, and be will be constructed using the default parameter values 0,0,0. By default the compiler will implement a simple assignment operator for us and a==b. (don't worry we will create our own soon).

Free Operators vs Member Operators
Reddy defines two different types of operator, "Free Operators" and "Member Operators" Member operators are members of the class, and as such have full access to the private data areas of the class, there are some operators that can only be implemented using Member Operators ( = [] -> ->* () (T) new / delete) however all the other operators can be implemented in both ways. In this example I'm going to implement both type using two different project files (which can be downloaded from the link at the top of the page).

Free Operators are not part of the class, and are declared as functions separate to the class, this means that the function doesn't have access to the class private data area so we must write some form of accesors for the attributes for it to work, however this does have the advantage of allow us to reduce the coupling of the Vec3 class to our other implementation.

The other advantage of Free Operators is that they give us better symmetry and allow us to do something like 2*V and V*2 in the same code.

Insertion Operator
The first operator I'm going to implement is the << insertion operator, this will allow us to use std::cout to print the contents of our class, this is perhaps not the best one to start with as it actually add extra complication to what we are doing as we need to send data to the std::ostream class and it will need to know about the internal structure of our Vec3 class. The easiest way of doing this, and in most texts the way shown, is by using the friend prefix this indicates that the method is a friend of the class and it can access the private data. This does however break encapsulation and promotes many coding arguments.

The following code will implement the << operator as a member operator in is placed in the Vec3.h
friend std::ostream& operator<<(std::ostream& _output, const Vec3& _v); 
We can now write the implementation code in the Vec3.cpp file
std::ostream& operator<<(
                         std::ostream& _output,
                         const Vec3 & _v
                        )
{
  return _output<<"["<<_v.m_x<<","<<_v.m_y<<","<<_v.m_z<<"]";
}
This will format the string and return it to the ostream object, we can use the code in the following way
#include "Vec3.h"
#include <iostream>

int main()
{
  Vec3 a(1,2,3);
  Vec3 b;
  Vec3 c(2,3,4);

  std::cout<<"a "<<a<<" b "<<b<<" c "<<c<<"\n";
}
And will give the output
a [1,2,3] b [0,0,0] c [2,3,4]
To generate the free operator version we actually have to write a lot more code in the Vec3 Class. As I mentioned earlier the free operator version is defined outside of the class definition, usually I still prefer to keep the definition and the code in the same .h and .cpp files however this is not mandatory anymore as the free operators are not part of the class. The following code shows the prototype of the free operator
std::ostream& operator<<(std::ostream& _output, const Vec3& _v);
Now as this is no longer a friend to our Vec3 class we need to implement a method to gather the data we require and return it to the free operator in this case I'm going to implement the member function getString() as follows
/// @brief a method to get the data as a string
std::string getString() const;
And the code to format the string and return it
#include <sstream>

std::string Vec3::getString() const
{
  std::stringstream string;
  string<<"["<<m_x<<","<<m_y<<","<<m_z<<"]";
  return string.str();
}
Here we construct a stringstream object and pass the formatted data to it. This is then converted to a string and returned from the method. Finally the free operator can access the data from the class as follows
std::ostream& operator<<(
                         std::ostream& _output,
                         const Vec3 &_v
                        )
{
  return _output<<_v.getString();
}
The client program to use the operator will be exactly the same.

Tuesday, 14 February 2012

Getting Started with the Programming Assignment Pt 5 A starmap

In the previous pos I got the ship moving, and on the way to work I decided it would be good to make a better background to the game, so I created a very simple StarMap class which loads two textures and creates two rotating sphere with alpha blended textures. This is shown in the following video and I will write a more detailed explanation when I get a chance. I've also got a new model form Turbo Squid until I get a better one.

The star map image is from here and I recommend this site in general especially the cool geometry stuff here. The planets were taken from here