Hello,

I'm trying to make a simple 3D wireframe model viewer. This is being done using the good ol' Windows GD of I as opposed to Direct3D or OpenGL. Performance is not really my objective here, accuracy is a lot more important.

Reading around, quaternions seem all the rage. To that end, I have attempted to project the vertices of the model into 2D based on a global quaternion (the user can rotate the entire object and zoom in/out, nothing else. The object stays in the centre of the window).

The properties of the current scene are stored in pScene, as thus:-

The quaternion is initialized at the beginning of the render operation using the members of pScene->aRotation as input:-Code:`typedef struct {`

unsigned short usWidth;

unsigned short usHeight;

t_angle3d aRotation; // roll, pitch, yaw (radians)

double dZoom; // z translation

double dQuaternion[4];

} t_scene;

Each vertex in the model is projected using the below function. If they are found to be projected in or behind the camera (z <= 0), they are discarded:-Code:`void EulerToQuat(double dRoll, double dPitch, double dYaw, double *pdQuaternion)`

{

double cr, cp, cy, sr, sp, sy, cpcy, spsy;

cr = cos(dRoll / 2);

cp = cos(dPitch / 2);

cy = cos(dYaw / 2);

sr = sin(dRoll / 2);

sp = sin(dPitch / 2);

sy = sin(dYaw / 2);

cpcy = cp * cy;

spsy = sp * sy;

pdQuaternion[0] = (cr * cpcy) + (sr * spsy);

pdQuaternion[1] = (sr * cpcy) - (cr * spsy);

pdQuaternion[2] = (cr * sp * cy) + (sr * cp * sy);

pdQuaternion[3] = (cr * cp * sy) - (sr * sp * cy);

}

This, er,Code:`int ProjectVertex(t_scene *pScene, t_vertex3d *pV, t_vertex2d *pVout)`

{

double dWsquared, dXsquared, dYsquared, dZsquared;

t_vertex3d vTemp;

// I should probably store these in pScene if I'm rotating everything the same way

dWsquared = pScene->dQuaternion[0] * pScene->dQuaternion[0];

dXsquared = pScene->dQuaternion[1] * pScene->dQuaternion[1];

dYsquared = pScene->dQuaternion[2] * pScene->dQuaternion[2];

dZsquared = pScene->dQuaternion[3] * pScene->dQuaternion[3];

vTemp.x = pV->x * (dWsquared + dXsquared - dYsquared - dZsquared) + (2 * ((pScene->dQuaternion[0] * pScene->dQuaternion[2] * pV->z) + (pScene->dQuaternion[1] * pScene->dQuaternion[3] * pV->z) + (pScene->dQuaternion[1] * pScene->dQuaternion[2] * pV->y) - (pScene->dQuaternion[0] * pScene->dQuaternion[3] * pV->y)));

vTemp.y = pV->y * (dWsquared - dXsquared + dYsquared - dZsquared) + (2 * ((pScene->dQuaternion[1] * pScene->dQuaternion[2] * pV->x) + (pScene->dQuaternion[0] * pScene->dQuaternion[3] * pV->x) + (pScene->dQuaternion[2] * pScene->dQuaternion[3] * pV->z) - (pScene->dQuaternion[1] * pScene->dQuaternion[0] * pV->z)));

vTemp.z = pV->z * (dWsquared - dXsquared - dYsquared + dZsquared) + (2 * ((pScene->dQuaternion[1] * pScene->dQuaternion[3] * pV->x) - (pScene->dQuaternion[0] * pScene->dQuaternion[2] * pV->x) + (pScene->dQuaternion[0] * pScene->dQuaternion[1] * pV->y) + (pScene->dQuaternion[3] * pScene->dQuaternion[2] * pV->y)));

vTemp.z -= pScene->dZoom;

if (vTemp.z <= 0)

return -1;

if (pVout)

{

// I'm not sure where "32" comes from, it just seems a reasonable scaling factor

// Examples I have seen use (40 / 10) but where does this come from?!?

pVout->x = ((vTemp.x / vTemp.z) * 32) + (pScene->usWidth / 2);

pVout->y = ((vTemp.y / vTemp.z) * -32) + (pScene->usHeight / 2);

}

return 0;

}

almostworks but doesn't. :(

There's something up with the projection. It seems to vary between horizontally squashed to stretched when rotated through 360 degrees pitch.

Can anyone help?