The system I'm running on is fairly dated. It's just an AMD 1.333 GHz with 64 MB GeForce 3 and 896 MB RAM with an Audigy 2 sound card.
Right now this is brute force. The hardware is clipping/culling the frustrum which is not the best idea in the world. I really want to implement my own frustrum culling except I'm not quite sure how to get the clip matrix from D3D. Once I figure that out I just test
-x<w<x
-y<w<y
-z<w<z
To create my render list. I have optimized the inner loop so that it renders all alpha blended objects at one time. Even if I have well over 10000 alpha blended objects it still only requires 2 state changes per frame which is very nice.
The planets are not progressive meshes and I'm kinda deciding whether to use them or not. It is a good possibility that progressive meshes may actually take longer to decimate than simply using set LOD's. Progressive LOD's may not work well.
Also as my system is old it really doesn't matter to me if I tax it because my system is not a good representation of my target platform. I also cannot use any pixel shaders over version 2.0 or vertex shaders over version 2.0. My card does not support them. So just because it lags on my system does not mean it will lag on another.
Very very soon I'm going to put all of this stuff in effects files so that I can check the device caps and adjust my display quality based on the rig it's running on.
Need help on these problems
But I'm having some problems with Window's messages. It seems that every time I move the cursor in the upper left hand corner the stupid mouse cursor appears, as well as when I move it in the upper right. I've trapped the WM_SYSCOMMAND message and trapped cracked it into lParam and wParam but it still allows you to select the system menu. I've looked at the Direct3D framework that MS wrote and it isn't doing anything I'm not doing. It's really 'bugging' me.
Firing lasers so they move through the target
Now I have this figured out except that the lasers don't look as though they move through the target until they are way off in the distance. In essence I need to adjust the convergence of the lasers so that they converge at a spot just in front or some distance in front of the ship. Should I alter the direction vector components individually by the up and right vectors of the camera or should I just wait until these are actually being fired by a gun in 3D space?
Sound clipping
It seems that DirectMusic doesn't like playing a lot of sound effects through one channel. It tends to clip them rather quickly. This is probably due to them using some crappy additive mixing scheme in the primary buffer. I.E.:
I would imagine its something like this since I do have some experience in writing sound engines.
Code:
WORD finalsamplevalue=0;
std::vector<Buffer>::iterator ptr;
for (ptr=Buffers.begin();ptr!=Buffers.end();ptr++)
{
WORD samplevalue=(ptr->SoundData[ptr->PlayCursor]*ptr->VolumeCoef);
finalsamplevalue++;
}
finalsamplevalue/=numsamples;
//clamp final mix
if (finalsamplevalue<0) finalsamplevalue=0;
if (finalsamplevalue>65535) finalsamplevalue=65535;
....write final sample value to buffer
This is of course for unsigned short sample buffers, but you get the idea. I shudder to think about writing my own mixer but if I keep getting this clipping I just might.
That's where I'll probably move to pure assembly and use MMX so I can be fast...even though it won't be as fast as the hardware.
Rotating objects to match their velocity vector
Now I know how to compute a velocity vector from one point in space to another, but I'm not sure how to actually rotate the object to match the given vector orientation. Just simply using the normalized vector components for rotation does not work. The normalized vector will always fall in the range 0 to 1.0f, but the D3DXRotation functions expect a value from -PI to PI.
Things I've tried:
1. Compute dot product between velocity vector and current lookAt vector for object. Use dot product as angle argument for D3DXRotationAxis along the Velocity Vector.
Code:
D3DXVECTOR3 Base(0.0f,0.0f,1.0f); //pointing down positive Z
//Find angle between vectors
float dot=D3DXVec3Dot(&Base,&VelocityVector);
//Rotate on velocity vector by specified angle
D3DXMATRIX Final;
D3DXMatrixRotationAxis(&Final,&VelocityVector,dot);
But this does not work. This assumes that we are already on the velocity vector axis and so it does not work. If I use the base vector as the vector it still does not work properly.
I'm sorta lost.
2. Compute the picking ray that goes through mouse point mx,my. Compute atan2(y,z), atan2(x,z) and atan2(x,y). Rotate object by those amounts on x,y, and z. This produces some very ugly gimbal lock situations and is quite slow because of the atan2.
Here is the code I have so far:
Code:
#include "CLaser.h"
#include "math.h"
const DWORD LaserVertex::FVF=D3DFVF_XYZ | D3DFVF_TEX1;
void CActiveLaserContainer::Create(IDirect3DDevice9 *_Device,std::string _File)
{
Device=_Device;
//Properties.Position=D3DXVECTOR3(0.0f,0.0f,0.0f);
LaserVertex *verts;
Device->CreateVertexBuffer(4*sizeof(LaserVertex),
D3DUSAGE_WRITEONLY,
LaserVertex::FVF,
D3DPOOL_MANAGED,
&VB,
0);
VB->Lock(0,0,(void **)&verts,0);
verts[0]=LaserVertex(D3DXVECTOR3(-.3f, 0.0f, 5.0f), 0.0f, 0.0f);
verts[1]=LaserVertex(D3DXVECTOR3(.3f, 0.0f, 5.0f), 1.0f, 0.0f);
verts[2]=LaserVertex(D3DXVECTOR3(-.3f, 0.0f, 0.0f), 0.0f, 1.0f);
verts[3]=LaserVertex(D3DXVECTOR3(.3f, 0.0f, 0.0f), 1.0f, 1.0f);
VB->Unlock();
D3DXCreateTextureFromFile(Device,_File.c_str(),&Texture);
}
void CLaser::Create(IDirect3DDevice9 *_Device,
LaserProps _Props,
D3DXMATRIX _view,
D3DXVECTOR3 _lookAt,
unsigned long _mx,
unsigned long _my)
{
Properties=_Props;
//Compute picking ray
float px=0.0f;
float py=0.0f;
D3DVIEWPORT9 vp;
_Device->GetViewport(&vp);
D3DXMATRIX proj;
_Device->GetTransform(D3DTS_PROJECTION,&proj);
px=((( 2.0f*_mx)/ vp.Width ) - 1.0f)/ (proj(0,0));
py=(((-2.0f*_my)/ vp.Height) + 1.0f)/ (proj(1,1));
D3DXVECTOR3 Direction(px,py,1.0f);
//Compute Velocity Vector
D3DXMATRIX ViewInverse;
D3DXMatrixInverse(&ViewInverse,0,&_view);
D3DXVec3TransformNormal(&Direction,&Direction,&ViewInverse);
D3DXVec3Normalize(&Direction,&Direction);
Properties.VelocityVector=Direction;
//Setup base translation matrix
D3DXMATRIX Trans;
D3DXMatrixTranslation(&Trans,
Properties.GunPos.x,
Properties.GunPos.y,
Properties.GunPos.z);
//Setup laser-to-target translation matrices
D3DXMATRIX Trans2;
D3DXMatrixTranslation(&Trans2,Direction.x,Direction.y,1.0f);
//Rotate lasers toward's target
D3DXMATRIX Rot;
D3DXMatrixRotationYawPitchRoll(&Rot,px,-py,1.0f);
//Store our world matrix so we don't compute later
Properties.World=Rot*Trans*Trans2*ViewInverse;
}
void CActiveLaserContainer::Render(float timeDelta,D3DXMATRIX view,CLaser *Laser)
{
D3DXMATRIX world=Laser->Properties.World;
//Fast translation - no multiply
world._41=Laser->Properties.Position.x;
world._42=Laser->Properties.Position.y;
world._43=Laser->Properties.Position.z;
//Transform object
Device->SetTransform(D3DTS_WORLD,&world);
//Render object
Device->SetStreamSource(0,VB,0,sizeof(LaserVertex));
Device->SetFVF(LaserVertex::FVF);
Device->SetTexture(0,Texture);
Device->DrawPrimitive(D3DPT_TRIANGLESTRIP,0,2);
}
That's the relevant code for my laser creation and rendering. Please help me.
Here are the headers:
Code:
#ifndef CLASER
#define CLASER
#include "d3dx9.h"
#include "CQuickCom.h"
#include <vector>
//Current vertex type
struct LaserVertex
{
D3DXVECTOR3 Position;
float u,v;
LaserVertex(D3DXVECTOR3 &_pos,float _u,float _v):Position(_pos),u(_u),v(_v) {}
static const DWORD FVF;
};
//Structure for a ray if needed
struct CRay
{
D3DXVECTOR3 Origin;
D3DXVECTOR3 Direction;
};
//Laser properties structure - still a work in progress
struct LaserProps
{
//Position, speed, picking ray, and world matrix for this object
D3DXMATRIX World;
CRay PickingRay;
float Speed;
D3DXVECTOR3 Position;
//Convergence factor - not working
D3DXVECTOR3 ConvergenceVector;
//Current gun position and rotational matrix
D3DXVECTOR3 GunPos;
D3DXMATRIX *RotationMatrix;
D3DXVECTOR3 Rotation;
D3DXVECTOR3 VelocityVector;
//Attributes
float FireRate;
float RechargeRate;
float EnergyDrain;
float DamageAmount;
unsigned int DamageType;
float LifeTimer;
float LifeDistance;
//Textures - not used yet
IDirect3DTexture9 *Texture;
IDirect3DTexture9 *AlphaTexture;
//Sounds
WCHAR *SoundFile;
};
class CLaser
{
protected:
friend class CActiveLaserContainer;
LaserProps Properties;
CLaser(void) {}
public:
LaserProps GetProps(void) {return Properties;};
void Create(IDirect3DDevice9 *_Device,
LaserProps _Props,
D3DXMATRIX _view,
D3DXVECTOR3 _lookAt,
unsigned long _mx,
unsigned long _my);
//Update ourself
void Update(float timeDelta)
{
Properties.Position+=((Properties.VelocityVector*Properties.Speed)*timeDelta);
}
};
class CActiveLaserContainer
{
protected:
std::vector<CLaser> Lasers;
//Shared resources
IDirect3DTexture9 *Texture;
IDirect3DDevice9 *Device;
IDirect3DVertexBuffer9 *VB;
public:
CActiveLaserContainer(void):Texture(NULL),Device(NULL),VB(NULL) {}
virtual ~CActiveLaserContainer(void)
{
Lasers.clear();
SAFE_RELEASE(Texture);
SAFE_RELEASE(VB);
}
void Create(IDirect3DDevice9 *_Device,std::string _File);
//Add a laser to the mix
unsigned int AddLaser(LaserProps _NewLaser,
D3DXMATRIX _view,
D3DXVECTOR3 _lookAt,
unsigned long _mx,
unsigned long _my)
{
CLaser temp;
temp.Create(Device,_NewLaser,_view,_lookAt,_mx,_my);
Lasers.push_back(temp);
return Lasers.size()-1;
}
void Render(float timeDelta,D3DXMATRIX _view,CLaser *Laser);
void RemoveLaser(unsigned int _ID);
bool GetProps(unsigned int lasernum,LaserProps *outProps)
{
if (Lasers.empty()) return false;
std::vector<CLaser>::iterator laser=Lasers.begin();
int count=0;
while (count!=lasernum)
{
laser++;
count++;
}
outProps=&(laser->Properties);
return true;
}
//Update all lasers
void Update(D3DXVECTOR3 &_ObjectVector,D3DXMATRIX view,float timeDelta)
{
std::vector<CLaser>::iterator laser;
for (laser=Lasers.begin();laser!=Lasers.end();laser++)
{
//Check life-span
if (laser->Properties.LifeTimer>laser->Properties.LifeDistance)
{
if (laser!=Lasers.end()-1)
{
//We are dying
laser=Lasers.erase(laser);
}
}
//Make sure we are not at end of list - big time crash if we are
if (laser!=(Lasers.end()))
{
laser->Properties.LifeTimer+=timeDelta;
//Update laser -> faster than calling laser->Update()
laser->Properties.Position+=
(laser->Properties.VelocityVector*laser->Properties.Speed)*timeDelta;
//Draw the laser
Render(timeDelta,view,laser);
}
}
}
};
#endif
Notice that I've left some code in CLaser that doesn't do anything. I used to update the lasers in the local class but then decided it was faster to update it from the container class. In fact I'm learning that structure is very very important to how fast the thing will render. Making something private just to be C++-like is a sure way to destroy your framerates. Every time you call a function you create function call overhead. Do that about 10000 times a frame and you crawl.
Sorry so long but you can't say I didn't post enough code for you to help me. I tried on my own first I'm thinking.