Jump to content

  • Log In with Google      Sign In   
  • Create Account


OandO

Member Since 11 Oct 2010
Offline Last Active Today, 05:04 PM

Topics I've Started

CORRECT gluLookAt clone

25 December 2013 - 12:25 PM

While searching for information on how gluLookAt works, I found out that a lot of the code floating around on the internet is based on incorrect assumptions about how the modelview matrix works, and I didn't find anything that produced the correct result. In case anyone else runs into this at any point, I've written a correct version, that produces an identical matrix to gluLookAt:

void flor_Cam_LookAt(mat4x4_t matrix, vec3_t eyePos, vec3_t lookAt, vec3_t up)
{
vec3_t side;
vec3_t forward;
vec3_t newUp;
double length;
 
flor_Maths_Vec3Normalise(up, up);
forward[0] = lookAt[0] - eyePos[0];
forward[1] = lookAt[1] - eyePos[1];
forward[2] = lookAt[2] - eyePos[2];
length = sqrt((forward[0]*forward[0])+(forward[1]*forward[1])+(forward[2]*forward[2]));
forward[0]/=length; // Saves on an additional sqrt performed by flor_Maths_Vec3Normalise()
forward[1]/=length;
forward[2]/=length;
flor_Maths_Vec3CrossProduct(forward, up, side);
flor_Maths_Vec3Normalise(side, side);
flor_Maths_Vec3CrossProduct(side, forward, newUp);
flor_Maths_Vec3Normalise(newUp, newUp);
 
matrix[0] = side[0];
matrix[4] = side[1];
matrix[8] = side[2];
matrix[1] = newUp[0];
matrix[5] = newUp[1];
matrix[9] = newUp[2];
matrix[2] = -forward[0];
matrix[6] = -forward[1];
matrix[10] = -forward[2];
matrix[3] = matrix[7] = matrix[11] = 0.0;
 
matrix[12] = 0.0;
matrix[13] = 0.0;
matrix[14] = 0.0;
matrix[15] = 1.0;
 
matrix[12]=matrix[0]*-eyePos[0]+matrix[4]*-eyePos[1]+matrix[8]*-eyePos[2]+matrix[12];
matrix[13]=matrix[1]*-eyePos[0]+matrix[5]*-eyePos[1]+matrix[9]*-eyePos[2]+matrix[13];
matrix[14]=matrix[2]*-eyePos[0]+matrix[6]*-eyePos[1]+matrix[10]*-eyePos[2]+matrix[14];
matrix[15]=matrix[3]*-eyePos[0]+matrix[7]*-eyePos[1]+matrix[11]*-eyePos[2]+matrix[15];
}

Matrices are column-major arrays, and vectors are just arrays too, hopefully it's reasonably easy to understand.


The simplest of tasks...

23 October 2013 - 09:59 AM

I now understand why games tend to take a very simplistic approach to reloading. I wanted to be clever, these two functions are a very roundabout way of incrementing one counter and decrementing another:

