0
\$\begingroup\$

This is not in regard to a video game, but to a 3D model viewer someone else created and I am modifying.

I would like to implement first-person -style mouse and keyboard controls in the viewer, so that you can navigate through the model as if it were a house or building.

Right now, when you move the mouse up, down, left or right, the rotations are relative to the camera's coordinate axes. Which means over time you can end up with some really weird camera positions.

How do I change this to FPS-style rotations where (I think) you use latitude/longitude rotations, and the "up" direction is always perpendicular to the ground plane.

Is there a simple conversion formula I can use on the mouse input? Thanks!

(BTW, I've already implemented correct lateral camera translation. The only issue remaining is the camera rotation.)

[edit]

The article below describes my problem well, but does not provide a solution I can use since I am not using Unity.

I'm rotating an object on two axes, so why does it keep twisting around the third axis?

[edit]

Here is the function I am trying to edit. Most of it is not relevant, but you can browse through it to see what's going on. The only part I really want to change is the line with the comment "fix rotations here!!".

void LDrawModelViewer::updateCameraPosition(void)
{
    float multiplier = 100.0f;
    float factor = 1.0f / multiplier;
    TCFloat motionAmount = 20.0f * (TCFloat)sqrt(fov / 45.0f);
    /*
    if (modifierKeys & MKShift)
    {
        motionAmount *= 2.0f;
        strafeAmount *= 2.0f;
        rotationAmount *= 2.0f;
        rollAmount *= 2.0f;
    }
    */
#ifdef WIN32
    if (hrpcFrequency.QuadPart != 0)
    {
        LARGE_INTEGER newCount;

        QueryPerformanceCounter(&newCount);
        factor = (float)((newCount.QuadPart - hrpcFrameCount.QuadPart) /
            (double)hrpcFrequency.QuadPart);
    }
    else
    {
        factor = (GetTickCount() - frameTicks) / 1000.0f;
    }
#endif // WIN32
#ifdef _QT
    factor = qtime.elapsed() /1000.0f;
#endif
#ifdef COCOA
    if (FRAME_TIME != nil)
    {
        factor = (float)-[FRAME_TIME timeIntervalSinceNow];
    }
#endif // COCOA
    if (viewMode == VMFlyThrough && flyMode == FMUpSide)
    {
        TCVector upVector(0.0f, -1.0f, 0.0f);
        TCVector tempMotion(0.0f, 0.0f, 0.0f);
        TCFloat matrix[16];
        TCFloat inverseMatrix[16];
        TCVector::invertMatrix(camera.getFacing().getMatrix(), inverseMatrix);
        TCVector::multMatrix(inverseMatrix, rotationMatrix, matrix);
        TCVector tempVector = upVector.transformNormal(matrix);
        if (cameraMotion[2] > 0.0f)
        {
            tempMotion[1] += -tempVector[2];
            tempMotion[2] += tempVector[1];
        }
        else if (cameraMotion[2] < 0.0f)
        {
            tempMotion[1] += tempVector[2];
            tempMotion[2] += -tempVector[1];
        }
        if (cameraMotion[1] > 0.0f)
        {
            tempMotion[1] += (1 - tempVector[2]);
            tempMotion[2] += (1 - tempVector[1]);
        }
        else if (cameraMotion[1] < 0.0f)
        {
            tempMotion[1] += -(1 - tempVector[2]);
            tempMotion[2] += -(1 - tempVector[1]);
        }
        tempMotion[0] = cameraMotion[0];
        camera.move(tempMotion * motionAmount * factor * multiplier);
        // fix rotations here!!
        camera.rotate(TCVector(cameraXRotate, cameraYRotate, cameraZRotate) *
            factor * multiplier * 1.5f);
    }
    else
    {
        //camera.move(cameraMotion * size / 100.0f * factor * multiplier);
        camera.move(cameraMotion * motionAmount * factor * multiplier);
        camera.rotate(TCVector(cameraXRotate, cameraYRotate, cameraZRotate) *
            factor * multiplier * 1.5f);
    }
}

[edit]

On a hunch, I changed that line to this:

    // Form a view vector using total pitch & yaw as spherical coordinates.
    TCVector globeVector
    (
        cos(cameraXRotate) * sin(cameraYRotate),
        sin(cameraXRotate),
        cos(cameraXRotate) * cos(cameraYRotate)
    );
    camera.rotate(globeVector * factor * multiplier * 1.5f);

But this did not have the desired effect.

[edit]

Okay, I replaced the previous code with:

    TCFloat identity[16];
    memcpy(identity, TCVector::sm_identityMatrix, sizeof(TCVector::sm_identityMatrix));
    TCVector::calcRotationMatrix(cameraXRotate, cameraYRotate, identity);

But I get an error that TCVector::sm_identityMatrix is inaccessible.

Also, how do I feed the new matrix back to the camera? The camera.rotate method only takes a three component vector as input AFAIK. I was unable to locate any documentation on the camera object, and the code has few comments in it.

\$\endgroup\$

marked as duplicate by DMGregory, Community Oct 20 '17 at 6:05

This question has been asked before and already has an answer. If those answers do not fully address your question, please ask a new question.

  • \$\begingroup\$ Matrix/quaternion math isn't Unity specific. You just need to use your versions of the functions that are used in the answer to the post marked as a duplicate. \$\endgroup\$ – Tyyppi_77 Oct 20 '17 at 11:13
  • \$\begingroup\$ I am not using an external library and need to implement this from scratch. \$\endgroup\$ – posfan12 Oct 20 '17 at 11:22
  • 1
    \$\begingroup\$ What language are you using and why are you not using external libraries? If it's a 3d app, presumably there are already matrices and quaternions somewhere in there. \$\endgroup\$ – Alexandre Vaillancourt Oct 20 '17 at 11:25
  • \$\begingroup\$ So implement matrix & quaternion math then? There's no getting around those really if you're working in 3D. \$\endgroup\$ – Tyyppi_77 Oct 20 '17 at 11:32
  • 1
    \$\begingroup\$ Your TCVector type already includes the methods you need to solve this, if it's the one I found here. The main methods you need are the ones that create an identity matrix (so you have a clean starting point) and calcRotationMatrix which already does all the work of rotating a matrix in the latitude-longitude way described in the answer to the linked duplicate. \$\endgroup\$ – DMGregory Oct 20 '17 at 12:58
-1
\$\begingroup\$

You need a conversion matrix from world space to local space. So you can rotate the camera from the clients view (local space).

\$\endgroup\$
  • \$\begingroup\$ Don't you mean the opposite? I mean, I want to rotate along lat/long aligned with world space. \$\endgroup\$ – posfan12 Oct 20 '17 at 5:57
  • \$\begingroup\$ Never mind. You are, in fact, absolutely correct. \$\endgroup\$ – posfan12 Oct 20 '17 at 10:56
  • \$\begingroup\$ I can no longer upvote this answer. \$\endgroup\$ – posfan12 Oct 20 '17 at 11:23
  • \$\begingroup\$ Why you want to upvote it? \$\endgroup\$ – Yosh Synergi Oct 20 '17 at 11:25
  • \$\begingroup\$ I actually want to remove the downvote because you were correct. \$\endgroup\$ – posfan12 Oct 20 '17 at 11:58

Not the answer you're looking for? Browse other questions tagged or ask your own question.