0

So, I have make a 3d engine and I am trying to make the camera orbit around the player (in this case a cube), I have code for orbiting, 3D Projection, etc. but even with the correct matrixes the camera with stop displaying the cube from 1.57079 to -1.57079 radians (basically on the back) on both axis.

I have tried looking for more rotation matrixes but I cant find any different ones.

Note: To disable the camera "having eyes on the back of its head", Comment out line 140 of 3D.h

main.cpp

// main.cpp : This file contains the 'main' function. Program execution begins and ends there.

#include "Game.h"
#include <chrono>
#include <iostream>;

int main(int argc, char *argv[])
{
    using namespace std::chrono;
    uint64_t startingMs = duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count();
    uint64_t currentMs = startingMs;
    uint64_t prevFrameMs = currentMs;
    Game* game = new Game();
    game->init(1280, 720);
    int tick = 0;
    while (game->running) {
        currentMs = duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count();
        float deltaTime = (float)(currentMs - prevFrameMs) / 1000.0f;
        game->handleEvents(deltaTime);
        game->update();
        game->render();
        tick++;
        if ((currentMs - startingMs) >= 1000) {
            std::cout << tick-1 << " FPS\n";
            tick = 0;
            startingMs = currentMs;
        }
        prevFrameMs = currentMs;
    }

    game->clean();

    return 0;
}

Game.h

#pragma once
#include "SDL.h"
#include "3D.h"
#include <map>

class Game {
public:
    bool running;
    void init(int w, int h) {
        SDL_Init(SDL_INIT_EVERYTHING);
        window = SDL_CreateWindow("Quest of the Sword", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, w, h, SDL_WINDOW_SHOWN|SDL_WINDOW_RESIZABLE);
        width = w; height = h;
        renderer = SDL_CreateRenderer(window, -1, 0);
        running = true;
        camera.init(w, h, 90.0f, 0.1f, 1000.0f);
        erik.LoadFromObjectFile("cube.obj");
        camera.cameraPosition = { 0, 0, 3 };
        camera.cameraRotation = { 0, 180, 0 };
    };


    void update() {
        camera.CalculateOrbit(erik.position, orbitRotation, camera.cameraPosition, camera.cameraRotation);
    };


    void render() {
        SDL_SetRenderDrawColor(renderer, 255, 255, 255, SDL_ALPHA_OPAQUE);
        SDL_RenderClear(renderer);

        camera.render(width, height, erik, renderer);

        SDL_RenderPresent(renderer);
    };

    void handleEvents(float deltaTime) {
        SDL_Event event;
        SDL_PollEvent(&event);
        switch (event.type) {
        case SDL_QUIT:
            running = false;
            break;
        case SDL_WINDOWEVENT:
            if (event.window.event == SDL_WINDOWEVENT_RESIZED) {
                width = event.window.data1;
                height = event.window.data2;
                SDL_DestroyRenderer(renderer);
                SDL_SetWindowSize(window, width, height);
                renderer = SDL_CreateRenderer(window, -1, 0);
            }
            break;
        case SDL_KEYDOWN:
            keyboard[event.key.keysym.sym] = false;
            break;
        case SDL_KEYUP:
            keyboard[event.key.keysym.sym] = true;
            break;
        default:
            break;
        }

        if (keyboard[SDLK_w]) {
            camera.cameraPosition.z -= 2 * deltaTime;
        }
        if (keyboard[SDLK_s]) {
            camera.cameraPosition.z += 2 * deltaTime;
        }
        if (keyboard[SDLK_a]) {
            camera.cameraPosition.x += 2 * deltaTime;
        }
        if (keyboard[SDLK_d]) {
            camera.cameraPosition.x -= 2 * deltaTime;
        }

        if (keyboard[SDLK_UP]) {
            orbitRotation.y += 1 * deltaTime;
        }
        if (keyboard[SDLK_DOWN]) {
            orbitRotation.y -= 1 * deltaTime;
        }
        if (keyboard[SDLK_LEFT]) {
            orbitRotation.x += 1 * deltaTime;
        }
        if (keyboard[SDLK_RIGHT]) {
            orbitRotation.x -= 1 * deltaTime;
        }
        if (keyboard[SDLK_F11] and not fullscreen and not prevKeyboard[SDLK_F11]) {
            resBeforeFullscreen.x = width; resBeforeFullscreen.y = height;
            SDL_SetWindowFullscreen(window, SDL_WINDOW_FULLSCREEN_DESKTOP);
            fullscreen = not fullscreen;
        }
        else if (keyboard[SDLK_F11] and fullscreen and not prevKeyboard[SDLK_F11]) {
            SDL_DestroyRenderer(renderer);
            SDL_SetWindowSize(window, resBeforeFullscreen.x, resBeforeFullscreen.y);
            renderer = SDL_CreateRenderer(window, -1, 0);
            SDL_SetWindowFullscreen(window, 0);
            fullscreen = not fullscreen;
        }
        prevKeyboard = keyboard;
    }

