I am making a 3D Engine but even with the correct matrixes but it breaks when rotating

50 Views Asked by At

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;
        }
    }
};
0

There are 0 best solutions below