Archived

This topic is now archived and is closed to further replies.

CpMan

libGDN project details!

Recommended Posts

What is libGDN? libGDN is a community project aimed at the creation of a flexible, cross-platform, non-api specific general game development library. It's scope may include anything from skeletal animation systems, terrain engines, octree and quadtree or basically anything applicable to the game development process. How is it being released and what is the license? The project is being developed as a SourceForge project using CVS. It is being released under the LGPL. Who's developing it? Anyone who wants to. You can either become a developer on the project through SourceForge (email me or ProgrammerOne to be added to the developer list), or you can email one of us pieces of code (make sure they are fairly up to spec...we don't want to spend hours converting code to fit with the library's struture). What is the structure? There are basically two kinds of code in the library...that that is system or API dependent, i.e. code that contains Windows API function calls, or OpenGL calls, DirectX calls, and that code that is system independent. There are several aims to the structure that will be laid out. First: Minimize the amount of code that must be rewritten when moving modules to new operating systems or changing which system dependent code will be linked in. In fact, the aim is to to have NO changes whatsoever in system independent code (as if that wasn't obvious). Second: Allow the libraries parts to be taken away from the rest of the library and still work properly. Lastly: Allow code to be easily added at any time. Everything also goes under namespace gdn with POSSIBLE nested namespaces, but i'm not sure. This is not only to reduce naming conflicts, but also is used as the second parameter (I think), for BOOST_CLASS_REQUIRE (more on that later). So exactly how do we accomplish this? The whole idea is to isolate system dependant code as much as possible. Using templates, one can swap system dependent elements out of the system without too much trouble. The best way to illustrate this is with an example. The "classic" example that we have come up with is a texture manager. The advantage of this example is that it encompasses a couple layers of code, and would be very useful in many applications as a standalone entity. We need the texture manager to do several things: load bitmaps from a file, manage them internally, (won't be going into this much, as the focus is on interactions between modules) and allow output of texture information. Now you might ask yourself: How do we provide all those services while only having to write one texture manager. The answer is templates. I'll start from the ground up. First there needs to be a loading system for which textures are added to the texture manager's control. Say for instance I want to load a TGA file. This requires two things: getting the data from a file and somehow getting it into the manager. Notice that the loading portion can be split up into two pieces for maximum cross platform usage. It can be split up into a general file I/O system, which is, in many cases, system independent, and a file type (TGA), loading routine. Each of these are encapsulated in a class. The TGA file loader takes as a templated parameter type the general file I/O class this is to be used. The general file I/O class contains simple methods to read and write data from a file...that's all. That way a TGA file loader, a BMP file loader, any file input system written for a specific purpose will work on ANY system provided that the system has a file I/O system written for it. But there is a problem. How do we garuntee that a given file I/O class has the required functions at compile time? Being templated, it's not totally garunteed. For this purpose, class/function signature checking, we're use the boost_concept_check system, specifically the BOOST_CLASS_REQUIRE method. Whenever a function or class member is used in code, boost forces a compiler error if the function's signature does not match. So now that we have a basically garunteed compile time signature checking (not infallible, though...it can't check the return type ), what's next. Now we need to link the file loader, which can load in any supported I/O system, into the texture manager. The texture manager takes a texture loader as a templated class paramater. Again, the texture loader basically needs one garunteed function, a load function that returns a texture in it's most basic form, a stream of bytes and a bit of data specifying size bits per pixel, etc. So now the texture manager can store any texture type that it has a loading class for (multiple types could also be encapsulated in a single class). We could extend this flexibility even further by supporting a default templated type, much like STL, that did not require a loading class. Even further, we could create a templated function inside the manager class that took a texture loader class for types that may not be encapsulated in a single loading class. But enough about loading, what about storing, and more specifically, getting the texture data out. It sort of defeats the purpose if the texture class can only output byte data, since the developer wil have to do his own conversions. Why not extend on the original templated class idea and specify a texture type? The class would need to contain at least three things: a typedef of the type to store data in the texture manager, like LPDIRECT3DTEXTURE8, an input function, which would take a stream of byte data as the image properties, as translate it into the specified texture type, and an output that would do the opposite. Then it's simple, the manager takes a texture type class, and uses the appropriate types to store data in the specified texture format and convert it when inputting from files and outputting to the developer. And again, the use of a default templated type would make it so data is only stored as byte data. It sounds a little complex, but it's not too bad, and the flexibility and interchangability is incredible. But you might not even limit the input/output type model to things like a texture manager. Anything that outputs a mesh to be drawn, like a skeletal animation system, terrain engine, etc. could go along with this model. Basically anything that outputs specific, isolatable piece of data. And it would be easy to output in other formats too...just write another type converter and plug it in. Simple as that. Think of the flexibility. I could take such a system and plug it into virtually anything, a painting program, GDI bitmap display system, military simulator, you name it. It's all about the generalized interchangeability of the system. So I guess that about outlines the basics of the system. One of the team members is writing a basic sample at the moment, that shows the general idea. This form will get extended to the entire engine where applicable. OK that's enough on the structure. What is the code style expected? We're going to be lenient here. As long as it's readable, doesn't use hungarian notation, (please no, we had an overwhelming NO NO NO at the meeting yesterday) we're ok with it. We ask that you capitalize the first word of every variable name and function name and every word thereafter. For example: GetTexture(...), TextureName, Identification, etc. We do suggest however, that you go along with something like Apache http server code style if possible: ) What is the policy on using existing libraries/API's in code? We're going to maintain a fairly open policy on using existing libraries in code right now. The only qualifications that exist are: The library is easily accessible, the library, if platform/API specific, is only used in a code section that is platform/API specific (duh). Otherwise, I can't seem to think of any more restrictions, but things may change if it becomes a problem. What about Documentation? Documentation should be done in two places if possible (please). Internally for what the code does, and externally (in text files), for how it's to be used. Documentation will be stored in a CVS directory (more on that later). (Finally) What is the policy on variable types? Basically all we want is this: Every module, if it's using some special internal variable type, or takes it as input, or whatever, should contain methods to output and intput the variable in some way is a standard c++ type. For instance, a matrix class needs some way to output and input it as say a 4x4 matrix. This is designed so as to reduce the need for conversions between modules. **************************************************************** This is a copy of a design doc that will be posted on the SourceForge site today. If you want to submit, please email me at my sourceforge account or cppman@ufl.edu. Please give me a few days, say until the end of the weekend to get the CVS system set up and write a bit on where files go, etc. Thanks. Matt BTW....if you didn't want to read this because it's too long I don't blame you one bit.... The log of last night's meeting is in CVSROOT in the repository (i'll be putting the rest somewhere later when I figure out where). Gamedev for learning. libGDN for putting it all together. [edited by - CpMan on October 31, 2002 4:30:05 PM] [edited by - CpMan on November 4, 2002 1:43:08 AM] [edited by - CpMan on November 4, 2002 1:44:30 AM]