    void clean() {
        SDL_DestroyWindow(window);
        SDL_DestroyRenderer(renderer);
        SDL_Quit();
    };

private:
    Camera camera;
    Object erik{};
    SDL_Window* window{};
    SDL_Renderer* renderer{};
    std::map<int, bool> keyboard;
    std::map<int, bool> prevKeyboard;
    uint64_t prevMs;
    Vector2 orbitLocation{};
    int width{}, height{};
    bool fullscreen = false;
    Vector2 resBeforeFullscreen;
    Vector2 orbitRotation;
};

3D.h

#pragma once
#include <iostream>
#include <fstream>
#include <string>
#include <sstream>
#include <strstream>
#include <vector>
#include <cmath>
#include <stdio.h>
#include "SDL.h"

struct Vector3
{
    float x, y, z;
};

struct Vector2
{
    float x, y;
};

struct Triangle
{
    Vector3 points[3];
};

struct TriangleOf3Ints
{
    int v1, v2, v3;
};

struct Object
{
    std::vector<Triangle> Triangles{};
    Vector3 position;
    bool LoadFromObjectFile(std::string sFilename)
    {
        std::ifstream f(sFilename);
        if (!f.is_open())
            return false;

        std::vector<Vector3> verts;

        while (!f.eof())
        {
            char line[128];
            f.getline(line, 128);

            std::strstream s;
            s << line;

            char junk;

            if (line[0] == 'v')
            {
                Vector3 v{};
                s >> junk >> v.x >> v.y >> v.z;
                verts.push_back(v);
            }

            if (line[0] == 'f')
            {
                int f[3]{};
                s >> junk >> f[0] >> f[1] >> f[2];
                Triangles.push_back({ verts[f[0] - (int)1], verts[f[1] - (int)1], verts[f[2] - (int)1] });
            }
        }

        return true;
    }
};

struct Matrix4x4
{
    float m[4][4] = {0};
};

class Camera
{
public:
    Matrix4x4 projectionMatrix;
    Matrix4x4 rotateXaxiMatrix;
    Matrix4x4 rotateYaxiMatrix;
    Vector3 cameraPosition{};
    Vector3 cameraRotation{};
    void init(int width, int height, float FOV, float nearPlane, float farPlane) {
        float fovRad = 1.0f / tanf(FOV * 0.5f / 180.0f * 3.14159f);
        projectionMatrix.m[0][0] = (float)height / width * fovRad;
        projectionMatrix.m[1][1] = fovRad;
        projectionMatrix.m[2][2] = farPlane / (farPlane - nearPlane);
        projectionMatrix.m[3][2] = (-farPlane * nearPlane) / (farPlane - nearPlane);
        projectionMatrix.m[2][3] = 1.0f;
        projectionMatrix.m[3][3] = 0.0f;
        rotateXaxiMatrix.m[0][0] = 1.0f;
        rotateXaxiMatrix.m[3][3] = 1.0f;
        rotateYaxiMatrix.m[1][1] = 1.0f;
        rotateYaxiMatrix.m[3][3] = 1.0f;
    }
    void CalculateOrbit(const Vector3& centerPosition, const Vector2 rotation, Vector3& objectPosition, Vector3& objectRotation)
    {
        float orbitSpeed = 0.01f;
        float radius = 3.0f;
        objectPosition.x = centerPosition.x + radius * std::sin(rotation.x) * std::cos(rotation.y);
        objectPosition.y = centerPosition.y + radius * std::sin(rotation.y);
        objectPosition.z = centerPosition.z + radius * std::cos(rotation.x) * std::cos(rotation.y);
        Vector3 directionToCenter = { centerPosition.x - objectPosition.x, centerPosition.y - objectPosition.y, centerPosition.z - objectPosition.z };
        objectRotation.x = std::atan2(directionToCenter.y, directionToCenter.z);
        objectRotation.y = std::atan2(directionToCenter.x, directionToCenter.z);
    }