void updateWeaponLoadState(characterUnit_t *character)
{
int fireMode = 0; // FIXME - retrieve this from player structure
int loadType = character->weaponSlot[character->ps.weapon].loadType;
 
if (character->ps.magazineAmmo[character->ps.weapon] + character->ps.chamberedAmmo[character->ps.weapon] <= 0)
{ // No ammo, switch to empty state unless we're trying to reload
if (character->ps.weaponLoadState[character->ps.weapon] != RELOADSTATE_REMOVING_OLD_AMMO
&& character->ps.weaponLoadState[character->ps.weapon] != RELOADSTATE_ADDING_NEW_AMMO
&& character->ps.weaponLoadState[character->ps.weapon] != RELOADSTATE_OPENING
&& character->ps.weaponLoadState[character->ps.weapon] != RELOADSTATE_CLOSING)
{ // We not removing an empty mag or putting a new mag in at the moment, so set state to empty
character->ps.weaponLoadState[character->ps.weapon] = RELOADSTATE_EMPTY;
return;
}
}
 
if (character->weaponSlot[character->ps.weapon].shotsPerTrigger[fireMode] > 0)
{ // We're a single shot or burst weapon...
if (isWeaponManuallyCockedAfterShot(character) && character->ps.shotsRemaining == 0)
{ // ... that needs to be manually recocked after firing, and has finished firing
if (character->ps.weaponLoadState[character->ps.weapon] == RELOADSTATE_COCKING && character->ps.weaponTime <= GameTime)
{ // We've completed the cocking sequence, go back to ready state
character->ps.weaponLoadState[character->ps.weapon] = RELOADSTATE_READY; // Set up to ready, and take a round from mag to chamber
character->ps.chamberedAmmo[character->ps.weapon]++; // FIXME - more/less than one round?
character->ps.magazineAmmo[character->ps.weapon]--;
}
else if (character->ps.weaponLoadState[character->ps.weapon] == RELOADSTATE_FIRING)
{ // Weapon is not cocked, do it now.
character->ps.weaponLoadState[character->ps.weapon] = RELOADSTATE_COCKING;
// FIXME - cocking time could perhaps be affected by character stats.
character->ps.weaponTime = GameTime + character->weaponSlot[character->ps.weapon].cockingTime; 
}
}
}
else if (character->ps.weaponLoadState[character->ps.weapon] == RELOADSTATE_FIRING && character->ps.weaponTime <= GameTime)
{ // An automatic or semi-automatic weapon that still has some ammo, and should be ready to fire again
character->ps.weaponLoadState[character->ps.weapon] = RELOADSTATE_READY;  // Set to ready, and take a round from mag to chamber
character->ps.chamberedAmmo[character->ps.weapon]++; // FIXME - more/less than one round?
character->ps.magazineAmmo[character->ps.weapon]--;
return;
}
 
if (character->ps.weaponState != WEAPONSTATE_RAISING &&
character->ps.weaponState != WEAPONSTATE_LOWERING &&
character->ps.weaponState != WEAPONSTATE_DROPPED)
{ // We're in a state where reloading is allowed
// Do these checks again, just in case something weird happened
if (character->ps.magazineAmmo[character->ps.weapon] >= character->weaponSlot[character->ps.weapon].magazineSize)
{ // Already a full mag, don't reload
return;
}
if (character->ps.shotsRemaining > 0)
{ // Don't reload if we're still trying to fire a burst
return;
}
// FIXME - Make sure we have ammo to put in our weapon
 
character->ps.weaponState = WEAPONSTATE_RELOADING;
 
if (character->ps.weaponTime <= GameTime)
{ // Ready to move to the next phase of our reload
if (character->ps.weaponLoadState[character->ps.weapon] == RELOADSTATE_READY)
{ // we've finished reloading apparently?
character->ps.weaponState = WEAPONSTATE_READY;
}
else if (character->ps.weaponLoadState[character->ps.weapon] == RELOADSTATE_REMOVING_OLD_AMMO)
{ // For any weapon type, adding new ammo will directly follow in from removing old
character->ps.weaponLoadState[character->ps.weapon] = RELOADSTATE_ADDING_NEW_AMMO;
character->ps.weaponTime += character->weaponSlot[character->ps.weapon].magInsertTime;
character->ps.magazineAmmo[character->ps.weapon] = 0;
}
else if (character->ps.weaponLoadState[character->ps.weapon] == RELOADSTATE_ADDING_NEW_AMMO)
{ // Just finished adding a round/magazine/whatever
if ((loadType == WTYPE_HAMMER_BREAK || loadType == WTYPE_HAMMER_BREAK_MULTI ||
loadType == WTYPE_BOLTACTION_BREECHLOAD || loadType == WTYPE_AUTO_BREECHLOAD)
&& character->ps.magazineAmmo[character->ps.weapon] < character->weaponSlot[character->ps.weapon].magazineSize)
{ // A weapon that loads one round at a time, and we have room for another
character->ps.magazineAmmo[character->ps.weapon]++; // FIXME - This could add more than one round?
// FIXME - remove a round from our ammo reserves
character->ps.weaponTime += character->weaponSlot[character->ps.weapon].magInsertTime;
}
else if (loadType == WTYPE_HAMMER_BREAK || loadType == WTYPE_HAMMER_BREAK_MULTI ||
loadType == WTYPE_BOLTACTION_BREECHLOAD)
{ // A weapon that loads one at a time, but is already full and needs closing
character->ps.weaponLoadState[character->ps.weapon] = RELOADSTATE_CLOSING;
character->ps.weaponTime += character->weaponSlot[character->ps.weapon].weaponCloseTime;
}
else if (loadType == WTYPE_AUTO_BREECHLOAD && character->ps.chamberedAmmo[character->ps.weapon] < character->weaponSlot[character->ps.weapon].chamberedRounds)
{ // Needs to be cocked
character->ps.weaponLoadState[character->ps.weapon] = RELOADSTATE_COCKING;
character->ps.weaponTime += character->weaponSlot[character->ps.weapon].cockingTime;
}
else
{ // A weapon that fills up all in one go
character->ps.magazineAmmo[character->ps.weapon] = character->weaponSlot[character->ps.weapon].magazineSize; // FIXME - Add the amount of the mag we're holding in our hand, in case we're cancelling a reload
if (loadType == WTYPE_HAMMER_BREAKSIMULTANEOUS || loadType == WTYPE_HAMMER_BREAKSIMULTANEOUS_MULTI ||
loadType == WTYPE_BOLTACTION_CLIP || loadType == WTYPE_AUTO_CLIP || WTYPE_AUTO_BELT)
{ // Needs to be closed now the new ammo has been added
character->ps.weaponLoadState[character->ps.weapon] = RELOADSTATE_CLOSING;
character->ps.weaponTime += character->weaponSlot[character->ps.weapon].weaponCloseTime;
}
else if (character->ps.chamberedAmmo[character->ps.weapon] < character->weaponSlot[character->ps.weapon].chamberedRounds)
{ // Needs to be cocked immediately after adding the ammo, because there's an empty chamber
character->ps.magazineAmmo[character->ps.weapon] = character->weaponSlot[character->ps.weapon].magazineSize; // FIXME - Add the amount of the mag we're holding in our hand, in case we're cancelling a reload
character->ps.weaponLoadState[character->ps.weapon] = RELOADSTATE_COCKING;
character->ps.weaponTime += character->weaponSlot[character->ps.weapon].cockingTime;
}
else
{ // Doesn't need to be cocked or closed, ready to fire again
character->ps.magazineAmmo[character->ps.weapon] = character->weaponSlot[character->ps.weapon].magazineSize; // FIXME - Add the amount of the mag we're holding in our hand, in case we're cancelling a reload
character->ps.weaponLoadState[character->ps.weapon] = RELOADSTATE_READY;
character->ps.weaponState = WEAPONSTATE_READY;
}
}
}
else if (character->ps.weaponLoadState[character->ps.weapon] == RELOADSTATE_OPENING)
{ // Just finished opening the weapon
if (loadType == WTYPE_HAMMER_BREAK || loadType == WTYPE_HAMMER_BREAKSIMULTANEOUS || 
loadType == WTYPE_HAMMER_BREAK_MULTI || loadType == WTYPE_HAMMER_BREAKSIMULTANEOUS_MULTI ||
loadType == WTYPE_AUTO_BELT)
{ // Old rounds have to be removed once the weapon is opened
character->ps.weaponLoadState[character->ps.weapon] = RELOADSTATE_REMOVING_OLD_AMMO;
character->ps.weaponTime += character->weaponSlot[character->ps.weapon].magPullTime;
}
else
{ // Can start adding rounds immediately after opening
character->ps.weaponLoadState[character->ps.weapon] = RELOADSTATE_ADDING_NEW_AMMO;
character->ps.weaponTime += character->weaponSlot[character->ps.weapon].magInsertTime;
}
}
else if (character->ps.weaponLoadState[character->ps.weapon] == RELOADSTATE_CLOSING)
{ // Just finished closing the weapon
if (loadType == WTYPE_BOLTACTION_CLIP || loadType == WTYPE_BOLTACTION_BREECHLOAD)
{ // Closing the weapon cocks it, so we're ready to fire
character->ps.magazineAmmo[character->ps.weapon]--; // FIXME - could chamber !1 round?
character->ps.chamberedAmmo[character->ps.weapon]++; // FIXME - could chamber !1 round?
character->ps.weaponLoadState[character->ps.weapon] = RELOADSTATE_READY;
character->ps.weaponState = WEAPONSTATE_READY;
}
else
{ // Weapon needs to be cocked after closing
character->ps.weaponLoadState[character->ps.weapon] = RELOADSTATE_COCKING;
character->ps.weaponTime += character->weaponSlot[character->ps.weapon].cockingTime;
}
}
else if (character->ps.weaponLoadState[character->ps.weapon] == RELOADSTATE_COCKING)
{ // Just finished cocking the weapon, ready to fire
character->ps.magazineAmmo[character->ps.weapon]--; // FIXME - could chamber !1 round?
character->ps.chamberedAmmo[character->ps.weapon]++; // FIXME - could chamber !1 round?
character->ps.weaponLoadState[character->ps.weapon] = RELOADSTATE_READY;
character->ps.weaponState = WEAPONSTATE_READY;
}
}
}
}
 