Share this post


Link to post
Share on other sites
I''d love to help, but...

I could never work on a project which capitalises the first letter of all its variable names. Capitalising class names is necessary, and capitalising functions is okay (although I don''t do it), but variable names?!

BTW, I do have an image class which might be useful if it were cleaned up a bit. It loads jpegs (using IJG''s free library), bmps and raws, and can resample images and perform some blending. It supports 1-4 channels. If you''re interested, when I get time (and you revoke the thing about variable names), I''ll contribute it.

If you want to see what it can do, take a look at the source code to my 4 elements 3 entry Wizard Duel 2 in the image.h and image.cpp files.

____________________________________________________________
www.elf-stone.com

Share this post


Link to post
Share on other sites
Do you mean every variable name, or just the public member variables (whose purpose is debatable anyway)?

For example, should we have

for(int I=0; I<10; ++I){...}

Cédric

Share this post


Link to post
Share on other sites
I see a big problem with the non-standarization of basic concrete class types. Most of the contributions, especially the more complex ones, will need some form of vector class, matrix class, colour class, geometric base functions (intersection, clipping, etc).

If everybody is going to supply his own, you'll get total chaos. Matrix representation A will not be compatible with representation B, so two pieces of code won't cooperate, code A expects ARGB data, while code B uses RGBA, etc. It also means a lot of redundant code, and a huge risk of bugs.

