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?