void startWeaponReload(characterUnit_t *character)
{
completeWeapon_t *weapon = &character->weaponSlot[character->ps.weapon];
int *loadState = &character->ps.weaponLoadState[character->ps.weapon];
unsigned int *weaponTime = &character->ps.weaponTime;
 
if (character->ps.magazineAmmo[character->ps.weapon] >= weapon->magazineSize)
{ // Already a full mag, don't reload
return;
}
if (character->ps.shotsRemaining > 0)
{ // Don't reload if we're still trying to fire a burst
return;
}
// FIXME - Make sure we have ammo to put in our weapon
 
// Put us into the correct state for immediately after starting a reload
switch (character->weaponSlot[character->ps.weapon].loadType)
{
case WTYPE_MELEE:
return; // Melee shouldn't ever have to reload, right?
case WTYPE_HAMMER_BREAK:
*loadState = RELOADSTATE_OPENING;
*weaponTime = GameTime + weapon->weaponOpenTime;
break;
case WTYPE_HAMMER_BREAKSIMULTANEOUS:
*loadState = RELOADSTATE_OPENING;
*weaponTime = GameTime + weapon->weaponOpenTime;
break;
case WTYPE_HAMMER_BREAK_MULTI:
*loadState = RELOADSTATE_OPENING;
*weaponTime = GameTime + weapon->weaponOpenTime;
break;
case WTYPE_HAMMER_BREAKSIMULTANEOUS_MULTI:
*loadState = RELOADSTATE_OPENING;
*weaponTime = GameTime + weapon->weaponOpenTime;
break;
case WTYPE_BOLTACTION_CLIP:
*loadState = RELOADSTATE_OPENING;
*weaponTime = GameTime + weapon->weaponOpenTime;
break;
case WTYPE_BOLTACTION_BREECHLOAD:
*loadState = RELOADSTATE_OPENING;
*weaponTime = GameTime + weapon->weaponOpenTime;
break;
case WTYPE_BOLTACTION_MAGAZINE:
*loadState = RELOADSTATE_REMOVING_OLD_AMMO;
*weaponTime = GameTime + weapon->magPullTime;
break;
case WTYPE_AUTO_CLIP:
*loadState = RELOADSTATE_OPENING;
*weaponTime = GameTime + weapon->weaponOpenTime;
break;
case WTYPE_AUTO_MAGAZINE:
*loadState = RELOADSTATE_REMOVING_OLD_AMMO;
*weaponTime = GameTime + weapon->magPullTime;
break;
case WTYPE_AUTO_MAGAZINE_SELFEJECTING:
*loadState = RELOADSTATE_ADDING_NEW_AMMO;
*weaponTime = GameTime + weapon->magInsertTime;
break;
case WTYPE_AUTO_BELT:
*loadState = RELOADSTATE_OPENING;
*weaponTime = GameTime + weapon->weaponOpenTime;
break;
case WTYPE_AUTO_BREECHLOAD:
*loadState = RELOADSTATE_ADDING_NEW_AMMO;
*weaponTime = GameTime + weapon->magInsertTime;
break;
default:
return;
}
 
character->ps.weaponState = WEAPONSTATE_RELOADING;
}