The first thing to do, before attempting such a huge library, is to create a clean and stable framework, including a standard vector and matrix class, colour representation, and a geometric library. The use of those base classes should be mandatory for all contributions. Otherwise, your library will not be homogeneous, but nothing more than a loose collection of code snippets.

/ Yann

[edited by - Yann L on October 31, 2002 5:49:26 PM]

Share this post


Link to post
Share on other sites
OK, about variable names:
I was actually being more specific about member variables of classes....but now that I think of it...functions are only the semi-important standard for naming....even not really at all.

Yann L: Good point about the variable standardization. I suppose that a standard for basic types is in order, matrix, vector, color, etc. I''m wondering about the math library though....I think you should get some basic operations on the types down...vector adding, subtraction, dot product, cross product, and so on...but a complete math library may not be totally neccesary in the beginning. If the basic types are defined, the rest of the math library will probably fall into place over time, although it would be useful to code basic 3D math systems in the beginning. Given the CVS code model, a developer can add advanced operations later on if they are not already supported, to the original basic type classes.

I like the idea about inheritance. It could be useful for things like the I/O classes, etc to have a basic model down. Users could still code a totally different implementation, assuming it contains the required functions/data types.

Thanks for the opinions, keep ''em coming

Gamedev for learning.
libGDN for putting it all together.

Share this post


Link to post
Share on other sites
CVS? I seem to have been seeing that phrase quite often as of late, and I was wondering it refers to, or what it means?

Could someone please explain this to me?

[Edit] eye kahnt shpeel [/Edit]

[edited by - raymondo on October 31, 2002 9:03:07 PM]

Share this post


Link to post
Share on other sites
CpMan:
You don't need an entire math lib. Just create the basic types and make them mandatory. That way, interoperability is guaranteed. Then, for each basic type, define a full set of operators. Use templates for the types, template specialization for often used types can be added later (for better performance).

Basic types would be:

vector types

vector class, 2 components: template<class T> class vector2 { T x,y; ... };
vector class, 3 components: template<class T> class vector3 { T x,y,z; ... };
vector class, 4 components: template<class T> class vector4 { T x,y,z,w; ... };

// convenience typedefs
typedef vector2<float> fvector2;
typedef vector3<float> fvector3;
typedef vector4<float> fvector4;
...etc...

basic operators: add, sub, scalar mul, dotproduct, crossproduct, scalar div, unary + and -, multiple forms of constructors. vector * matrix multiply (various). Stream operators.

basic member functions: normalize, length, sum, polar coordinates.

matrix types
same idea as above: matrix2, matrix3, matrix4.

basic operators: matrix multiply, vector * matrix, subscripting operator.

basic member functions: transpose, invert, coordsys extraction, matrix composition. Matrix construction: identity, rotation matrix, translation matrix, scale matrix.
...etc...

Well, you get the idea - just the basics, no specialized stuff.

For the geometric lib, create some basic functions like clipping (Sutherland-Cohen, Sutherland Hodgman, etc), intersection tests (ray-triangle, ray-sphere, triangle-triangle, ray-box, etc) and perhaps some special functions like CW/CCW test, point-in-poly test, etc.

/ Yann

[edited by - Yann L on October 31, 2002 9:49:44 PM]

Share this post


Link to post
Share on other sites
CpMan: I was going to talk to you about standarized types ...
but Fruny beat me to it
I''ll write a basic vector, matrix thing (also to show off style)..

yeah - I was going to talk about inheritance on the IO system, but *sigh* fruny beat me to it again..

IO system should be runtime polymorphism anyway.

Also: I also think that people didnt'' understand the caps thing. we really mean..

  
class SomeClass {
private:
int SomeVariable;
public:
void DoSomething();
};

void SomeClass::DoSomething() {
int i,j,k;
int var, blah, zaasss;
double some_d;
SomeOtherClass some_class;
// we don''t really care, so long the public interface

// follows the style...

}

Share this post


Link to post
Share on other sites
Having already written a vector class that does exactly this, I might as well post it here. However, be warned, it is written for *speed*, not understanding. On VC6,7 and GCC2.9.5,3.2+ generally all the operations are done without use of temporaries.

So you can write something like this:

  
Vector<float,3> v1(5,4,4);
float blah[3] = {0,1,2};
Vector<float,3> v2(blah);
Vector<float,3> v3;

