struct Akimbo_2_Code {
Welcome to my Akimbo 2 Code page. Here you can see a few snippets of code that I wrote for my current project Akimbo 2.
Akimbo 2 is a dual wielding FPS that uses most of the systems I have developed. You learn more about the game on its project page.
Because Akimbo 2 is a FPS with moving crosshairs, we needed a function that rotates the guns to face the crosshairs. Unfortunately, the crosshairs do not have 3D world coordinates, but rather 2D pixel coordinates. Luckily the engine I use has a pick ray function already defined that gives me a 3D point of a pixel, using the current OpenGl Model-view matrix, along with the direction of the ray coming out of that pixel. This info along with an up vector is used to rotate the guns to looks at the crosshair. There is also a feature in Akimbo_2 that allows the player to lock onto a target. In this case we want a matrix that looks at some 3D world point. I use a camera look-at function to find this matrix and then simply apply it to the gun.See Akimbo_2_Code_1 (below).
Another piece of code that I am fond of is my Sound Manager class. The engine I use has only very simplistic sound functions that allow you to adjust the volume and panning of sounds. I really wanted to have 3D sounds so I could hear where the gun shots were coming from. Akimbo_2_Code_2 shows off the update function for my 3D sounds. It is amazing what the dot product can do!
Akimbo_2_Code_1: code that rotates a gun to view either a 3D point in the world, or a 2D point on the camera.
voidWeaponBase::playerRelativeDisplacement() { //The guns displacement matrix is entirely local to its own coordinate system, //and then multiplied with the cameras matrix. But because qefnWinPickRay uses //current model view matrix, I am going to have to load the identity matrix into OpenGL if(crosshair == NULL) return; if(lockedOn) { float mat12[12]; float xyzUp[3] = {0,1,0}; float breakAngle = 60; //calculate the angle of the players view and the ray to the target Vector playerToTarget = lockOnObject->pos-player->pos; playerToTarget.normalize(); float dotProduct = dot(player->dir,playerToTarget); float angle = RAD2DEG(acos(dotProduct)); //if greater than the break angle, release lockon! if(angle > breakAngle) { releaseLockOn(); return; //get out! } //create a look at matrix that looks at the target qeCamLookAtM12f(mat12,pos.buf(),lockOnObject->pos.buf(),xyzUp); mat16.CopyFromMat12f(mat12); //we have annihilated the displacement! Get it back! Matrix playerViewDis; multiply(player->view,displace,playerViewDis); mat16.SetTranslation(playerViewDis.GetTranslation()); //gun ends up facing the wrong way, flip it around! Matrix correction(4,4,1); correction.MakeRotation(180,Vector(0,1,0)); //I don't think this is right, it should be the up vector mat16 *= correction; //recalculate the direction of the gun in world space, and we're done! multiply(mat16,Vector(0,0,1),dir); dir.normalize(); } else { Point xyz; //Make sure we are in the Model View Matrix mode glMatrixMode(GL_MODELVIEW); //save the current model view matrix glPushMatrix(); //load up the identity glLoadIdentity(); //This uses the currently set Model View Matrix in OpenGL, which we just set to the Identity qefnWinPickRay(xyz.buf(),dir.buf(),crosshair->getScreenPos()[X],crosshair->getScreenPos()[Y]); dir.normalize(); //create the displacement rotation matrix getGunMatrix(-dir,xyz,Vector(0,1,0),displaceRot); //we are now done with the Model View Matrix, set it back to the way it was glPopMatrix(); } }
Akimbo_2_Code_2: Code that calcualtes both the volume and pan values for a 3D sounds.
int SoundManager::update() { //keep track of which sounds need a killing stack<list<Sound3D>::iterator> killStack; //for each 3D sound, measure the point distances, and scale the volume list<Sound3D>::iterator itr = sound3DList.begin(); for(;itr != sound3DList.end(); itr++) { int killSound; //For volume, we simply find the distance to the emitter //convert the distance to a 0-1 range (1 = on top of emitter) //and multiply the max volume by this scale factor. float distance = itr->emitter->distanceTo(*(itr->listenerPos)); float distanceFractor = distance/sqr(itr->range); float scale = 1-distanceFractor; float vol = QESNDVOL_MAX * scale; //For panning we need need to know if the emitter is on the left or right //We use the forward vector and up vector to calculate the right vector Vector right = cross(*(itr->listenerVec),*(itr->listenerUp)); right.normalize(); Vector dirToSound = *(itr->emitter) - *(itr->listenerPos); dirToSound.normalize(); //negative angle = left ear //positive angle = right ear float angle = dot(dirToSound, right); float pan = QESNDVOL_MAX*angle; killSound = qeSndPlayIdVol(itr->soundID, vol); killSound = qeSndPlayIdPan(itr->soundID, pan); if(killSound>0) { killStack.push(itr); } } //kill any sounds that have been marked to kill while(!killStack.empty()) { sound3DList.erase(killStack.top()); killStack.pop(); } return 0; }