I'll let you know if it works. Edit: It didn't, but now it does.

 

Also the forum deleted all my indentations, but that just adds to the horror.


A few questions about UDP sockets

05 August 2013 - 10:15 AM

I've been working on fairly simple client-server setup for a game, using UPD (It's an arena shooter with lots of moving objects) and doing raw socket programming in C. I know how to create sockets, how to send, receive and process packets and whatnot, and at the other end I know what I want to send, who to and how often. However I've got a fairly substantial gap in my knowledge about the general layout I should be using:

 

Should I be using separate sockets on the server for each client, what are the advantages/disadvantages of this over a single socket for everyone?

 

Should I have separate inbound/outbound sockets? I believe this is a requirement for TCP, not sure how UDP deals with it though. I know using one socket for both in and out works, just not sure if it's a good way of doing things.

 

How do I allow any combination of clients and server using IPv4 and IPv6 to connect to each other?

 

Perhaps I'm just not searching for the right terms, but I'm having difficultly finding resources and this bit-in-the-middle sort of thing, so any help would be great!


Thread Pool - how big should it be?

11 March 2013 - 12:25 PM

I'm just starting to learn about multithreading, and I've begun implementing a simple thread pooling system into my game engine project. So far I've got a user-specified number of threads created on start-up, which then go into a sleep state until they're woken up and told what to do. The plan is that each sequential task will be able to grab a number of idle threads, and divide it's workload between them, after which they will go back to sleep until the next task needs them. (I'm considering also having a thread that's dedicated to background loading for things like streaming audio, but that's irrelevant at the moment)

 