    Triangle renderTriangle(int width, int height, Triangle tri, Vector3 positionOffset) {
        Triangle triProjected{}, triTranslated{}, triRotated0{}, triRotated{};
        triTranslated = tri;
        triTranslated.points[0].z = tri.points[0].z - cameraPosition.z + positionOffset.z;
        triTranslated.points[1].z = tri.points[1].z - cameraPosition.z + positionOffset.z;
        triTranslated.points[2].z = tri.points[2].z - cameraPosition.z + positionOffset.z;
        triTranslated.points[0].y = tri.points[0].y - cameraPosition.y + positionOffset.y;
        triTranslated.points[1].y = tri.points[1].y - cameraPosition.y + positionOffset.y;
        triTranslated.points[2].y = tri.points[2].y - cameraPosition.y + positionOffset.y;
        triTranslated.points[0].x = tri.points[0].x - cameraPosition.x + positionOffset.x;
        triTranslated.points[1].x = tri.points[1].x - cameraPosition.x + positionOffset.x;
        triTranslated.points[2].x = tri.points[2].x - cameraPosition.x + positionOffset.x;
        rotateXaxiMatrix.m[1][1] = cos(cameraRotation.x);
        rotateXaxiMatrix.m[2][1] = sin(cameraRotation.x);
        rotateXaxiMatrix.m[1][2] = -sin(cameraRotation.x);
        rotateXaxiMatrix.m[2][2] = cos(cameraRotation.x);
        rotateYaxiMatrix.m[0][0] = cos(cameraRotation.y);
        rotateYaxiMatrix.m[2][0] = -sin(cameraRotation.y);
        rotateYaxiMatrix.m[0][2] = sin(cameraRotation.y);
        rotateYaxiMatrix.m[2][2] = cos(cameraRotation.y);
        MultiplyMatrixVector(triTranslated.points[0], triRotated0.points[0], rotateYaxiMatrix);
        MultiplyMatrixVector(triTranslated.points[1], triRotated0.points[1], rotateYaxiMatrix);
        MultiplyMatrixVector(triTranslated.points[2], triRotated0.points[2], rotateYaxiMatrix);
        MultiplyMatrixVector(triRotated0.points[0], triRotated.points[0], rotateXaxiMatrix);
        MultiplyMatrixVector(triRotated0.points[1], triRotated.points[1], rotateXaxiMatrix);
        MultiplyMatrixVector(triRotated0.points[2], triRotated.points[2], rotateXaxiMatrix);
        if (triRotated.points[0].z > cameraPosition.z && triRotated.points[1].z > cameraPosition.z && triRotated.points[2].z > cameraPosition.z) {
            Triangle emptyTri;
            emptyTri.points[0].z = 0;
            return emptyTri;
        }
        MultiplyMatrixVector(triRotated.points[0], triProjected.points[0], projectionMatrix);
        MultiplyMatrixVector(triRotated.points[1], triProjected.points[1], projectionMatrix);
        MultiplyMatrixVector(triRotated.points[2], triProjected.points[2], projectionMatrix);
        triProjected.points[0].x += 1.0f;
        triProjected.points[0].y += 1.0f;
        triProjected.points[1].x += 1.0f;
        triProjected.points[1].y += 1.0f;
        triProjected.points[2].x += 1.0f;
        triProjected.points[2].y += 1.0f;
        triProjected.points[0].x *= 0.5f * (float)width;
        triProjected.points[0].y *= 0.5f * (float)height;
        triProjected.points[1].x *= 0.5f * (float)width;
        triProjected.points[1].y *= 0.5f * (float)height;
        triProjected.points[2].x *= 0.5f * (float)width;
        triProjected.points[2].y *= 0.5f * (float)height;
        return triProjected;
    }
    void render(int width, int height, Object obj, SDL_Renderer* renderer) {
        Triangle triangle{};
        for (const Triangle& tri : obj.Triangles) {
            triangle = renderTriangle(width, height, tri, obj.position);
            if (triangle.points[0].z > 0 && triangle.points[1].z > 0 && triangle.points[2].z > 0) {
                const std::vector< SDL_Vertex > verts =
                {
                    { SDL_FPoint{ triangle.points[0].x, triangle.points[0].y}, SDL_Color{255, 0, 0, 255}, SDL_FPoint{0},},
                    { SDL_FPoint{ triangle.points[1].x, triangle.points[1].y }, SDL_Color{ 0, 0, 255, 255 }, SDL_FPoint{ 0 }, },
                    { SDL_FPoint{ triangle.points[2].x, triangle.points[2].y }, SDL_Color{ 0, 255, 0, 255 }, SDL_FPoint{ 0 }, },
                };
                SDL_RenderGeometry(renderer, nullptr, verts.data(), verts.size(), nullptr, 0);
            }
        }
    }
    void MultiplyMatrixVector(Vector3& i, Vector3& o, Matrix4x4& m) {
        o.x = i.x * m.m[0][0] + i.y * m.m[1][0] + i.z * m.m[2][0] + m.m[3][0];
        o.y = i.x * m.m[0][1] + i.y * m.m[1][1] + i.z * m.m[2][1] + m.m[3][1];
        o.z = i.x * m.m[0][2] + i.y * m.m[1][2] + i.z * m.m[2][2] + m.m[3][2];
        float w = i.x * m.m[0][3] + i.y * m.m[1][3] + i.z * m.m[2][3] + m.m[3][3];

        if (w != 0.0f)
        {
            o.x /= w; o.y /= w; o.z /= w;
        }
    }
};
  • 5
    I recommend you to start over, and then write a *small* and *simple* piece of code, that you can easily test and debug. Then add another *small* and *simple* piece of code, that you again can easily test and debug. That will make is *much* easier with the debugging part, and maybe even possible to isolate the non-working code into a small and simple [mre]. – Some programmer dude Sep 01 '23 at 18:25
  • 4
    Otherwise, use your version control system to roll back commits until it works, then reapply the commits one by one until it stops working, and then try to isolate the last commit into a [mre] to debug. – Some programmer dude Sep 01 '23 at 18:26
  • 4
    And if none of the alternative above are possible or applicable (why oh why don't you use a VCS?) then remove code bit by bit as long as it doesn't work. When it starts working again then you add the code back, one *little* and *simple* piece at a time, until it again stops working. Then you create your [mre] and start debugging it. – Some programmer dude Sep 01 '23 at 18:27
  • 2
    Dont try to directly write your engine as an executable. As Some progammer dude says write small pieces of code in a static C++ library first. Then get a unit testing framework (like [googletest](http://google.github.io/googletest/)) and test those small pieces of code. Actually you should try to break your code by calling it with all kinds of values (including ones in corner cases) and if such a test fails you can debug it. – Pepijn Kramer Sep 01 '23 at 18:28
  • Sometimes you can't find stuff and just have to [knuckle down](https://dictionary.cambridge.org/dictionary/english/knuckle-down), [grok the math](https://en.wikipedia.org/wiki/Grok), and write the code. – user4581301 Sep 01 '23 at 18:29
  • 2
    Or TL;DR: Please learn how to [*debug*](https://ericlippert.com/2014/03/05/how-to-debug-small-programs/) your programs. And learn how to use a [*debugger*](https://stackoverflow.com/questions/25385173/what-is-a-debugger-and-how-can-it-help-me-diagnose-problems) to step through your code line by line while monitoring variables and their values. – Some programmer dude Sep 01 '23 at 18:29

0 Answers0