cout << "v1: " << v1 << endl;
cout << "v2: " << v2 << endl;
v3 = v1 + v2 - (v2/4);
cout << "v3: " << v3 << endl;

and that statement will be optimized extremely well without extraneous temporaries, etc.

Class follows here:

  
namespace mathLib
{
#define inline __forceinline

// Vector class

template <class vecType,u32 vecSize>
class Vector
{
vecType m_vals[vecSize];
public:
typedef Vector<vecType,vecSize> myVec;

inline Vector()
{
}

inline Vector(const myVec &vector)
{
*this = vector;
}

inline Vector(const vecType vector[vecSize])
{
*this = vector;
}

inline Vector(const vecType t1,
const vecType t2 = 0,
const vecType t3 = 0,
const vecType t4 = 0)
{
if (vecSize > 0) m_vals[0] = t1;
if (vecSize > 1) m_vals[1] = t2;
if (vecSize > 2) m_vals[2] = t3;
if (vecSize > 3) m_vals[3] = t4;
}

inline const vecType operator[](int pos) const
{
return m_vals[pos];
}

inline vecType &operator[](int pos)
{
return m_vals[pos];
}

template<class T>
struct assignment
{
template<u32 I,class R>
struct recurse
{
enum { COUNTER = I+1 };
static inline void Assign(myVec &V,const T &A)
{
V[I] = A[I];
recurse<COUNTER,int>::Assign(V,A);
}
};
template<> struct recurse<vecSize,int>
{
static inline void Assign(myVec &V,const T &A) { }
};
static inline void Assign(myVec &V,const T &A)
{
recurse<0,int>::Assign(V,A);
}
};

inline myVec &operator=(const myVec &vector)
{
if (this != &vector)
{
assignment<myVec>::Assign(*this,vector);
}
return *this;
}

struct assignmentArray
{
template<u32 I,class R>
struct recurse
{
enum { COUNTER = I+1 };
static inline void Assign(myVec &V,const vecType A[vecSize])
{
V[I] = A[I];
recurse<COUNTER,int>::Assign(V,A);
}
};
template<> struct recurse<vecSize,int>
{
static inline void Assign(myVec &V,const vecType A[vecSize]) { }
};
static inline void Assign(myVec &V,const vecType A[vecSize])
{
recurse<0,int>::Assign(V,A);
}
};

inline myVec &operator=(const vecType vector[vecSize])
{
assignmentArray::Assign(*this,vector);
return *this;
}

struct addition
{
template<u32 I,class R>
struct recurse
{
enum { COUNTER = I+1 };
static inline void Assign(myVec &V,const myVec &A,const myVec &B)
{
V[I] = A[I] + B[I];
recurse<COUNTER,int>::Assign(V,A,B);
}
};
template<> struct recurse<vecSize,int>
{
static inline void Assign(myVec &V,const myVec &A,const myVec &B) { }
};
static inline void Assign(myVec &V,const myVec &A,const myVec &B)
{
recurse<0,int>::Assign(V,A,B);
}
};

inline myVec operator+(const myVec &vector) const
{
myVec tmp;
addition::Assign(tmp,*this,vector);
return tmp;
}

inline myVec &operator+=(const myVec &vector)
{
addition::Assign(*this,*this,vector);
return *this;
}

struct subtraction
{
template<u32 I,class R>
struct recurse
{
enum { COUNTER = I+1 };
static inline void Assign(myVec &V,const myVec &A,const myVec &B)
{
V[I] = A[I] - B[I];
recurse<COUNTER,int>::Assign(V,A,B);
}
};
template<> struct recurse<vecSize,int>
{
static inline void Assign(myVec &V,const myVec &A,const myVec &B) { }
};
static inline void Assign(myVec &V,const myVec &A,const myVec &B)
{
recurse<0,int>::Assign(V,A,B);
}
};

inline myVec operator-(const myVec &vector) const
{
myVec tmp;
subtraction::Assign(tmp,*this,vector);
return tmp;
}

inline myVec &operator-=(const myVec &vector)
{
subtraction::Assign(*this,*this,vector);
return *this;
}

struct multiplication
{
template<u32 I,class R>
struct recurse
{
enum { COUNTER = I+1 };
static inline void Assign(myVec &V,const myVec &A,const vecType value)
{
V[I] = A[I] * value;
recurse<COUNTER,int>::Assign(V,A,value);
}
};
template<> struct recurse<vecSize,int>
{
static inline void Assign(myVec &V,const myVec &A,const vecType value) { }
};
static inline void Assign(myVec &V,const myVec &A,const vecType value)
{
recurse<0,int>::Assign(V,A,value);
}
};

inline myVec operator*(const vecType value) const
{
myVec tmp;
multiplication::Assign(tmp,*this,value);
return tmp;
}

inline myVec &operator*=(const vecType value)
{
multiplication::Assign(*this,*this,value);
return *this;
}

struct division
{
template<u32 I,class R>
struct recurse
{
enum { COUNTER = I+1 };
static inline void Assign(myVec &V,const myVec &A,const vecType value)
{
V[I] = A[I] / value;
recurse<COUNTER,int>::Assign(V,A,value);
}
};
template<> struct recurse<vecSize,int>
{
static inline void Assign(myVec &V,const myVec &A,const vecType value) { }
};
static inline void Assign(myVec &V,const myVec &A,const vecType value)
{
recurse<0,int>::Assign(V,A,value);
}
};

inline myVec operator/(const vecType value) const
{
myVec tmp;
division::Assign(tmp,*this,value);
return tmp;
}

inline myVec &operator/=(const vecType value)
{
division::Assign(*this,*this,value);
return *this;
}

struct dotProduct
{
template<u32 I,class R>
struct recurse
{
enum { COUNTER = I+1 };
static inline vecType Assign(const myVec &A,const myVec &B)
{
return
A[I] * B[I] +
recurse<COUNTER,int>::Assign(A,B);
}
};
template<> struct recurse<vecSize,int>
{
static inline vecType Assign(const myVec &A,const myVec &B)
{
return 0;
}
};
static inline vecType Assign(const myVec &A,const myVec &B)
{
return recurse<0,int>::Assign(A,B);
}
};

inline vecType Dot(const myVec vector) const
{
return dotProduct::Assign(*this,vector);
}

inline Vector<vecType,3> Cross(const Vector<vecType,3> vector) const
{
myVec tmp;
tmp[0] = m_vals[1] * vector[2] - m_vals[2] * vector[1];
tmp[1] = m_vals[2] * vector[0] - m_vals[0] * vector[2];
tmp[2] = m_vals[0] * vector[1] - m_vals[1] * vector[0];
return tmp;
}

inline vecType Length() const
{
return (vecType)sqrt(dotProduct::Assign(*this,*this));
}

inline myVec Normalize() const
{
myVec tmp;
vecType length = 1 / Length();
multiplication::Assign(tmp,*this,length);
return tmp;
}
};

#undef inline
};

