diff --git a/Makefile b/Makefile index cd64120..a01da71 100644 --- a/Makefile +++ b/Makefile @@ -16,7 +16,7 @@ obj/%.o: src/%.cpp mkdir -p $(@D) gcc -std=c++0x -D DEBUG -c $< -o $@ $(INCDIRS:%=-I%) -bin/server: obj/geo2d.o obj/str.o obj/ini.o obj/account.o obj/server/server.o +bin/server: obj/geo2d.o obj/str.o obj/ini.o obj/account.o obj/server/server.o obj/err.o gcc $^ -o $@ -lstdc++ $(SERVERLIBS:%=-l%) $(LIBDIRS:%=-L%) bin/client: obj/geo2d.o obj/ini.o obj/client/client.o obj/util.o obj/client/connection.o obj/str.o obj/err.o obj/client/textscroll.o\ @@ -27,5 +27,5 @@ bin/test3d: obj/test3d/water.o obj/ini.o obj/test3d/hub.o obj/xml.o obj/str.o ob obj/io.o obj/texture.o obj/test3d/mesh.o obj/util.o obj/font.o obj/test3d/collision.o obj/test3d/toon.o gcc $^ -o $@ $(TEST3DLIBS:%=-l%) $(LIBDIRS:%=-L%) -bin/manager: obj/manager/manager.o obj/str.o obj/account.o +bin/manager: obj/manager/manager.o obj/str.o obj/account.o obj/err.o gcc $^ -o $@ -lstdc++ $(MANAGERLIBS:%=-l%) $(LIBDIRS:%=-L%) diff --git a/bin/test3d.zip b/bin/test3d.zip index 019917a..6ccebb2 100644 Binary files a/bin/test3d.zip and b/bin/test3d.zip differ diff --git a/manager.cbp b/manager.cbp index 00be3df..3727dda 100644 --- a/manager.cbp +++ b/manager.cbp @@ -31,6 +31,8 @@ + + diff --git a/notes.txt b/notes.txt index d6ebd9a..b5ad905 100644 --- a/notes.txt +++ b/notes.txt @@ -1,4 +1,4 @@ -Game templates version 1.2.0 +Game templates version 1.3.0 [test3d] diff --git a/server.cbp b/server.cbp index 4784533..223e3ad 100644 --- a/server.cbp +++ b/server.cbp @@ -41,6 +41,8 @@ + + diff --git a/src/account.cpp b/src/account.cpp index 0b7d346..d1f08cf 100644 --- a/src/account.cpp +++ b/src/account.cpp @@ -26,11 +26,17 @@ #include "account.h" #include "str.h" +#include "err.h" #include #define HASHSTRING_LENGTH 20 void getHash(const char* username, const char* password, unsigned char* hash) { + /* + This function is used for checking passwords. + If it changes, all account files must be created anew. + */ + SHA_CTX ctx; SHA1_Init(&ctx); SHA1_Update(&ctx,(void*)username,strlen(username)); @@ -41,14 +47,9 @@ void getHash(const char* username, const char* password, unsigned char* hash) void AccountFileName (const char *directory, const char *username, char *out) { - sprintf(out,"%s%c%s.account", directory, PATH_SEPARATOR, username); -} + // .account -std::string accountError; - -const char *GetAccountError () -{ - return accountError.c_str(); + sprintf(out,"%s%c%s.account", directory, PATH_SEPARATOR, username); } #define LINEID_ACCOUNT "ACCOUNT" @@ -59,30 +60,40 @@ bool MakeAccount (const char* dirPath, const char* username, const char* passwor _password[PASSWORD_MAXLENGTH]; unsigned char hash[HASHSTRING_LENGTH]; + /* + username is case insensitive, thus always converted to lowercase + password is case sensitive + */ int i; - for(i=0;username[i];i++) + for (i=0;username[i];i++) _username[i]=tolower(username[i]); _username[i]=NULL; - for(i=0;password[i];i++) - _password[i]=tolower(password[i]); - _password[i]=NULL; + strcpy (_password, password); + + // Open the account file AccountFileName (dirPath, _username, filepath); - FILE* f=fopen (filepath, "wb"); - if(!f) + FILE* f = fopen (filepath, "wb"); + if (!f) { - accountError = std::string("cannot open output file ") + filepath + ": " + strerror(errno); + std::string _errorstring = std::string ("cannot open output file ") + filepath + ": " + strerror (errno); + SetError (_errorstring.c_str ()); return false; } - getHash(_username, _password, hash); - fprintf(f,"%s %s ",LINEID_ACCOUNT,_username); + /* + Write a single line, including + the username and password hash. + */ + + getHash (_username, _password, hash); + fprintf (f, "%s %s ", LINEID_ACCOUNT, _username); // write each hash byte - for(i=0; i #include -#define ERRSTR_LEN 512 +#define ERRSTR_LEN 16384 // String to store the error in: -char error [ERRSTR_LEN] = ""; +char error [ERRSTR_LEN] = "", + tmp [ERRSTR_LEN]; const char *GetError () { @@ -35,16 +36,17 @@ const char *GetError () } void SetError (const char* format, ...) { - // use a temporary buffer, because 'error' might be one of the args - char tmp [ERRSTR_LEN]; - // Insert args: va_list args; va_start (args, format); - vsprintf (tmp, format, args); + // use the temporary buffer, because 'error' might be one of the args + int res = vsprintf (tmp, format, args); va_end (args); - // copy from temporary buffer: - strcpy (error, tmp); + if (res > 0) + { + // copy from temporary buffer: + strcpy (error, tmp); + } } diff --git a/src/geo2d.cpp b/src/geo2d.cpp index c119f37..ebfafa7 100644 --- a/src/geo2d.cpp +++ b/src/geo2d.cpp @@ -18,8 +18,8 @@ */ -#include"geo2d.h" -#include +#include "geo2d.h" +#include vec2::vec2(){} vec2::vec2(float _x, float _y) @@ -80,56 +80,65 @@ vec2 vec2::unit() const vec2 vec2::rotate(float a) const { vec2 r; - const float c=cos(a), - s=sin(a); + const float + c = cos(a), + s = sin(a); - r.x=c*x - s*y; - r.y=s*x + c*y; + r.x = c * x - s * y; + r.y = s * x + c * y; return r; } -float vec2::angle() const +float vec2::angle () const { - return atan(y/x); + return atan (y / x); } vec2 operator* (const float& f, const vec2& v) { - return (v*f); + return (v * f); } float distance2(const vec2& v1, const vec2& v2) { - return (v2-v1).length2(); + return (v2 - v1).length2(); } float distance(const vec2& v1, const vec2& v2) { - return (v2-v1).length(); + return (v2 - v1).length(); } float dot (const vec2& v1, const vec2& v2) { - return v1.x*v2.x+v1.y*v2.y; + return v1.x * v2.x + v1.y * v2.y; } float angle (const vec2&v1,const vec2&v2) { float a=acosf(dot(v1.unit(),v2.unit())); - while(a>M_PI) { a-=2*M_PI; } - while(a<=-M_PI) { a+=2*M_PI; } + + /* + Return an angle between -PI and PI. + Adding or subtracting 2PI always results + in the same angle. + */ + while (a > M_PI) { a -= 2*M_PI; } + while (a <= -M_PI) { a += 2*M_PI; } + return a; } -vec2 projection(const vec2& v,const vec2& on_v) +vec2 projection (const vec2& v,const vec2& on_v) { - return on_v*dot(v,on_v)/on_v.length2(); + return on_v * dot (v, on_v) / on_v.length2 (); } vec2 lineIntersection(const vec2& a1, const vec2& a2, const vec2& b1, const vec2& b2) { - float d=(a1.x-a2.x)*(b1.y-b2.y)-(a1.y-a2.y)*(b1.x-b2.x), - a=a1.x*a2.y-a1.y*a2.x, - b=b1.x*b2.y-b1.y*b2.x; - vec2 r((a*(b1.x-b2.x)-b*(a1.x-a2.x))/d,(a*(b1.y-b2.y)-b*(a1.y-a2.y))/d); + float d = (a1.x - a2.x) * (b1.y - b2.y) - (a1.y - a2.y) * (b1.x - b2.x), + a = a1.x * a2.y - a1.y * a2.x, + b = b1.x * b2.y - b1.y * b2.x; + + vec2 r ((a*(b1.x-b2.x)-b*(a1.x-a2.x))/d,(a*(b1.y-b2.y)-b*(a1.y-a2.y))/d); return r; } vec2 pointOnBezierCurve(float ti, const vec2& p0, const vec2& p1, const vec2& p2, const vec2& p3) { - float invti = 1.0f-ti; + float invti = 1.0f - ti; return invti*invti*invti*p0 + 3*invti*invti*ti*p1 + 3*invti*ti*ti*p2 + ti*ti*ti*p3; } diff --git a/src/geo2d.h b/src/geo2d.h index f46721e..c0f721f 100644 --- a/src/geo2d.h +++ b/src/geo2d.h @@ -18,40 +18,84 @@ */ -#ifndef GEOMETRY_H -#define GEOMETRY_H +#ifndef GEO2D_H +#define GEO2D_H + +/* + The vec2 is a vector of length 2, used for 2D space calculations. + */ struct vec2 { - float x,y; - - vec2 operator* (const float& f) const; - vec2 operator/ (const float& f) const; - vec2 operator+ (const vec2& v2) const; - vec2 operator- (const vec2& v2) const; - void operator+= (const vec2& v2); - void operator-= (const vec2& v2); - bool operator== (const vec2& other) const; + // coordinates: + float x, y; + + /* + vec2 objects can be added on, subtracted and + multiplied/divided by a scalar. + */ + + vec2 operator* (const float &f) const; + vec2 operator/ (const float &f) const; + vec2 operator+ (const vec2 &v2) const; + vec2 operator- (const vec2 &v2) const; + void operator+= (const vec2 &v2); + void operator-= (const vec2 &v2); + bool operator== (const vec2 &other) const; vec2 operator- () const; - // Length2 is computationally less expensive than Length - float length2() const; - float length() const; + /* + length2 is the squared length, it's + computationally less expensive than Length + */ + float length2 () const; + float length () const; - float angle() const; // with x-axis - vec2 unit() const; - vec2 rotate(float angle) const; + float angle () const; // with x-axis - vec2(); - vec2(float x, float y); + vec2 unit () const; // the unit vector has length 1.0 + + /** + * Rotates this vector in counter clockwise direction. + * :param angle: angle in radians + */ + vec2 rotate (float angle) const; + + vec2 (); + vec2 (float x, float y); }; -vec2 operator* (const float& f, const vec2& v); -float distance2(const vec2& v1, const vec2& v2); -float distance(const vec2& v1, const vec2& v2); -float dot (const vec2& v1, const vec2& v2); -float angle(const vec2&v1,const vec2&v2); +vec2 operator* (const float &f, const vec2 &v); + +/* + distance2 is squared distance, it's + computationally less expensive than distance. + */ +float distance2(const vec2 &v1, const vec2 &v2); +float distance(const vec2 &v1, const vec2 &v2); + +float dot (const vec2 &v1, const vec2 &v2); + +// angle between two vectors, in radians: +float angle (const vec2 &v1, const vec2 &v2); + +// projects on vector on the other: vec2 projection(const vec2& v,const vec2& on_v); -vec2 lineIntersection(const vec2& a1, const vec2& a2, const vec2& b1, const vec2& b2); -vec2 pointOnBezierCurve(float t, const vec2& p0, const vec2& p1, const vec2& p2, const vec2& p3); + +/** + * Gets the intersection between line a (from a1 to a2) + * and line b (from b1 to b2). + */ +vec2 lineIntersection (const vec2& a1, const vec2& a2, const vec2& b1, const vec2& b2); + +/** + * Calculates the position for a point on a bezier curve. + * + * :param t: should be between 0.0 and 1.0 + * :param p0, p1, p2, p3: control points that define the curve + * + * For more information, see: + * https://en.wikipedia.org/wiki/B%C3%A9zier_curve + */ +vec2 pointOnBezierCurve (float t, const vec2& p0, const vec2& p1, const vec2& p2, const vec2& p3); #endif diff --git a/src/ip.h b/src/ip.h index 525f493..dbaeda4 100644 --- a/src/ip.h +++ b/src/ip.h @@ -35,21 +35,21 @@ inline void ip2String (const IPaddress& address, char* out) { Uint8 *bytes = (Uint8 *)&address.host; sprintf (out, "%u.%u.%u.%u:%u", - (unsigned int)bytes[0], - (unsigned int)bytes[1], - (unsigned int)bytes[2], - (unsigned int)bytes[3], - (unsigned int)address.port); + (unsigned int)bytes [0], + (unsigned int)bytes [1], + (unsigned int)bytes [2], + (unsigned int)bytes [3], + (unsigned int)address.port); } inline void string2IP (const char* str, IPaddress& out) { Uint8 *bytes = (Uint8 *)&out.host; sscanf (str, "%u.%u.%u.%u:%u", - (unsigned int*)&bytes[0], - (unsigned int*)&bytes[1], - (unsigned int*)&bytes[2], - (unsigned int*)&bytes[3], - (unsigned int*)&out.port); + (unsigned int *)&bytes [0], + (unsigned int *)&bytes [1], + (unsigned int *)&bytes [2], + (unsigned int *)&bytes [3], + (unsigned int *)&out.port); } #endif diff --git a/src/manager/manager.cpp b/src/manager/manager.cpp index d59f0cb..d19afb9 100644 --- a/src/manager/manager.cpp +++ b/src/manager/manager.cpp @@ -29,6 +29,7 @@ #include #include #include "../str.h" +#include "../err.h" void PromptUsername (char *username, const int maxLength) { @@ -40,6 +41,12 @@ void PromptUsername (char *username, const int maxLength) } void PromptPassword (char *password, const int maxLength) { + /* + Password prompt should be masked input: ******* + + That's the reason why ncurses is used. + */ + int i = 0; unsigned char ch = 0; @@ -100,7 +107,8 @@ void OnMakeAccount (const std::string &exe_dir) } } } - while(usernameError); + // Is username correct syntax? Else reloop. + while (usernameError); PromptPassword (password, PASSWORD_MAXLENGTH); @@ -111,21 +119,26 @@ void OnMakeAccount (const std::string &exe_dir) } else { - printw ("%s\n", GetAccountError ()); + printw ("%s\n", GetError ()); } } bool OnCommand(const char* cmd, const std::string &exe_dir) { + // Execute command 'cmd', given by user + if (strcmp (cmd, "quit") == 0) { + // stop looping return false; } else if (strcmp (cmd, "make-account")==0) { OnMakeAccount (exe_dir); } - else if (emptyline (cmd) || strcmp (cmd,"help") == 0) + else if (emptyline (cmd) || strcmp (cmd, "help") == 0) { + // If an empty line was entered, print help: + printw ("quit: exit application\n"); printw ("make-account: create new account file\n"); printw ("help: print this info\n"); @@ -151,12 +164,14 @@ int main (int argc, char** argv) bool running = true; while (running) { + // print commandline prompt: printw ("manager>"); + // Get the command and remove trailing whitespaces: getnstr (cmd, cmdlen); stripr (cmd); - // take input commands + // execute command running = OnCommand(cmd, exe_dir); } diff --git a/src/matrix.h b/src/matrix.h index 6e8e99f..394b732 100644 --- a/src/matrix.h +++ b/src/matrix.h @@ -170,6 +170,9 @@ inline matrix4 matID() return m; } +/* + Careful, Matrices with determinant 0 are not invertible! + */ inline matrix4 matInverse(const matrix4 &m) { float @@ -229,6 +232,7 @@ inline matrix4 matInverse(const matrix4 &m) return kInv; } +// The transpose is flipped along the diagonal inline matrix4 matTranspose (const matrix4 &m) { matrix4 t; @@ -240,7 +244,7 @@ inline matrix4 matTranspose (const matrix4 &m) return t; } -inline matrix4 matRotX( const float angle )// radians +inline matrix4 matRotX( const float angle ) // radians { matrix4 m; const float c=cos(angle); @@ -253,7 +257,7 @@ inline matrix4 matRotX( const float angle )// radians return m; } -inline matrix4 matRotY( const float angle )// radians +inline matrix4 matRotY( const float angle ) // radians { matrix4 m; const float c=cos(angle); @@ -266,7 +270,7 @@ inline matrix4 matRotY( const float angle )// radians return m; } -inline matrix4 matRotZ( const float angle )// radians +inline matrix4 matRotZ( const float angle ) // radians { matrix4 m; const float c=cos(angle); @@ -279,7 +283,7 @@ inline matrix4 matRotZ( const float angle )// radians return m; } -inline matrix4 matQuat(const Quaternion &q) +inline matrix4 matQuat (const Quaternion &q) // also a rotation matrix { float f = 2.0f / q.Length(); @@ -295,7 +299,7 @@ inline matrix4 matQuat(const Quaternion &q) return m; } -inline matrix4 matRotAxis(const vec3& axis, float angle)// radians +inline matrix4 matRotAxis (const vec3& axis, float angle) // radians { matrix4 m; vec3 a=axis.Unit(); @@ -316,7 +320,7 @@ inline matrix4 matRotAxis(const vec3& axis, float angle)// radians return m; } -inline matrix4 matScale(const float x,const float y,const float z) +inline matrix4 matScale (const float x, const float y, const float z) { matrix4 m; @@ -327,7 +331,7 @@ inline matrix4 matScale(const float x,const float y,const float z) return m; } -inline matrix4 matTranslation( const float x, const float y, const float z ) +inline matrix4 matTranslation (const float x, const float y, const float z) { matrix4 m; @@ -338,7 +342,7 @@ inline matrix4 matTranslation( const float x, const float y, const float z ) return m; } -inline matrix4 matTranslation( const vec3& v ) +inline matrix4 matTranslation (const vec3& v) { matrix4 m; @@ -349,8 +353,12 @@ inline matrix4 matTranslation( const vec3& v ) return m; } -inline matrix4 matLookAt( const vec3& eye, const vec3& lookat, const vec3& up ) +inline matrix4 matLookAt (const vec3& eye, const vec3& lookat, const vec3& up) { + /* + Useful for camera positions. + */ + vec3 _right, _up, _forward; matrix4 m; @@ -366,8 +374,11 @@ inline matrix4 matLookAt( const vec3& eye, const vec3& lookat, const vec3& up ) return m; } -// "matSkew" requires a plane normal and plane d input. -inline matrix4 matSkew( const vec3 n/*normalized*/, float d ) +/* + "matSkew" requires a plane normal and plane d input. + The normal (n) is assumed to have length 1.0 + */ +inline matrix4 matSkew (const vec3 n, float d) { matrix4 m; // distance=p*n+d @@ -394,7 +405,9 @@ inline matrix4 matSkew( const vec3 n/*normalized*/, float d ) return m; } -inline matrix4 matPerspec(float view_angle,float aspect_ratio,float near_viewdist,float far_viewdist) + +// Perspective projection matrix. Z-axis points into the screen. +inline matrix4 matPerspec (float view_angle, float aspect_ratio, float near_viewdist, float far_viewdist) { float a=0.5f*view_angle; float f=1.0f/tan(a); @@ -411,7 +424,8 @@ inline matrix4 matPerspec(float view_angle,float aspect_ratio,float near_viewdis return m; } -inline matrix4 matOrtho(float leftX, float rightX, float upY, float downY, float nearZ, float farZ) +// Orthographic projection matrix. Arguments define screen boundaries. +inline matrix4 matOrtho (float leftX, float rightX, float upY, float downY, float nearZ, float farZ) { float Xdiff = rightX - leftX, Ydiff = upY - downY, diff --git a/src/quat.h b/src/quat.h index 9a65c59..042a36b 100644 --- a/src/quat.h +++ b/src/quat.h @@ -25,6 +25,14 @@ #define QUAT_ID Quaternion(0,0,0,1) +/* + Quaternions are used to represent rotation. + + For further info, see: + https://en.wikipedia.org/wiki/Quaternion + https://en.wikipedia.org/wiki/Slerp + */ + struct Quaternion { union { @@ -57,7 +65,7 @@ struct Quaternion { return length2; } - float Length () const + float Length () const { return sqrt (Length2()); } diff --git a/src/random.cpp b/src/random.cpp index c9891ef..7de19b9 100644 --- a/src/random.cpp +++ b/src/random.cpp @@ -33,5 +33,7 @@ float RandomFloat(float _min, float _max) int n = 10000; float f = float(rand() % n) / n; + // f is a random number between 0.0f and 1.0f + return _min + f * (_max - _min); } diff --git a/src/server/server.cpp b/src/server/server.cpp index 168e223..73a0b5a 100644 --- a/src/server/server.cpp +++ b/src/server/server.cpp @@ -57,35 +57,40 @@ bool Server::Init() settingsPath = std::string(SDL_GetBasePath()) + SETTINGS_FILE; accountsPath = std::string(SDL_GetBasePath()) + ACCOUNT_DIR; - // load the settings from the config + // load the maxUsers setting from the config maxUsers = LoadSetting(settingsPath.c_str(), MAXLOGIN_SETTING); - if(maxUsers>0) + if (maxUsers > 0) { - users=new UserP[maxUsers]; - for(Uint64 i=0; iaccountName [0] = NULL; users[i]->ticksSinceLastContact = 0; - // Generate a RSA key to encrypt passwords in + // Generate a RSA key to encrypt login password in BIGNUM *bn = BN_new (); BN_set_word (bn, 65537); users[i]->key = RSA_new (); - bool keySuccess = RSA_generate_key_ex (users[i]->key, 1024,bn,NULL) + bool keySuccess = RSA_generate_key_ex (users[i]->key, 1024, bn, NULL) && RSA_check_key (users[i]->key); + BN_free (bn); - if(!keySuccess) + if (!keySuccess) { - RSA_free(users[i]->key); + RSA_free (users[i]->key); delete users[i]; users[i]=NULL; return NULL; } - return users[i]; + return users [i]; } } @@ -176,20 +183,20 @@ Server::UserP Server::GetUser (const IPaddress& address) for(Uint64 i = 0; i < maxUsers; i++) { if (users[i] - && users[i]->address.host == address.host - && users[i]->address.port == address.port ) + && users[i]->address.host == address.host + && users[i]->address.port == address.port) { - return users[i]; + return users [i]; } } return NULL; } Server::UserP Server::GetUser (const char* accountName) { - for(Uint64 i=0; iaccountName,accountName)==0 ) + && strcmp (users[i]->accountName, accountName)==0 ) { return users[i]; } @@ -207,10 +214,10 @@ bool Server::LoggedIn(Server::User* user) void Server::OnPlayerRemove (Server::User* user) { // Tell other players about this one's removal: - int len = USERNAME_MAXLENGTH+1; - Uint8*data=new Uint8[USERNAME_MAXLENGTH+1]; - data[0]=NETSIG_DELPLAYER; - memcpy(data+1,user->accountName,USERNAME_MAXLENGTH); + int len = USERNAME_MAXLENGTH + 1; + Uint8*data = new Uint8 [USERNAME_MAXLENGTH + 1]; + data [0] = NETSIG_DELPLAYER; + memcpy (data + 1,user->accountName, USERNAME_MAXLENGTH); // Send the message to all clients: for(Uint64 i=0; iusername, params->password)) { + // username and password match + + // register username with ip-address: strcpy (user->accountName, params->username); + user->params.hue = rand() % 360; // give the user a random color user->state.pos.x=-1000; user->state.pos.y=-1000; @@ -518,7 +532,7 @@ void Server::OnAuthenticate(const IPaddress& clientAddress, LoginParams* params) data [0] = NETSIG_LOGINSUCCESS; memcpy(data + 1, &user->params, sizeof(UserParams)); memcpy(data + 1 + sizeof(UserParams), &user->state, sizeof(UserState)); - SendToClient(clientAddress,data,len); + SendToClient (clientAddress,data,len); delete data; // Tell other users about this new user: @@ -528,8 +542,8 @@ void Server::OnAuthenticate(const IPaddress& clientAddress, LoginParams* params) { if(users[i]!=user) { - TellUserAboutUser (user,users[i]); - TellUserAboutUser (users[i],user); + TellUserAboutUser (user, users[i]); + TellUserAboutUser (users[i], user); } } } @@ -540,11 +554,11 @@ void Server::OnAuthenticate(const IPaddress& clientAddress, LoginParams* params) SendToClient (clientAddress, &response, 1); // Remove the user that requested the login: - DelUser(pendingUser); + DelUser (pendingUser); } } } -void Server::OnLoginRequest(const IPaddress& clientAddress) +void Server::OnLoginRequest (const IPaddress& clientAddress) { if (GetUser (clientAddress)) { @@ -555,22 +569,25 @@ void Server::OnLoginRequest(const IPaddress& clientAddress) } else { - UserP user = AddUser(); + UserP user = AddUser (); if (user) { + // Send the user a public key to encrypt password: + int keySize = i2d_RSAPublicKey(user->key, NULL); Uint8* data = new Uint8 [keySize + 1]; data [0] = NETSIG_RSAPUBLICKEY; - unsigned char *pe = (unsigned char*)(data + 1); + unsigned char *pe = (unsigned char *)(data + 1); i2d_RSAPublicKey (user->key, &pe); - user->address = clientAddress; - SendToClient (clientAddress, data, keySize + 1); delete [] data; + + // Register IP address: + user->address = clientAddress; } - else // we couldn't create another user + else // too many users already { Uint8 response = NETSIG_SERVERFULL; SendToClient (clientAddress, &response, 1); @@ -635,16 +652,18 @@ int Server::LoopThreadFunc (void* p) while (!server->done) // is the server still running? { - while(SDLNet_UDP_Recv (server->socket, server->in)) + // Poll for incoming packets: + while (SDLNet_UDP_Recv (server->socket, server->in)) { server->OnRequest (server->in->address, server->in->data, server->in->len); } + // Get time passed since last iteration: ticks = SDL_GetTicks(); server->Update (ticks - ticks0); ticks0 = ticks; - SDL_Delay (100); // <- this is to allow the other processes to run + SDL_Delay (100); // sleep to allow the other thread to run } return 0; @@ -656,6 +675,7 @@ int main(int argc, char** argv) if(!server.Init()) return 1; + // Take command line input: while (server.TakeCommands()); server.CleanUp(); diff --git a/src/server/server.h b/src/server/server.h index 1deca60..e70ecef 100644 --- a/src/server/server.h +++ b/src/server/server.h @@ -30,8 +30,16 @@ #include "../account.h" #define PACKET_MAXSIZE 512 -#define MAX_CHAT_LENGTH 100 +#define MAX_CHAT_LENGTH 100 // must fit inside packet +/* + A netsig byte is usually placed at the beginning + of the package data. It tells the server or client + what data will follow after it. + + In some cases, the netsig byte alone is enough + information already. + */ #define NETSIG_PINGSERVER 0x01 #define NETSIG_RSAENCRYPTED 0x02 #define NETSIG_PINGCLIENT 0x03 @@ -54,7 +62,7 @@ #define COMMAND_MAXLENGTH 256 #define SERVER_RSA_PADDING RSA_PKCS1_PADDING -inline int maxFLEN(RSA* rsa) { return (RSA_size(rsa) - 11); } +inline int maxFLEN (RSA* rsa) { return (RSA_size(rsa) - 11); } #define CONNECTION_TIMEOUT 10.0f // seconds @@ -65,7 +73,7 @@ struct LoginParams }; struct UserParams { - int hue; + int hue; // color }; struct UserState { @@ -74,8 +82,8 @@ struct UserState }; struct ChatEntry { - char username [USERNAME_MAXLENGTH], - message [MAX_CHAT_LENGTH]; + char username [USERNAME_MAXLENGTH], // who said it? + message [MAX_CHAT_LENGTH]; // what was said? }; class Server @@ -86,8 +94,8 @@ class Server IPaddress address; char accountName[USERNAME_MAXLENGTH]; // empty if not authenticated Uint32 ticksSinceLastContact; - bool pinging; - RSA* key; + bool pinging; + RSA* key; // For password encryption/decryption. UserState state; UserParams params; @@ -95,13 +103,13 @@ class Server typedef User* UserP; UserP* users; Uint64 maxUsers; - static bool LoggedIn(User*); + static bool LoggedIn (User*); - UserP AddUser(); - UserP GetUser(const IPaddress& address); - UserP GetUser(const char* accountName); - void DelUser(const Uint64 i); - void DelUser(UserP user); + UserP AddUser (); + UserP GetUser (const IPaddress& address); + UserP GetUser (const char* accountName); + void DelUser (const Uint64 i); + void DelUser (UserP user); std::list chat_history; @@ -112,35 +120,35 @@ class Server UDPpacket *in, *out, **udpPackets; - int port; + int port; UDPsocket socket; - bool done; + bool done; // if true, loopThread ends SDL_Thread *loopThread; - static int LoopThreadFunc(void* server); + static int LoopThreadFunc (void* server); - void Update(Uint32 ticks); + void Update (Uint32 ticks); - void PrintUsers() const; + void PrintUsers () const; void PrintChatHistory () const; void TellAboutLogout(User* to, const char* loggedOutUsername); - void OnRequest(const IPaddress& clientAddress, Uint8*data, int len); - void OnRSAEncrypted(const IPaddress& clientAddress, Uint8*data, int len); - void OnLoginRequest(const IPaddress& clientAddress); - void OnAuthenticate(const IPaddress& clientAddress, LoginParams* params); - void OnLogout(User* user); + void OnRequest (const IPaddress& clientAddress, Uint8*data, int len); + void OnRSAEncrypted (const IPaddress& clientAddress, Uint8*data, int len); + void OnLoginRequest (const IPaddress& clientAddress); + void OnAuthenticate (const IPaddress& clientAddress, LoginParams* params); + void OnLogout (User* user); - bool SendToClient(const IPaddress& clientAddress,const Uint8*data, int len); + bool SendToClient (const IPaddress& clientAddress,const Uint8*data, int len); - void OnPlayerRemove(User* user); + void OnPlayerRemove (User* user); - void TellUserAboutUser(User* to, User* about); + void TellUserAboutUser (User* to, User* about); - void OnChatMessage(const User*, const char*); - void OnStateSet(User* user,UserState* newState); - void SendUserStateToAll(User* user); + void OnChatMessage (const User*, const char*); + void OnStateSet (User* user,UserState* newState); + void SendUserStateToAll (User* user); void SendToAll (const Uint8*, const int len); diff --git a/src/shader.cpp b/src/shader.cpp index 940546d..189cfb3 100644 --- a/src/shader.cpp +++ b/src/shader.cpp @@ -24,6 +24,11 @@ #include "err.h" #include "shader.h" +/** + * Compile either the source of a vertex or fragment shader. + * :param type: either GL_VERTEX_SHADER or GL_FRAGMENT_SHADER + * :returns: OpenGL handle to shader, or 0 on error + */ GLuint CreateShader(const std::string& source, int type) { if(type != GL_VERTEX_SHADER && type != GL_FRAGMENT_SHADER) @@ -32,25 +37,35 @@ GLuint CreateShader(const std::string& source, int type) return 0; } - GLint result = GL_FALSE; + GLint result; int logLength; - GLuint shader = glCreateShader(type); + GLuint shader = glCreateShader (type); + // Compile const char *src_ptr = source.c_str(); - glShaderSource(shader, 1, &src_ptr, NULL); - glCompileShader(shader); + glShaderSource (shader, 1, &src_ptr, NULL); + glCompileShader (shader); - glGetShaderiv(shader, GL_COMPILE_STATUS, &result); + glGetShaderiv (shader, GL_COMPILE_STATUS, &result); if(result!=GL_TRUE) { - glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &logLength); + // Error occurred, get compile log: + glGetShaderiv (shader, GL_INFO_LOG_LENGTH, &logLength); + + char *errorString = new char [logLength + 1]; + glGetShaderInfoLog (shader, logLength, NULL, errorString); + + if (type == GL_VERTEX_SHADER) + + SetError ("Error compiling vertex shader: %s", errorString); + + else if (type == GL_FRAGMENT_SHADER) - char *errorString = new char[logLength]; - glGetShaderInfoLog(shader, logLength, NULL, errorString); - SetError ("Error compiling shader: %s", errorString); - delete[] errorString; + SetError ("Error compiling fragment shader: %s", errorString); - glDeleteShader(shader); + delete [] errorString; + + glDeleteShader (shader); return 0; } @@ -63,33 +78,42 @@ GLuint CreateShaderProgram(const std::string& sourceVertex, const std::string& s int logLength; GLuint program=0, vertexShader, fragmentShader; - vertexShader = CreateShader(sourceVertex, GL_VERTEX_SHADER); - fragmentShader = CreateShader(sourceFragment, GL_FRAGMENT_SHADER); + // Compile the sources: + vertexShader = CreateShader (sourceVertex, GL_VERTEX_SHADER); + fragmentShader = CreateShader (sourceFragment, GL_FRAGMENT_SHADER); - if(vertexShader && fragmentShader) + if (vertexShader && fragmentShader) { + // Combine the compiled shaders into a program: program = glCreateProgram(); - glAttachShader(program, vertexShader); - glAttachShader(program, fragmentShader); - glLinkProgram(program); - glGetProgramiv(program, GL_LINK_STATUS, &result); - if(result!=GL_TRUE) + glAttachShader (program, vertexShader); + glAttachShader (program, fragmentShader); + + glLinkProgram (program); + + glGetProgramiv (program, GL_LINK_STATUS, &result); + if (result != GL_TRUE) { + // Error occurred, get log: + glGetProgramiv(program, GL_INFO_LOG_LENGTH, &logLength); - char *errorString = new char[logLength]; - glGetProgramInfoLog(program, logLength, NULL, errorString); + char *errorString = new char [logLength]; + glGetProgramInfoLog (program, logLength, NULL, errorString); SetError ("Error linking shader program: %s", errorString); - delete[] errorString; + delete [] errorString; - glDeleteProgram(program); + glDeleteShader (vertexShader); + glDeleteShader (fragmentShader); + glDeleteProgram (program); return 0; } } - glDeleteShader(vertexShader); - glDeleteShader(fragmentShader); + // Not needed anymore at this point: + glDeleteShader (vertexShader); + glDeleteShader (fragmentShader); return program; } diff --git a/src/shader.h b/src/shader.h index 13fdf7f..f65ef13 100644 --- a/src/shader.h +++ b/src/shader.h @@ -26,6 +26,12 @@ #include +/** + * Creates a shader program from source. + * :param sourceVertex: source of the vertex shader + * :param sourceFragment: source of the fragment shader + * :returns: OpenGL handle to the shader program, or 0 on error + */ GLuint CreateShaderProgram (const std::string& sourceVertex, const std::string& sourceFragment); #endif // SHADER_H diff --git a/src/str.cpp b/src/str.cpp index 89c9124..02f9014 100644 --- a/src/str.cpp +++ b/src/str.cpp @@ -29,48 +29,64 @@ const char *ParseFloat(const char *in, float *out) { float f = 10.0f; int digit, ndigit = 0; + + // start from zero *out = 0; + const char *p = in; while (*p) { - if (isdigit(*p)) + if (isdigit (*p)) { digit = (*p - '0'); - if (f > 1.0f) + + if (f > 1.0f) // left from period { *out *= f; *out += digit; } - else + else // right from period, decimal { *out += f * digit; f *= 0.1f; } ndigit++; } - else if (tolower(*p) == 'e') + else if (tolower (*p) == 'e') { + // exponent + + // if no digits precede the exponent, assume 1 if (ndigit <= 0) *out = 1.0f; p++; - if (*p == '+') - p++; + if (*p == '+') // '+' just means positive power, default + p++; // skip it, don't give it to atoi int e = atoi (p); *out = *out * pow(10, e); - if (*p == '-') p++; - while (isdigit(*p)) p++; + // read past exponent number + if (*p == '-') + p++; + + while (isdigit(*p)) + p++; + return p; } else if (*p == '.') { + // expect decimal digits after this + f = 0.1f; } else if (*p == '-') { + // negative number + float v; p = ParseFloat(p + 1, &v); @@ -80,6 +96,8 @@ const char *ParseFloat(const char *in, float *out) } else { + // To assume success, must have read at least one digit + if (ndigit > 0) return p; else @@ -90,9 +108,9 @@ const char *ParseFloat(const char *in, float *out) return p; } -bool isnewline(const int c) +bool isnewline (const int c) { - return (c=='\n'||c=='\r'); + return (c=='\n' || c=='\r'); } bool emptyline (const char *line) { @@ -100,12 +118,15 @@ bool emptyline (const char *line) { if (!isspace (*line)) return false; + line ++; } return true; } void stripr (char *s) { + // Start on the back and move left: + int n = strlen (s); while (true) { diff --git a/src/str.h b/src/str.h index fc498db..23bd059 100644 --- a/src/str.h +++ b/src/str.h @@ -24,6 +24,12 @@ #include #include +/* + Here, the PATH_SEPARATOR macro is used + to join paths. Could also use boost for this: + + http://www.boost.org/doc/libs/1_53_0/libs/filesystem/doc/reference.html + */ #ifdef _WIN32 #define PATH_SEPARATOR '\\' #else @@ -32,14 +38,35 @@ bool isnewline (const int c); bool emptyline (const char *line); + +/** + * Places a NULL character so that trailing whitespaces + * are removed. + */ void stripr (char *s); -const char *ParseFloat(const char *in, float *out); // not locale dependent +/** + * atof and scanf are locale dependent. ParseFloat isn't, + * decimals always assumed to have dots here, never commas. + * + * :returns: the string after the parsed number on success, + * NULL on failure. + */ +const char *ParseFloat(const char *in, float *out); +/** + * Like strcmp, but ignores case. + */ int StrCaseCompare(const char *s1, const char *s2); +/** + * splits a string by given character. + */ void split (const char *s, const char by, std::list &out); +/** + * Gives the start and end position of the word at pos in the string. + */ void WordAt (const char *s, const int pos, int &start, int &end); #endif // STR_H diff --git a/src/test3d/collision.cpp b/src/test3d/collision.cpp index e48cabc..b3b8037 100644 --- a/src/test3d/collision.cpp +++ b/src/test3d/collision.cpp @@ -23,25 +23,42 @@ #include #include "../util.h" +/** + * Returns the absolute value: negative becomes positive, + * positive stays positive. + */ template -Number abs(Number n) +Number abs (Number n) { if (n < 0) return -n; else return n; } -int LineSphereIntersections(const vec3& p1, const vec3& p12, const vec3& center, float radius, double *t1, double *t2) +/** + * Calculates the intersection points between + * a sphere (center, radius) and a line (p1, p12) + * + * t1 and t2 will be set so that: + * intersection1 = p1 + t1 * p12 + * intersection2 = p1 + t2 * p12 + * + * :returns: the number of intersection points (0, 1 or 2) + */ +int LineSphereIntersections (const vec3& p1, const vec3& p12, + const vec3& center, float radius, + + double *t1, double *t2) { - double a = Dot(p12, p12); // a is always positive - double b = 2.0 * Dot(p12, p1 - center); - double c = Dot(center,center) + Dot(p1,p1) - 2.0 * Dot(center, p1) - radius * radius; - double diskriminant = b * b - 4.0 * a * c; + double a = Dot (p12, p12), // a is always positive + b = 2.0 * Dot(p12, p1 - center), + c = Dot (center, center) + Dot (p1, p1) - 2.0 * Dot (center, p1) - radius * radius, + diskriminant = b * b - 4.0 * a * c; if (diskriminant < 0 || a == 0) { - *t1=0; - *t2=0; + *t1 = 0; + *t2 = 0; return 0; } if (diskriminant == 0) @@ -49,31 +66,48 @@ int LineSphereIntersections(const vec3& p1, const vec3& p12, const vec3& center, *t2 = *t1 = (-b / (2.0 * a)); return 1; } - double sqrt_diskriminant = sqrt(diskriminant); + double sqrt_diskriminant = sqrt (diskriminant); *t1 = ((-b + sqrt_diskriminant) / (2.0 * a)); *t2 = ((-b - sqrt_diskriminant) / (2.0 * a)); + // because a is always positive, t1 > t2 return 2; } +/** + * Calculates intersection point between a line (p1, p12) and a plane + * (there's always one, unless the line is parallel to the plane) + * + * The return value is set so that: + * intersection = p1 + return_value * p12 + */ float LinePlaneIntersectionDirParameter(const vec3& p1, const vec3& p12, const Plane &plane) { vec3 p12_norm = p12.Unit(); - float distance_plane_p1 = DistanceFromPlane(p1, plane); - float divisor = Dot(plane.n, p12_norm); + + float distance_plane_p1 = DistanceFromPlane (p1, plane); + float divisor = Dot (plane.n, p12_norm); + if (divisor == 0.0f) - return 0.0f; // line lies on plane + return 0.0f; // line is parallel to plane + float t = -distance_plane_p1 / divisor; return t / p12.Length(); // at p1 t=0, at p2 t=1.0f } -vec3 LinePlaneIntersectionDir(const vec3& p1,const vec3& p12, const Plane &plane) +/** + * Calculates intersection point between a line (p1, p12) and a plane + * (there's always one, unless the line is parallel to the plane) + * + * :returns: intersection point + */ +vec3 LinePlaneIntersectionDir (const vec3& p1, const vec3& p12, const Plane &plane) { vec3 p12_norm = p12.Unit(); - float distance_plane_p1 = DistanceFromPlane(p1, plane); - float divisor = Dot(plane.n, p12_norm); + float distance_plane_p1 = DistanceFromPlane (p1, plane); + float divisor = Dot (plane.n, p12_norm); if(divisor == 0.0f) - return p1; // line lies on plane + return p1; // line parallel to plane float t = -distance_plane_p1 / divisor; @@ -81,7 +115,9 @@ vec3 LinePlaneIntersectionDir(const vec3& p1,const vec3& p12, const Plane &plane return result; } + SphereCollider :: SphereCollider (const vec3 &c, const float r) : relativeCenter(c), radius(r) {} + bool SphereCollider :: HitsTriangle (const Triangle &tri, const vec3 &startP, const vec3 &movement, vec3 &outClosestPointOnSphere, vec3 &outContactPointSphereToTriangle, vec3 &outContactNormal) { @@ -105,7 +141,7 @@ bool SphereCollider :: HitsTriangle (const Triangle &tri, const vec3 &startP, co else // at p1, the sphere is still far enough from the plane { - if (Dot(movement, (center - closestPointOnSphere)) > -0.000001f * radius) + if (Dot (movement, (center - closestPointOnSphere)) > -0.000001f * radius) // Movement is away from plane return false; @@ -128,26 +164,30 @@ bool SphereCollider :: HitsTriangle (const Triangle &tri, const vec3 &startP, co { // See if the sphere hits the edge during movement - vec3 tri_c12 = ClosestPointOnLine(tri.p[0], tri.p[1], contactPointSphereToPlane), - tri_c23 = ClosestPointOnLine(tri.p[1], tri.p[2], contactPointSphereToPlane), - tri_c13 = ClosestPointOnLine(tri.p[0], tri.p[2], contactPointSphereToPlane); - float dist_c12 = (tri_c12 - contactPointSphereToPlane).Length2(), + vec3 tri_c12 = ClosestPointOnLine (tri.p[0], tri.p[1], contactPointSphereToPlane), + tri_c23 = ClosestPointOnLine (tri.p[1], tri.p[2], contactPointSphereToPlane), + tri_c13 = ClosestPointOnLine (tri.p[0], tri.p[2], contactPointSphereToPlane); + float dist_c12 = (tri_c12 - contactPointSphereToPlane).Length2(), dist_c23 = (tri_c23 - contactPointSphereToPlane).Length2(), dist_c13 = (tri_c13 - contactPointSphereToPlane).Length2(); - if(dist_c12 < dist_c13) + if (dist_c12 < dist_c13) { - if(dist_c12 < dist_c23) contactPointSphereToTriangle = tri_c12; - else contactPointSphereToTriangle = tri_c23; + if (dist_c12 < dist_c23) + contactPointSphereToTriangle = tri_c12; + else + contactPointSphereToTriangle = tri_c23; } else { - if(dist_c13 < dist_c23) contactPointSphereToTriangle = tri_c13; - else contactPointSphereToTriangle = tri_c23; + if(dist_c13 < dist_c23) + contactPointSphereToTriangle = tri_c13; + else + contactPointSphereToTriangle = tri_c23; } double t1, t2; - if(LineSphereIntersections(contactPointSphereToTriangle, movement, center, radius, &t1, &t2)) + if (LineSphereIntersections (contactPointSphereToTriangle, movement, center, radius, &t1, &t2)) { if(t1 <= 0 && t2 < 0) { @@ -182,10 +222,10 @@ bool FeetCollider :: HitsTriangle (const Triangle &tri, const vec3 &p, const vec { const Plane plane = tri.GetPlane(); - if (abs (Dot(plane.n, -toFeet)) < 0.0001f) + if (abs (Dot (plane.n, -toFeet)) < 0.0001f) return false; // plane is parallel to feet direction - vec3 pOnGroundUnderP = LinePlaneIntersectionDir(p, toFeet, plane), + vec3 pOnGroundUnderP = LinePlaneIntersectionDir (p, toFeet, plane), feet_start = p + toFeet, contactPointFeetToPlane; @@ -210,7 +250,7 @@ bool FeetCollider :: HitsTriangle (const Triangle &tri, const vec3 &p, const vec contactPointFeetToPlane = feet_start + t * movement; } - if (!PointInsideTriangle(tri, contactPointFeetToPlane)) + if (!PointInsideTriangle (tri, contactPointFeetToPlane)) { return false; } @@ -219,6 +259,7 @@ bool FeetCollider :: HitsTriangle (const Triangle &tri, const vec3 &p, const vec closestPointOnFeet = feet_start; outContactPoint = contactPointFeetToPlane; outContactNormal = plane.n; + return true; } #define COLLISION_MAXITERATION 10 @@ -251,6 +292,7 @@ vec3 CollisionMove (const vec3& p1, const vec3& p2, bool pushingSomething = false; + // Iterate over every triangle and every collider to detect hits: for(int i = 0; i < n_triangles; i++) { for (int c = 0; c < n_colliders; c++) @@ -260,9 +302,13 @@ vec3 CollisionMove (const vec3& p1, const vec3& p2, if (pCollider->HitsTriangle (triangles [i], current_p, targetMovement, testClosestPointOnObject, testContactPoint, testNormal)) { + // Collider hit a triangle + dist2 = (testContactPoint - testClosestPointOnObject).Length2(); if (dist2 < smallestDist) { + // The closest hit on the path gets priority + smallestDist = dist2; contactPoint = testContactPoint; @@ -296,14 +342,21 @@ vec3 CollisionMove (const vec3& p1, const vec3& p2, targetMovement = pushingMovement; j++; + + /* + We moved up to the wall and adjusted our movement, + but we might encounter a new wall while moving along this one. + Do another iteration to find out.. + */ } - else + else // unhindered movement { current_p += targetMovement; return current_p; } } + // Don't go on iterating forever in corners! while ((targetMovement.Length2() > 1.0e-8f) && j < COLLISION_MAXITERATION); return current_p; @@ -344,6 +397,8 @@ bool TestOnGround (const vec3& p, if (pCollider->HitsTriangle (triangles [i], p, -up, testClosestPointOnObject, testContactPoint, testNormal)) { + // Collider hits floor when moved down, now see if the floor's close enough: + fakePlane.n = testNormal; fakePlane.d = -Dot (fakePlane.n, testContactPoint); @@ -399,8 +454,11 @@ vec3 PutOnGround (const vec3& p, if (pCollider->HitsTriangle (triangles [i], p, -smallest * up, testClosestPointOnObject, testContactPoint, testNormal)) { + // Collider hits floor when moved down, now see if the floor's close enough: + movement = testContactPoint - testClosestPointOnObject; dist2 = movement.Length2(); + if (dist2 < smallest) { smallest = dist2; @@ -452,9 +510,13 @@ vec3 CollisionWalk (const vec3& p1, const vec3& p2, if (pCollider->HitsTriangle (triangles [i], current_p, targetMovement, testClosestPointOnObject, testContactPoint, testNormal)) { + // The collider hit the triangle on the way. + dist2 = (testContactPoint - testClosestPointOnObject).Length2(); if (dist2 < smallestDist) { + // The closest hit gets priority: + smallestDist = dist2; contactPoint = testContactPoint; @@ -508,13 +570,24 @@ vec3 CollisionWalk (const vec3& p1, const vec3& p2, targetMovement = pushingMovement; j++; + + /* + We moved up to the wall and adjusted our movement, + but we might encounter a new wall while moving along this one. + Do another iteration to find out.. + */ } - else + else // unhindered movement { current_p += targetMovement; // Put it back on the ground, if any ground_p = PutOnGround (current_p, colliders, n_colliders, triangles, n_triangles, min_cosine, up); + + /* + Distance to unground position must not be too large. + Take target movement as benchmark. + */ if ((ground_p - current_p).Length2() < targetMovement.Length2()) { return ground_p; @@ -523,6 +596,7 @@ vec3 CollisionWalk (const vec3& p1, const vec3& p2, return current_p; } } + // Don't go on iterating forever in corners! while ((targetMovement.Length2() > 1.0e-8f) && j < COLLISION_MAXITERATION); return current_p; @@ -534,301 +608,20 @@ vec3 CollisionTraceBeam(const vec3 &p1, const vec3 &p2, const Triangle *triangle return p1; // otherwise we might get unexpected results vec3 p12 = p2 - p1, isect = p2, newisect; + + // For every triangle, see if the beam goes through: for(int i = 0; i < n_triangles; i++) { - newisect = LinePlaneIntersectionDir(p1, p12, triangles[i].GetPlane()); + newisect = LinePlaneIntersectionDir (p1, p12, triangles[i].GetPlane()); - if (!PointInsideTriangle(triangles[i], newisect)) + if (!PointInsideTriangle (triangles[i], newisect)) continue; vec3 delta = newisect - p1; - if( delta.Length2() < (p1 - isect).Length2() && Dot(delta, p12) > 0) + if (delta.Length2() < (p1 - isect).Length2() && Dot (delta, p12) > 0) isect = newisect; } return isect; } -/* float GetGroundSlope (const vec3 &p1, const vec3 &p2, const Triangle *triangles, const int n_triangles) -{ - if(p1 == p2) - return 0; - - float smallest = 1.0e+15f, dist2, slope; - Plane plane; - vec3 isect1, isect2, p12; - for(int i = 0; i < n_triangles; i++) - { - plane = triangles[i].GetPlane(); - - if (abs(Dot (plane.n, VEC_UP)) < 1.0e-8f) - - // skip parallels - continue; - - isect1 = LinePlaneIntersectionDir(p1, VEC_DOWN, plane); - - dist2 = (isect1 - p1).Length2(); - if (dist2 < smallest) - { - smallest = dist2; - isect2 = LinePlaneIntersectionDir(p2, VEC_DOWN, plane); - - p12 = isect2 - isect1; - slope = p12.y / sqrt(sqr (p12.x) + sqr (p12.z)); - } - } - - return slope; -} -vec3 CollisionMoveFeet (const vec3& p1, const vec3& p2, const float height, const Triangle *triangles, const int n_triangles) -{ - vec3 feet_start = {p1.x, p1.y - height, p1.z}, - - pOnGroundUnderP1, - pContactPlane, - - desiredMovement = p2 - p1, - allowedMovement = desiredMovement; - - const vec3 requested_mv = desiredMovement; - - Plane closestPlane; - - int j = 0; - do - { - float smallestDist = 1.0e+15f, - start_above, - head_start_above, - dist2; - bool pushing = false; - - for(int i = 0; i < n_triangles; i++) - { - const Triangle *tri = &triangles[i]; - const Plane plane = tri->GetPlane(); - - if (abs (Dot(plane.n, VEC_UP)) < 0.0001f) - continue; // parallel to fall direction - - pOnGroundUnderP1 = LinePlaneIntersectionDir(p1, VEC_DOWN, plane); - - start_above = (feet_start - pOnGroundUnderP1).y; - head_start_above = start_above + height; - - if (start_above < 0 && head_start_above > 0) // feet sticking through ground - { - pContactPlane = {feet_start.x, feet_start.y - start_above, feet_start.z}; - } - else - { - vec3 projection = PlaneProjection(feet_start, plane); - - if (Dot ((projection - feet_start), desiredMovement) < 0.0001f) - continue; // not moving towards floor - - float t = LinePlaneIntersectionDirParameter(feet_start, desiredMovement, plane); - if((t > 1.0f) || (t < 0)) - continue; // no contact on the way to p2 - else - pContactPlane = feet_start + t * desiredMovement; - } - - if (!PointInsideTriangle(*tri, pContactPlane)) - continue; - - dist2 = (pContactPlane - feet_start).Length2(); - if (dist2 < smallestDist) - { - smallestDist = dist2; - allowedMovement = pContactPlane - feet_start; - closestPlane = plane; - } - pushing = true; - } - - if (pushing) - { - //plane projection of the movement: - vec3 delta = LinePlaneIntersectionDir(feet_start, desiredMovement, closestPlane) - feet_start; - - if(Dot(requested_mv, delta) < 0) - { - delta = {0, 0, 0}; - } - - feet_start += allowedMovement + closestPlane.n * 1.0e-3f; - desiredMovement = delta; - allowedMovement = desiredMovement; - - j++; - } - else - { - feet_start += allowedMovement; - return {feet_start.x, feet_start.y + height, feet_start.z}; - } - } - while ((desiredMovement.Length2() > 1.0e-8f) && j < COLLISION_MAXITERATION); - - return {feet_start.x, feet_start.y + height, feet_start.z}; -} -vec3 CollisionMoveSphere (const vec3& _p1, const vec3& _p2, float radius, const Triangle *triangles, const int n_triangles) -{ - if(_p1==_p2) - return _p1; - - vec3 p1 = _p1, - p2 = _p2, - p12 = p2 - p1; - - const vec3 ori_p12 = p12; - unsigned int j = 0; - float radius2 = radius * radius; - - vec3 newmove = p12, - newClosestPointOnSphere, - newContactPointSphereToTriangle; - - do - { - float distanceCenterToTriangle=1.0e+15f; - bool pushing=false; - for(int i = 0; i < n_triangles; i++) - { - const Triangle *tri = &triangles[i]; - const Plane plane = tri->GetPlane(); - - vec3 normal = plane.n, - p_move, - center = p1, - ClosestPointOnSphere; - - float distanceCenterToPlane = DistanceFromPlane(center, plane); - - if (distanceCenterToPlane > 0) - ClosestPointOnSphere = center - radius * normal; - else - ClosestPointOnSphere = center + radius * normal; - - vec3 contactPointSphereToPlane; - if (fabs (distanceCenterToPlane) < radius * 0.9999f) - - // sphere lies too close to plane - contactPointSphereToPlane = center - distanceCenterToPlane * normal; - - else // at p1, the sphere is still far enough from the plane - { - if (Dot(p12, (center - ClosestPointOnSphere)) > -0.000001f * radius) - // Movement is away from plane - continue; - - float t = LinePlaneIntersectionDirParameter (ClosestPointOnSphere, p12, plane); - if ((t > 1.0f) || (t < -radius/p12.Length())) - - // Movement doesn't reach plane - continue; - else - contactPointSphereToPlane = ClosestPointOnSphere + t * p12; - } - - vec3 contactPointSphereToTriangle; - if (PointInsideTriangle (*tri, contactPointSphereToPlane)) - { - // Then it's easy - contactPointSphereToTriangle = contactPointSphereToPlane; - } - else // hit point is outside triangle - { - // See if the sphere hits the edge during movement - - vec3 tri_c12 = ClosestPointOnLine(tri->p[0], tri->p[1], contactPointSphereToPlane), - tri_c23 = ClosestPointOnLine(tri->p[1], tri->p[2], contactPointSphereToPlane), - tri_c13 = ClosestPointOnLine(tri->p[0], tri->p[2], contactPointSphereToPlane); - float dist_c12 = (tri_c12 - contactPointSphereToPlane).Length2(), - dist_c23 = (tri_c23 - contactPointSphereToPlane).Length2(), - dist_c13 = (tri_c13 - contactPointSphereToPlane).Length2(); - - if(dist_c12 < dist_c13) - { - if(dist_c12 < dist_c23) contactPointSphereToTriangle = tri_c12; - else contactPointSphereToTriangle = tri_c23; - } - else - { - if(dist_c13 < dist_c23) contactPointSphereToTriangle = tri_c13; - else contactPointSphereToTriangle = tri_c23; - } - - double t1, t2; - if(LineSphereIntersections(contactPointSphereToTriangle, p12, center, radius, &t1, &t2)) - { - if (t1 <= 0 && t2 < 0) - { - if (t1 < -1.0f) - continue; // Too far away. - - ClosestPointOnSphere = t1 * p12 + contactPointSphereToTriangle; - } - else if(t1 > 0 && t2 < 0) - { - if (abs(t1) < abs(t2)) ClosestPointOnSphere = t1 * p12 + contactPointSphereToTriangle; - else ClosestPointOnSphere = t2 * p12 + contactPointSphereToTriangle; - } - else // if(t1>0 && t2>0) - continue; // Too far away. - } - else - continue; // No intersections with this sphere. - } - p_move = contactPointSphereToTriangle - ClosestPointOnSphere; - - // Pick the shortest possible moving distance of all triangles - float dist2 = (contactPointSphereToTriangle - center).Length2(); - if(dist2 < distanceCenterToTriangle) - { - distanceCenterToTriangle = dist2; - newmove = p_move; - newClosestPointOnSphere = ClosestPointOnSphere; - newContactPointSphereToTriangle = contactPointSphereToTriangle; - } - pushing = true; - } - - if (pushing) // movement needs to be adjusted - { - // Calculate a fictional plane - Plane plane; - plane.n = (p1 - newClosestPointOnSphere).Unit(); - plane.d = -Dot(plane.n, newContactPointSphereToTriangle); - - // plane projection of the movement: - vec3 delta = LinePlaneIntersectionDir( newClosestPointOnSphere + p12, plane.n, plane)- newContactPointSphereToTriangle; - - if (Dot(ori_p12, delta) < 0) // moves in opposite direction, let's not! - { - delta = {0, 0, 0}; - } - - // Do the moving part that collision allows - p1 += newmove + 0.0001f * radius * plane.n; - - // Reset our goal to what last plane's projection allows - p2 = p1 + delta; - p12 = p2 - p1; - newmove = p12; - - // count the number of pushes - j++; - } - else - { - p1 += newmove; - return p1; - } - } - while((p12.Length2() > 1.0e-8f * radius2) && j < COLLISION_MAXITERATION); - - return p1; -} */ diff --git a/src/test3d/hub.cpp b/src/test3d/hub.cpp index 7666971..a118fd7 100644 --- a/src/test3d/hub.cpp +++ b/src/test3d/hub.cpp @@ -24,6 +24,7 @@ #include "../xml.h" #include "../err.h" +// Color for the help text in each of the scenes (RGB): const GLfloat textColors [][3] = {{1.0f, 1.0f, 1.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 0.0f, 0.0f}}; HubScene::HubScene (App *pApp) : Scene (pApp), @@ -56,6 +57,7 @@ HubScene::HubScene (App *pApp) : Scene (pApp), pToonScene = new ToonScene (pApp); + // Start with this scene: pCurrent = pShadowScene; help = 0; } @@ -83,8 +85,9 @@ bool HubScene::Init () if (!fontInput) // file or archive missing return false; - xmlDocPtr pDoc = ParseXML(fontInput); - fontInput->close(fontInput); + // Parse the svg as xml document: + xmlDocPtr pDoc = ParseXML (fontInput); + fontInput->close (fontInput); if (!pDoc) { @@ -92,6 +95,7 @@ bool HubScene::Init () return false; } + // Convert xml to font object: success = ParseSVGFont (pDoc, 16, &font); xmlFreeDoc (pDoc); @@ -105,10 +109,16 @@ bool HubScene::Init () } void HubScene::Update (float dt) { + // Update current scene: pCurrent -> Update (dt); - const Uint8 *state = SDL_GetKeyboardState(NULL); - if (state[SDL_SCANCODE_H]) + /* + Update help text alpha. + If the key is down alpha must be 1.0, + else decrease gradually. + */ + const Uint8 *state = SDL_GetKeyboardState (NULL); + if (state [SDL_SCANCODE_H]) { alphaH = 1.0f; } @@ -120,8 +130,9 @@ void HubScene::Render () int w, h; SDL_GL_GetDrawableSize (pApp->GetMainWindow (), &w, &h); - glViewport(0, 0, w, h); + glViewport (0, 0, w, h); + // Render current scene: pCurrent -> Render (); // Render the text: @@ -132,7 +143,10 @@ void HubScene::Render () glEnable (GL_BLEND); glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - + /* + Screen is a w x h rectangle. + Positive y means down. + */ glMatrixMode(GL_PROJECTION); matrix4 matScreen = matOrtho(0.0f, w, 0.0f, h, -1.0f, 1.0f); glLoadMatrixf (matScreen.m); @@ -141,6 +155,11 @@ void HubScene::Render () glLoadIdentity(); glMultMatrixf (matTranslation(10.0f, 10.0f, 0.0f).m); + /* + When the help key is down (alpha = 1.0), help is shown. + When alpha goes down to 0.0, "Press 'h' for help shows up" + */ + GLfloat maxWidth = w - 20.0f; glColor4f (textColors [help][0], textColors [help][1], textColors [help][2], 1.0f - alphaH); glRenderText (&font, "Press 'h' for help", TEXTALIGN_LEFT, maxWidth); @@ -152,6 +171,7 @@ void HubScene::OnEvent(const SDL_Event *event) { Scene :: OnEvent (event); + // pass on event to current scene: pCurrent -> OnEvent (event); } void HubScene::OnKeyPress (const SDL_KeyboardEvent *event) @@ -161,6 +181,8 @@ void HubScene::OnKeyPress (const SDL_KeyboardEvent *event) if(event->keysym.sym == SDLK_ESCAPE) pApp->ShutDown(); + // These keys switch between scenes: + if(event->keysym.sym == SDLK_F1) { pCurrent = pShadowScene; diff --git a/src/test3d/hub.h b/src/test3d/hub.h index 1a91b91..ecf3fff 100644 --- a/src/test3d/hub.h +++ b/src/test3d/hub.h @@ -27,19 +27,30 @@ #include "../font.h" +/* + The hub scene manager switching between scenes. + It's also responsible for showing help text. + */ class HubScene : public App::Scene{ private: WaterScene *pWaterScene; ShadowScene *pShadowScene; ToonScene *pToonScene; + + // Currently rendered scene: Scene *pCurrent; + // Font for the help text shown: Font font; + // text alpha value GLfloat alphaH; + // current help string index: int help; + + // help strings: std::string helpText [3]; public: void OnEvent (const SDL_Event *event); diff --git a/src/test3d/toon.cpp b/src/test3d/toon.cpp index cb205fb..7367bdb 100644 --- a/src/test3d/toon.cpp +++ b/src/test3d/toon.cpp @@ -49,10 +49,11 @@ bool ToonScene::Init () bool success; xmlDocPtr pDoc; + // Load head mesh: f = SDL_RWFromZipArchive (resPath.c_str(), "head.xml"); if (!f) return false; - pDoc = ParseXML(f); + pDoc = ParseXML (f); f->close(f); if (!pDoc) @@ -61,7 +62,8 @@ bool ToonScene::Init () return false; } - success = ParseMesh(pDoc, &meshDataHead); + // Convert xml to mesh: + success = ParseMesh (pDoc, &meshDataHead); xmlFreeDoc (pDoc); if (!success) @@ -70,6 +72,7 @@ bool ToonScene::Init () return false; } + // Load background image: f = SDL_RWFromZipArchive (resPath.c_str(), "toonbg.png"); if (!f) return false; @@ -82,6 +85,7 @@ bool ToonScene::Init () return false; } + // Load the vertex shader source: f = SDL_RWFromZipArchive (resPath.c_str(), "shaders/toon.vsh"); if (!f) return false; @@ -95,6 +99,7 @@ bool ToonScene::Init () return false; } + // Load the fragment shader source: f = SDL_RWFromZipArchive (resPath.c_str(), "shaders/toon.fsh"); if (!f) return false; @@ -107,6 +112,7 @@ bool ToonScene::Init () return false; } + // Create shader from sources: shaderProgram = CreateShaderProgram (sourceV, sourceF); if (!shaderProgram) { @@ -118,6 +124,12 @@ bool ToonScene::Init () } void RenderUnAnimatedSubsetExtension (const MeshData *pMeshData, std::string id, const GLfloat howmuch) { + /* + Almost the same as a normal mesh rendering, + except that the vertices are moved along their normals. + This makes the rendered mesh a bit larger than the original. + */ + if(pMeshData->subsets.find(id) == pMeshData->subsets.end()) { SetError ("cannot render %s, no such subset", id.c_str()); @@ -129,10 +141,10 @@ void RenderUnAnimatedSubsetExtension (const MeshData *pMeshData, std::string id, const vec3 *n; vec3 p; - for (std::list::const_iterator it = pSubset->quads.begin(); it != pSubset->quads.end(); it++) + // Give OpenGL all quads: + glBegin(GL_QUADS); + for (std::list ::const_iterator it = pSubset->quads.begin(); it != pSubset->quads.end(); it++) { - glBegin(GL_QUADS); - PMeshQuad pQuad = *it; for (size_t j = 0; j < 4; j++) { @@ -144,12 +156,12 @@ void RenderUnAnimatedSubsetExtension (const MeshData *pMeshData, std::string id, glNormal3f (n->x, n->y, n->z); glVertex3f (p.x, p.y, p.z); } - - glEnd(); } + glEnd(); + // Give OpenGL all triangles: glBegin(GL_TRIANGLES); - for (std::list::const_iterator it = pSubset->triangles.begin(); it != pSubset->triangles.end(); it++) + for (std::list ::const_iterator it = pSubset->triangles.begin(); it != pSubset->triangles.end(); it++) { PMeshTriangle pTri = *it; for (size_t j = 0; j < 3; j++) @@ -178,7 +190,7 @@ void ToonScene::Render () SDL_GL_GetDrawableSize (pApp->GetMainWindow (), &w, &h); - // Set the 3d projection matrix: + // Set orthographic projection matrix: glMatrixMode (GL_PROJECTION); matrix4 matScreen = matOrtho(-w / 2, w / 2, -h / 2, h / 2, -1.0f, 1.0f); glLoadMatrixf (matScreen.m); @@ -222,7 +234,7 @@ void ToonScene::Render () glClearStencil (0); glClear (GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); - // Set the 3d projection matrix: + // Set perspective 3d projection matrix: glMatrixMode (GL_PROJECTION); matrix4 matCamera = matPerspec(VIEW_ANGLE, (GLfloat) w / (GLfloat) h, NEAR_VIEW, FAR_VIEW); glLoadMatrixf(matCamera.m); @@ -249,7 +261,7 @@ void ToonScene::Render () glCullFace (GL_FRONT); glEnable (GL_DEPTH_TEST); - glDepthMask (GL_FALSE); + glDepthMask (GL_FALSE); // no depth values, we need to draw inside it. glStencilFunc (GL_ALWAYS, 1, STENCIL_MASK); glStencilOp (GL_KEEP, GL_KEEP, GL_REPLACE); @@ -271,7 +283,7 @@ void ToonScene::Render () glColor4fv (meshDataHead.subsets.at("0").diffuse); RenderUnAnimatedSubset (&meshDataHead, "0"); // head - /* The face is inside the head, but drawn over it */ + // The face is inside the head, but drawn over it glDisable (GL_DEPTH_TEST); glColor4fv (meshDataHead.subsets.at("1").diffuse); @@ -285,6 +297,7 @@ void ToonScene::Render () glEnable (GL_DEPTH_TEST); + // Hair must be drawn over the face: glColor4fv (meshDataHead.subsets.at("2").diffuse); RenderUnAnimatedSubset (&meshDataHead, "2"); // hair diff --git a/src/test3d/toon.h b/src/test3d/toon.h index 1202e3c..3b3a72a 100644 --- a/src/test3d/toon.h +++ b/src/test3d/toon.h @@ -37,14 +37,17 @@ class ToonScene : public App::Scene // camera angles GLfloat angleY, angleX, distCamera; + // Background texture: Texture texBG; + // Head mesh MeshData meshDataHead; + // Handle to toon shader GLuint shaderProgram; public: - ToonScene (App*); + ToonScene (App *); ~ToonScene (); bool Init (void); diff --git a/src/test3d/water.cpp b/src/test3d/water.cpp index d405410..d87902e 100644 --- a/src/test3d/water.cpp +++ b/src/test3d/water.cpp @@ -35,11 +35,12 @@ #define VIEW_ANGLE 45.0f #define NEAR_VIEW 0.1f #define FAR_VIEW 1000.0f +#define CUBESIZE 2.0f WaterScene::WaterScene (App *pApp) : Scene(pApp), - fbReflection(0), cbReflection(0), dbReflection(0), texReflection(0), - fbRefraction(0), cbRefraction(0), dbRefraction(0), texRefraction(0), + fbReflection(0), texReflection(0), texReflecDepth(0), + fbRefraction(0), texRefraction(0), texRefracDepth(0), shaderProgramWater(0), timeDrop(0), @@ -56,8 +57,8 @@ WaterScene::WaterScene (App *pApp) : Scene(pApp), { for(int z=0; zGetMainWindow (), &w, &h); GLenum status; + char errBuf [100]; std::string resourceZip = std::string(SDL_GetBasePath()) + "test3d.zip", shaderDir = "shaders/", @@ -78,35 +80,24 @@ bool WaterScene::Init() /* Build two framebuffers for reflection and refraction. - Each framebuffer gets a color and depth buffer. + Each framebuffer gets a color and depth-stencil texture. - Also buid two textures, to which the frame buffer's pixel data - will be stored. + The color and depth values will be used by the pixel shader. + The stencil buffer is needed during render to texture. */ glGenFramebuffers (1, &fbReflection); glGenFramebuffers (1, &fbRefraction); - glGenRenderbuffers (1, &cbReflection); - glGenRenderbuffers (1, &cbRefraction); - glGenRenderbuffers (1, &dbReflection); - glGenRenderbuffers (1, &dbRefraction); - glGenTextures (1, &texReflection); glGenTextures (1, &texRefraction); + glGenTextures (1, &texReflecDepth); + glGenTextures (1, &texRefracDepth); glBindFramebuffer (GL_FRAMEBUFFER, fbReflection); - glBindRenderbuffer (GL_RENDERBUFFER, cbReflection); - glRenderbufferStorage (GL_RENDERBUFFER, GL_RGBA, w, h); - glFramebufferRenderbuffer (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, cbReflection); - - glBindRenderbuffer(GL_RENDERBUFFER, dbReflection); - glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, w, h); - glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, dbReflection); - glBindTexture(GL_TEXTURE_2D, texReflection); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, w, h, 0, GL_RGBA,GL_UNSIGNED_BYTE, NULL); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); @@ -114,26 +105,27 @@ bool WaterScene::Init() glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, texReflection, 0); + glBindTexture(GL_TEXTURE_2D, texReflecDepth); + glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH24_STENCIL8, w, h, 0, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, NULL); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + glFramebufferTexture(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, texReflecDepth, 0); + status = glCheckFramebufferStatus(GL_FRAMEBUFFER); glBindFramebuffer(GL_FRAMEBUFFER, 0); - if(status != GL_FRAMEBUFFER_COMPLETE) + if (status != GL_FRAMEBUFFER_COMPLETE) { - SetError ("error, reflection framebuffer is not complete"); + GLFrameBufferErrorString (errBuf, status); + + SetError ("error, reflection framebuffer is not complete: %s", errBuf); return false; } glBindFramebuffer (GL_FRAMEBUFFER, fbRefraction); - glBindRenderbuffer (GL_RENDERBUFFER, cbRefraction); - glRenderbufferStorage (GL_RENDERBUFFER, GL_RGBA, w, h); - glFramebufferRenderbuffer (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, cbRefraction); - - glBindRenderbuffer (GL_RENDERBUFFER, dbRefraction); - glRenderbufferStorage (GL_RENDERBUFFER, GL_DEPTH_COMPONENT, w, h); - glFramebufferRenderbuffer (GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, dbRefraction); - glBindTexture (GL_TEXTURE_2D, texRefraction); glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA8, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); @@ -141,15 +133,24 @@ bool WaterScene::Init() glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); - glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, texRefraction, 0); + glFramebufferTexture (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, texRefraction, 0); - status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + glBindTexture (GL_TEXTURE_2D, texRefracDepth); + glTexImage2D (GL_TEXTURE_2D, 0, GL_DEPTH24_STENCIL8, w, h, 0, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, NULL); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glBindFramebuffer(GL_FRAMEBUFFER, 0); + glFramebufferTexture (GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, texRefracDepth, 0); + + status = glCheckFramebufferStatus (GL_FRAMEBUFFER); + + glBindFramebuffer (GL_FRAMEBUFFER, 0); if (status != GL_FRAMEBUFFER_COMPLETE) { - SetError ("error, refraction framebuffer is not complete"); + GLFrameBufferErrorString (errBuf, status); + + SetError ("error, refraction framebuffer is not complete: %s", errBuf); return false; } @@ -200,14 +201,11 @@ WaterScene::~WaterScene() glDeleteTextures(1, &texReflection); glDeleteTextures(1, &texRefraction); + glDeleteTextures(1, &texReflecDepth); + glDeleteTextures(1, &texRefracDepth); glDeleteFramebuffers(1, &fbReflection); glDeleteFramebuffers(1, &fbRefraction); - - glDeleteRenderbuffers(1, &cbReflection); - glDeleteRenderbuffers(1, &cbRefraction); - glDeleteRenderbuffers(1, &dbReflection); - glDeleteRenderbuffers(1, &dbRefraction); } void WaterScene::UpdateWaterNormals() { @@ -268,6 +266,32 @@ void WaterScene::MakeWave (const vec3 p, const float l) } } } +void WaterScene::CubeBlockWaves () +{ + float x1 = posCube.x - CUBESIZE / 2, x2 = posCube.x + CUBESIZE / 2, + y1 = posCube.y - CUBESIZE / 2, y2 = posCube.y + CUBESIZE / 2, + z1 = posCube.z - CUBESIZE / 2, z2 = posCube.z + CUBESIZE / 2, + + smallestDist2; + + for (int x = 1; x < GRIDSIZE; x++) + { + for(int z = 1; z < GRIDSIZE; z++) + { + if (0 > y1 && 0 < y2) + { + if (gridpoints[x][z].x > x1 && gridpoints[x][z].x < x2 && + gridpoints[x][z].z > z1 && gridpoints[x][z].z < z2) + { + // Cancel all waves at the edge of the cube. + + gridpoints[x][z].y = 0; + } + } + } + } +} + #define SQRT2DIV2 0.707106781186547524400844362104849f void WaterScene::UpdateWater (const float dt) { @@ -289,16 +313,18 @@ void WaterScene::UpdateWater (const float dt) { for(int z=1; z<(GRIDSIZE-1); z++) { - d=gridpoints[x][z].y - gridpoints[x-1][z].y; + // The forces are composed of the y level differences between points: + + d=gridpoints[x][z].y - gridpoints[x-1][z].y; gridforces[x][z]-=d; gridforces[x-1][z]+=d; - d=gridpoints[x][z].y - gridpoints[x+1][z].y; + d=gridpoints[x][z].y - gridpoints[x+1][z].y; gridforces[x][z]-=d; gridforces[x+1][z]+=d; - d=gridpoints[x][z].y - gridpoints[x][z-1].y; + d=gridpoints[x][z].y - gridpoints[x][z-1].y; gridforces[x][z]-=d; gridforces[x][z-1]+=d; - d=gridpoints[x][z].y - gridpoints[x][z+1].y; + d=gridpoints[x][z].y - gridpoints[x][z+1].y; gridforces[x][z]-=d; gridforces[x][z+1]+=d; d=gridpoints[x][z].y - gridpoints[x-1][z+1].y; d*=SQRT2DIV2; @@ -312,6 +338,9 @@ void WaterScene::UpdateWater (const float dt) d=gridpoints[x][z].y - gridpoints[x+1][z-1].y; d*=SQRT2DIV2; gridforces[x][z]-=d; gridforces[x+1][z-1]+=d; + + // friction: + // gridforces[x][z] -= gridspeeds[x][z]; } } @@ -319,21 +348,20 @@ void WaterScene::UpdateWater (const float dt) if (ft > 1.0f) ft = 1.0f; - // friction / viscosity - float u = (float)exp(250.0f * dt * log(0.99f)); + float u = (float)exp (-2.5 * dt); for(int x=0; x0) return ((*this)/l); else return *this; } + /* + vec3 objects can be added on, subtracted and + multiplied/divided by a scalar. + */ + vec3 operator- () const { return vec3(-x,-y,-z); } vec3 operator- (const vec3 &v) const { return vec3(x-v.x,y-v.y,z-v.z); } vec3 operator+ (const vec3 &v) const { return vec3(x+v.x,y+v.y,z+v.z); } @@ -120,8 +134,10 @@ inline vec3 ClosestPointOnLine(const vec3& l1,const vec3& l2,const vec3& point) struct Plane { - vec3 n; - float d; + vec3 n; // plane normal, should have length 1.0 + float d; // shortest distance from (0,0,0) to plane + + // n * d is a point on the plane }; inline Plane Flip (const Plane &plane) @@ -146,7 +162,7 @@ struct Triangle { p[2] = p2; } - vec3 Center() const + vec3 Center () const { vec3 pb = 0.5f * (p[1] - p[0]), pz = pb + 0.333333f*(p[2] - pb); @@ -154,7 +170,7 @@ struct Triangle { return pz; } - Plane GetPlane() const + Plane GetPlane () const { Plane plane; plane.n = Cross(p[1] - p[0], p[2] - p[0]).Unit();