Jump to content

OandO

Member Since 11 Oct 2010
Online Last Active Today, 03:33 PM

Topics I've Started

OpenGL Texture Slots and Vertex Texture Fetching

25 January 2017 - 01:12 PM

A couple of questions about how OpenGL uses textures across vertex and fragment shaders. I've been working on a materials system, and I'm currently implementing a simple list of textures that are in use, to reduce the number of texture binds done if sequential materials use the same textures. If I'm understanding this correctly, I've found that the vertex shader and fragment shader have access to different numbers of texture slots, however the information here seems to be self-contradictory:

https://www.khronos.org/opengl/wiki/Vertex_Texture_Fetch

 

 

 

GPUs that support VTF use the same TIUs as the fragment shader. This means that you can bind a texture to TIU 1 and sample that same texture in the vertex shader and also the fragment shader.

 

 

 

If in your VS and FS, you access the same texture, that counts like accessing 2 TIUs.

 

I don't understand why this would be the case, or how to work with it. If I wanted to use a texture in the vertex, I'd have to bind it to one of the slots accessible by the vertex shader, but there's some additional cost to then accessing it in the fragment shader as well? I'm hoping someone can clarify this, as these two points seem to be at odds with each other.


GJK, Curved Shapes and Passing Through the Origin

15 July 2015 - 06:31 AM

I've been working on an implementation of GJK to determine the closest points between pairs of shapes. For the time being I'm working in 2D, my goal is to build a simplex as close to the origin as possible, and terminate when a 3-point simplex contains the origin. It's almost working correctly but there are a couple of cases which are still tripping me up.

For implicit, curved shapes the algorithm regularly fails to converge, especially for relatively shallow penetrations. The chapter on GJK in Real-Time Collision Detection suggests a tolerance in the termination condition, but I can't figure out what exactly it's supposed to be tolerating... I suspect the problem has something to do with there being and infinite number of potential support points to choose from, but I'm uncertain of how to combat that.

 

The other problem is deciding what to do when a 2-point simplex passes directly through the origin. This seems common with curved shapes but I was also able to engineer situations where it would occur with polygons. Of course if a 2-point simplex is passing through the origin then the shapes are intersecting, but the aim is to build a complete simplex and use it later to determine penetration depth. When the origin lies on the line segment formed by a 2-point simplex, I can't create a valid search direction. I tried picking a random direction and working from there, but I found a large number of false positives reported, and wasn't able to reliably catch and fix these.


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!