Share this post


Link to post
Share on other sites
quote:

You could even parameterize the dimensionality of a vector in a template


I specifically didn''t do that, because I wanted direct access to .x, .y, .z and .w members (for readability). There might be some sneaky way to achieve that with templates, but I was lazy and you now how they say, keep it simple stupid...

Share this post


Link to post
Share on other sites
Believe it or not...that''s what risingdragon has written so far, parameterization of the vector size. Yann L has a point though...it will make messy code if things can''t be accessed easily though .x.y.z.w, What are your opinions rd. I''d say go with 3 templated vector types..2,3, and 4 components...unless....What about keeping the existing class, then defining 3 "custom types" of 2 3 and 4 components based on this class...ie

template struct Vector3 {
public: Vector Vec;

//map Vec[0][1][2] to x,y and z
stdtype x,y,z;
//then on any operation, reset x,y,z.

TGhis would be slow though. It would be better to just overload the -> operator. That seems like a good idea to me. What does everyone think. It''s not a major change and doesn''t limit flexibility.


Gamedev for learning.
libGDN for putting it all together.

Share this post


Link to post
Share on other sites
That''s a good point Yann L.
Well, well, well: Premandrake that is good code..
I''m thinking why should I finish off implementing all those operators when you''ve already done it?

I''m thinking of using your code if that''s okay. You''ll get credit of course, for that part of it...I''m just going to put the namespaces in the overall GDN namespace, standarize the caps and naming, is all basically.

I await your approval
In the meantime, I''m going to take up Yann L''s challenge and see if I can get me some .x, .y, and .z''s...

Share this post


Link to post
Share on other sites
Here we go. I've made a test program showing off how to make .x, .y, and .z's appear in the vector class depending on template paramater:

    
#include <stdlib.h>
#include <iostream>

template <class T, std::size_t Dim> class vec_helper {
public:
vec_helper(T data[Dim]) {}
};

template <class T> class vec_helper<T,2>
{
public:
vec_helper(T data[2]) : x(data[0]), y(data[1]) {}

T& x;
T& y;
};

template <class T> class vec_helper<T,3>
{
public:
vec_helper(T data[3]) : x(data[0]), y(data[1]), z(data[2]) {}

T& x;
T& y;
T& z;
};


template <class T, std::size_t Dim> class vec : public vec_helper<T,Dim> {
public:
vec() : vec_helper<T,Dim>(data) {}
T& operator[](std::size_t i) {
return data[i];
}
private:
T data[Dim];
};



using namespace std;

int main() {
vec<float,2> test;
test[0] = 1;
test[1] = 1.5;
cout << test[0] << " " << test[1] << endl;
cout << test.x << " " << test.y << endl;
test.x = 2;
test.y = 4;
cout << test[0] << " " << test[1] << endl;
cout << test.x << " " << test.y << endl;
vec<float,3> test2;
test2[0] = 1;
test2[1] = 1.5;
test2[2] = 3;
cout << test2[0] << " " << test2[1] << " " << test2[2] << endl;
cout << test2.x << " " << test2.y <<" " << test2.z << endl;
test2.x = 2;
test2.y = 4;
test2.z = 5;
cout << test2[0] << " " << test2[1] << " " << test2[2] << endl;
cout << test2.x << " " << test2.y << " " << test2.z << endl;

// cout << test.z;

// FAILS! .z member doesn't exist in test, which is a vec<float,2>


system("PAUSE");
return 0;
}


This prints out:
1 1.5
1 1.5
2 4
2 4
1 1.5 3
1 1.5 3
2 4 5
2 4 5
Press any key to continue . . .

and of course, if users wish to have a .x, .y, .z, and .w members for a 4 dim vector they merely need to specialize the template vec_help.

BTW: names, caps, etc are not libGDN standard. This is a quick hack.

EDIT: fixed Dim variable in specializations...
-----------------------------
Gamedev for learning.
libGDN for putting it all together.

[edited by - risingdragon3 on November 2, 2002 2:19:59 AM]

Share this post


Link to post
Share on other sites
Rising Dragon - You know, I usually don't beat people (up), but since you ask so nicely :

/me beats risingdragon3 senseless with a C++ manual.

Documents [ GDNet | MSDN | STL | OpenGL | Formats | RTFM | Asking Smart Questions ]
C++ Stuff [ MinGW | Loki | SDL | Boost. | STLport | FLTK | ACCU Recommended Books ]


[edited by - Fruny on November 2, 2002 2:30:51 AM]

Share this post


Link to post
Share on other sites
Risingdragon3: Of course you may use the code . I will post my matrix class as well if there is any interest, but I don''t want to put it on here as it''s quite large and the page would probably require 5 minutes to load .

Share this post


Link to post
Share on other sites
Just noticed I forgot the stream overload stuff, it follows here:

  
#include <iostream>

namespace mathLib
{
// Stream connectivity

template <class vecType,u32 vecSize>
std::ostream &operator<<(std::ostream &stream,const Vector<vecType,vecSize> &vector)
{
stream << ''('';
for (int i=0; i<vecSize; i++)
{
if (i!=0) stream << '','';
stream << vector[i];
}
stream << '')'';
return stream;
}
}

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
I use class names that start with a captial and all variables start with lowercase letters. Then you can create variables with the same name as the class:


Image image;


You might say you should use a more descriptive name, but it is a common thing to do in a narrow scope.

As a side note, I think you should break up or shorten your "What is the structure?" section of the details. I really wanted to read it, but it was too long and difficult to read because it was one large block of text.

Share this post


Link to post
Share on other sites