The problem is I don't know how to determine the number of threads that should be created. Each task will just divide up it's workload amongst whatever is available (Tasks run consecutively and parallelise processing of individual objects internally, rather than several tasks running concurrently).


Choosing an appropriate space partitioning method

27 November 2012 - 11:20 AM

I'm trying to come up with a way of spatially describing objects in an environment. I've read a lot of articles and discussions on scene graphs and various methods of space-partitioning (Yes, I understand the difference between the two) but I still don't feel as though I any closer to deciding upon a good method.

I've got a very large game world, (3D, but mostly spread out horizontally and predominantly outdoors) which I'm going to be stream-loading content into. There's a large range of sizes that an object can be, everything from a tiny projectile up to huge buildings (Some smaller buildings which need to be easily added/removed from the world at times as well) and most objects are able to move in some way. I was considering using a quadtree/octree approach, but there are a few things putting me off that idea.

Firstly, if the game world is huge, it's presumably highly impractical to have a root node that's the size of the world. It would have to be split up many, many times until a node that encompasses just the currently loaded areas in created. One solution I suppose would be to divide the world up into a grid of potential large nodes, each one aware of it's neighbours, which can be dynamically grouped together to form the root node, or just be able to re-parent objects from one large node to another as they cross the boundaries. This seems overly complicated though, and makes it harder to find out simply where an object is. I'm also now of the impression that quadtree/octree designs aren't very good at tracking moving objects, I'm going to be rebuilding sections of the tree every frame when projectiles are in flight, for example. With a large number of moving objects, it seems inefficient to be constantly rebuilding the structure.

Part of the problem is that I'm not entirely sure what the responsibilities of the structure should be. Everything I read seems to describe a different set of problems being crammed into apparently similar designs. Using it to rapidly cull objects outside the field of view is an obvious use, is there a method that can easily incorporate occlusion culling as well? And if I'm representing the positions and rough sizes of objects in the world, should I expand it to assist in broad-phase collision detection as well?

PARTNERS