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