mirror of
https://github.com/Karaka-Management/cOMS.git
synced 2026-01-10 19:08:39 +00:00
553 lines
18 KiB
C
Executable File
553 lines
18 KiB
C
Executable File
/**
|
|
* Jingga
|
|
*
|
|
* @copyright Jingga
|
|
* @license OMS License 2.0
|
|
* @version 1.0.0
|
|
* @link https://jingga.app
|
|
*/
|
|
#ifndef COMS_CAMERA_H
|
|
#define COMS_CAMERA_H
|
|
|
|
#include "../stdlib/Types.h"
|
|
#include "../math/matrix/MatrixFloat32.h"
|
|
#include "../compiler/CompilerUtils.h"
|
|
#include "CameraMovement.h"
|
|
#include "../gpuapi/GpuApiType.h"
|
|
|
|
#define CAMERA_MAX_INPUTS 4
|
|
|
|
// @todo Please check out if we can switch to quaternions. We tried but failed.
|
|
|
|
/**
|
|
* Gpu API coordinate information
|
|
*
|
|
* Coord-Sys. NDC-X NDC-Y NDC-Z Clip-Space-Z Y-Axis
|
|
* DirectX left [-1, 1] [-1, 1] [0, 1] [0, 1] Up = positive
|
|
* Opengl right [-1, 1] [-1, 1] [-1, 1] [-1, 1] Up = positive
|
|
* Vulkan right [-1, 1] [-1, 1] [0, 1] [0, 1] Down = positive
|
|
* Metal right [-1, 1] [-1, 1] [0, 1] [0, 1] Up = positive
|
|
*
|
|
* The first value in Z always represents the near value and the second value the far value
|
|
*/
|
|
|
|
enum CameraStateChanges : byte {
|
|
CAMERA_STATE_CHANGE_NONE = 0,
|
|
CAMERA_STATE_CHANGE_NORMAL = 1,
|
|
CAMERA_STATE_CHANGE_WINDOW = 2,
|
|
};
|
|
|
|
struct Camera {
|
|
byte state_changes;
|
|
GpuApiType gpu_api_type;
|
|
|
|
v3_f32 location;
|
|
v4_f32 orientation;
|
|
|
|
v3_f32 front;
|
|
v3_f32 right;
|
|
v3_f32 up;
|
|
v3_f32 world_up;
|
|
|
|
f32 speed;
|
|
f32 sensitivity;
|
|
f32 zoom;
|
|
|
|
uint16 viewport_width;
|
|
uint16 viewport_height;
|
|
|
|
f32 fov;
|
|
f32 znear;
|
|
f32 zfar;
|
|
f32 aspect;
|
|
|
|
alignas(64) f32 view[16];
|
|
alignas(64) f32 projection[16];
|
|
alignas(64) f32 orth[16];
|
|
};
|
|
|
|
inline
|
|
void camera_init_rh_opengl(Camera* camera) {
|
|
camera->orientation = {0.0f, -90.0f, 0.0f, 1.0f};
|
|
camera->front = {0.0f, 0.0f, -1.0f};
|
|
camera->right = {1.0f, 0.0f, 0.0f};
|
|
camera->up = {0.0f, 1.0f, 0.0f};
|
|
camera->world_up = {0.0f, 1.0f, 0.0f};
|
|
}
|
|
|
|
inline
|
|
void camera_init_rh_vulkan(Camera* camera) {
|
|
camera->orientation = {0.0f, -90.0f, 0.0f, 1.0f};
|
|
camera->front = {0.0f, 0.0f, -1.0f};
|
|
camera->right = {1.0f, 0.0f, 0.0f};
|
|
camera->up = {0.0f, -1.0f, 0.0f};
|
|
camera->world_up = {0.0f, -1.0f, 0.0f};
|
|
}
|
|
|
|
inline
|
|
void camera_init_lh(Camera* camera) {
|
|
camera->orientation = {0.0f, 90.0f, 0.0f, 1.0f};
|
|
camera->front = {0.0f, 0.0f, 1.0f};
|
|
camera->right = {1.0f, 0.0f, 0.0f};
|
|
camera->up = {0.0f, 1.0f, 0.0f};
|
|
camera->world_up = {0.0f, 1.0f, 0.0f};
|
|
}
|
|
|
|
inline
|
|
void camera_init(Camera* camera) {
|
|
camera->znear = 0.1f;
|
|
camera->zfar = 10000.0f;
|
|
|
|
switch (camera->gpu_api_type) {
|
|
case GPU_API_TYPE_NONE: {
|
|
camera_init_rh_opengl(camera);
|
|
} break;
|
|
case GPU_API_TYPE_OPENGL: {
|
|
camera_init_rh_opengl(camera);
|
|
} break;
|
|
case GPU_API_TYPE_VULKAN: {
|
|
camera_init_rh_vulkan(camera);
|
|
} break;
|
|
case GPU_API_TYPE_DIRECTX: {
|
|
camera_init_lh(camera);
|
|
} break;
|
|
default:
|
|
UNREACHABLE();
|
|
}
|
|
}
|
|
|
|
static inline
|
|
void camera_update_vectors(Camera* camera) noexcept
|
|
{
|
|
f32 cos_ori_x = cosf(OMS_DEG2RAD(camera->orientation.x));
|
|
camera->front.x = cos_ori_x * cosf(OMS_DEG2RAD(camera->orientation.y));
|
|
camera->front.y = sinf(OMS_DEG2RAD(camera->orientation.x));
|
|
camera->front.z = cos_ori_x * sinf(OMS_DEG2RAD(camera->orientation.y));
|
|
|
|
vec3_cross(&camera->right, &camera->front, &camera->world_up);
|
|
vec3_cross(&camera->up, &camera->right, &camera->front);
|
|
|
|
// We checked if combining these 3 into a single SIMD function, but it was slower
|
|
vec3_normalize(&camera->right);
|
|
vec3_normalize(&camera->front);
|
|
vec3_normalize(&camera->up);
|
|
}
|
|
|
|
void camera_rotate(Camera* camera, int32 dx, int32 dy) noexcept
|
|
{
|
|
camera->state_changes |= CAMERA_STATE_CHANGE_NORMAL;
|
|
camera->orientation.x += dy * camera->sensitivity;
|
|
camera->orientation.y -= dx * camera->sensitivity;
|
|
|
|
if (camera->orientation.x > 89.0f) {
|
|
camera->orientation.x = 89.0f;
|
|
} else if (camera->orientation.x < -89.0f) {
|
|
camera->orientation.x = -89.0f;
|
|
}
|
|
|
|
if (camera->orientation.y > 360.0f) {
|
|
camera->orientation.y -= 360.0f;
|
|
} else if (camera->orientation.y < -360.0f) {
|
|
camera->orientation.y += 360.0f;
|
|
}
|
|
|
|
camera_update_vectors(camera);
|
|
}
|
|
|
|
// you can have up to 4 camera movement inputs at the same time
|
|
void camera_movement(
|
|
Camera* __restrict camera,
|
|
CameraMovement* __restrict movement,
|
|
f32 dt,
|
|
bool relative_to_world = true
|
|
) noexcept {
|
|
camera->state_changes |= CAMERA_STATE_CHANGE_NORMAL;
|
|
f32 velocity = camera->speed * dt;
|
|
|
|
if (relative_to_world) {
|
|
for (int32 i = 0; i < CAMERA_MAX_INPUTS; i++) {
|
|
switch(movement[i]) {
|
|
case CAMERA_MOVEMENT_FORWARD: {
|
|
camera->location.z += velocity;
|
|
} break;
|
|
case CAMERA_MOVEMENT_BACK: {
|
|
camera->location.z -= velocity;
|
|
} break;
|
|
case CAMERA_MOVEMENT_LEFT: {
|
|
camera->location.x -= velocity;
|
|
} break;
|
|
case CAMERA_MOVEMENT_RIGHT: {
|
|
camera->location.x += velocity;
|
|
} break;
|
|
case CAMERA_MOVEMENT_UP: {
|
|
camera->location.y += velocity;
|
|
} break;
|
|
case CAMERA_MOVEMENT_DOWN: {
|
|
camera->location.y -= velocity;
|
|
} break;
|
|
case CAMERA_MOVEMENT_PITCH_UP: {
|
|
camera->orientation.x += velocity;
|
|
} break;
|
|
case CAMERA_MOVEMENT_PITCH_DOWN: {
|
|
camera->orientation.x -= velocity;
|
|
} break;
|
|
case CAMERA_MOVEMENT_ROLL_LEFT: {
|
|
camera->orientation.z += velocity;
|
|
} break;
|
|
case CAMERA_MOVEMENT_ROLL_RIGHT: {
|
|
camera->orientation.z -= velocity;
|
|
} break;
|
|
case CAMERA_MOVEMENT_YAW_LEFT: {
|
|
camera->orientation.y += velocity;
|
|
} break;
|
|
case CAMERA_MOVEMENT_YAW_RIGHT: {
|
|
camera->orientation.y -= velocity;
|
|
} break;
|
|
case CAMERA_MOVEMENT_ZOOM_IN: {
|
|
camera->zoom += velocity;
|
|
} break;
|
|
case CAMERA_MOVEMENT_ZOOM_OUT: {
|
|
camera->zoom -= velocity;
|
|
} break;
|
|
default: {
|
|
UNREACHABLE();
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
v3_f32 forward = camera->front;
|
|
|
|
v3_f32 right;
|
|
vec3_cross(&right, &camera->world_up, &forward);
|
|
|
|
v3_f32 up;
|
|
vec3_cross(&up, &right, &forward);
|
|
|
|
vec3_normalize(&right);
|
|
vec3_normalize(&up);
|
|
|
|
for (int32 i = 0; i < CAMERA_MAX_INPUTS; i++) {
|
|
switch(movement[i]) {
|
|
case CAMERA_MOVEMENT_NONE: {
|
|
} break;
|
|
case CAMERA_MOVEMENT_FORWARD: {
|
|
camera->location.x += forward.x * velocity;
|
|
camera->location.y += forward.y * velocity;
|
|
camera->location.z += forward.z * velocity;
|
|
} break;
|
|
case CAMERA_MOVEMENT_BACK: {
|
|
camera->location.x -= forward.x * velocity;
|
|
camera->location.y -= forward.y * velocity;
|
|
camera->location.z -= forward.z * velocity;
|
|
} break;
|
|
case CAMERA_MOVEMENT_LEFT: {
|
|
camera->location.x -= right.x * velocity;
|
|
camera->location.y -= right.y * velocity;
|
|
camera->location.z -= right.z * velocity;
|
|
} break;
|
|
case CAMERA_MOVEMENT_RIGHT: {
|
|
camera->location.x += right.x * velocity;
|
|
camera->location.y += right.y * velocity;
|
|
camera->location.z += right.z * velocity;
|
|
} break;
|
|
case CAMERA_MOVEMENT_UP: {
|
|
camera->location.x += up.x * velocity;
|
|
camera->location.y += up.y * velocity;
|
|
camera->location.z += up.z * velocity;
|
|
} break;
|
|
case CAMERA_MOVEMENT_DOWN: {
|
|
camera->location.x -= up.x * velocity;
|
|
camera->location.y -= up.y * velocity;
|
|
camera->location.z -= up.z * velocity;
|
|
} break;
|
|
case CAMERA_MOVEMENT_PITCH_UP: {
|
|
camera->orientation.x += velocity;
|
|
} break;
|
|
case CAMERA_MOVEMENT_PITCH_DOWN: {
|
|
camera->orientation.x -= velocity;
|
|
} break;
|
|
case CAMERA_MOVEMENT_ROLL_LEFT: {
|
|
camera->orientation.z += velocity;
|
|
} break;
|
|
case CAMERA_MOVEMENT_ROLL_RIGHT: {
|
|
camera->orientation.z -= velocity;
|
|
} break;
|
|
case CAMERA_MOVEMENT_YAW_LEFT: {
|
|
camera->orientation.z += velocity;
|
|
} break;
|
|
case CAMERA_MOVEMENT_YAW_RIGHT: {
|
|
camera->orientation.z -= velocity;
|
|
} break;
|
|
case CAMERA_MOVEMENT_ZOOM_IN: {
|
|
camera->zoom += velocity;
|
|
} break;
|
|
case CAMERA_MOVEMENT_ZOOM_OUT: {
|
|
camera->zoom -= velocity;
|
|
} break;
|
|
default: {
|
|
UNREACHABLE();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
inline
|
|
void camera_orth_matrix_lh(Camera* __restrict camera) noexcept
|
|
{
|
|
//mat4_identity(camera->orth);
|
|
camera->orth[15] = 1.0f;
|
|
mat4_ortho_sparse_lh(
|
|
camera->orth,
|
|
0.0f, (f32) camera->viewport_width,
|
|
0.0f, (f32) camera->viewport_height,
|
|
camera->znear,
|
|
camera->zfar
|
|
);
|
|
}
|
|
|
|
inline
|
|
void camera_orth_matrix_rh_opengl(Camera* __restrict camera) noexcept
|
|
{
|
|
//mat4_identity(camera->orth);
|
|
camera->orth[15] = 1.0f;
|
|
mat4_ortho_sparse_rh_opengl(
|
|
camera->orth,
|
|
0.0f, (f32) camera->viewport_width,
|
|
0.0f, (f32) camera->viewport_height,
|
|
camera->znear,
|
|
camera->zfar
|
|
);
|
|
}
|
|
|
|
inline
|
|
void camera_orth_matrix_rh_vulkan(Camera* __restrict camera) noexcept
|
|
{
|
|
//mat4_identity(camera->orth);
|
|
camera->orth[15] = 1.0f;
|
|
mat4_ortho_sparse_rh_vulkan(
|
|
camera->orth,
|
|
0.0f, (f32) camera->viewport_width,
|
|
0.0f, (f32) camera->viewport_height,
|
|
camera->znear,
|
|
camera->zfar
|
|
);
|
|
}
|
|
|
|
inline
|
|
void camera_projection_matrix_lh(Camera* __restrict camera) noexcept
|
|
{
|
|
//mat4_identity(camera->projection);
|
|
camera->projection[15] = 1.0f;
|
|
mat4_perspective_sparse_lh(
|
|
camera->projection,
|
|
camera->fov,
|
|
camera->aspect,
|
|
camera->znear,
|
|
camera->zfar
|
|
);
|
|
}
|
|
|
|
inline
|
|
void camera_projection_matrix_rh_opengl(Camera* __restrict camera) noexcept
|
|
{
|
|
//mat4_identity(camera->projection);
|
|
camera->projection[15] = 1.0f;
|
|
mat4_perspective_sparse_rh(
|
|
camera->projection,
|
|
camera->fov,
|
|
camera->aspect,
|
|
camera->znear,
|
|
camera->zfar
|
|
);
|
|
}
|
|
|
|
inline
|
|
void camera_projection_matrix_rh_vulkan(Camera* __restrict camera) noexcept
|
|
{
|
|
//mat4_identity(camera->projection);
|
|
camera->projection[15] = 1.0f;
|
|
// @bug Fix
|
|
mat4_perspective_sparse_rh(
|
|
camera->projection,
|
|
camera->fov,
|
|
camera->aspect,
|
|
camera->znear,
|
|
camera->zfar
|
|
);
|
|
}
|
|
|
|
// This is usually not used, since it is included in the view matrix
|
|
// expects the identity matrix
|
|
inline
|
|
void camera_translation_matrix_sparse_rh(const Camera* __restrict camera, f32* translation) noexcept
|
|
{
|
|
translation[12] = camera->location.x;
|
|
translation[13] = camera->location.y;
|
|
translation[14] = camera->location.z;
|
|
}
|
|
|
|
inline
|
|
void camera_translation_matrix_sparse_lh(const Camera* __restrict camera, f32* translation) noexcept
|
|
{
|
|
translation[3] = camera->location.x;
|
|
translation[7] = camera->location.y;
|
|
translation[11] = camera->location.z;
|
|
}
|
|
|
|
void
|
|
camera_view_matrix_lh(Camera* __restrict camera) noexcept
|
|
{
|
|
v3_f32 zaxis = { camera->front.x, camera->front.y, camera->front.z };
|
|
|
|
v3_f32 xaxis;
|
|
vec3_cross(&xaxis, &camera->world_up, &zaxis);
|
|
vec3_normalize(&xaxis);
|
|
|
|
v3_f32 yaxis;
|
|
vec3_cross(&yaxis, &zaxis, &xaxis);
|
|
|
|
// We tested if it would make sense to create a vec3_dot_sse version for the 3 dot products
|
|
// The result was that it is not faster, only if we would do 4 dot products would we see an improvement
|
|
camera->view[0] = xaxis.x;
|
|
camera->view[1] = yaxis.x;
|
|
camera->view[2] = zaxis.x;
|
|
camera->view[3] = 0.0f;
|
|
camera->view[4] = xaxis.y;
|
|
camera->view[5] = yaxis.y;
|
|
camera->view[6] = zaxis.y;
|
|
camera->view[7] = 0.0f;
|
|
camera->view[8] = xaxis.z;
|
|
camera->view[9] = yaxis.z;
|
|
camera->view[10] = zaxis.z;
|
|
camera->view[11] = 0;
|
|
camera->view[12] = -vec3_dot(&xaxis, &camera->location);
|
|
camera->view[13] = -vec3_dot(&yaxis, &camera->location);
|
|
camera->view[14] = -vec3_dot(&zaxis, &camera->location);
|
|
camera->view[15] = 1.0f;
|
|
}
|
|
|
|
void
|
|
camera_view_matrix_rh_opengl(Camera* __restrict camera) noexcept
|
|
{
|
|
v3_f32 zaxis = { -camera->front.x, -camera->front.y, -camera->front.z };
|
|
|
|
v3_f32 xaxis;
|
|
vec3_cross(&xaxis, &zaxis, &camera->world_up);
|
|
vec3_normalize(&xaxis);
|
|
|
|
v3_f32 yaxis;
|
|
vec3_cross(&yaxis, &zaxis, &xaxis);
|
|
|
|
// We tested if it would make sense to create a vec3_dot_sse version for the 3 dot products
|
|
// The result was that it is not faster, only if we would do 4 dot products would we see an improvement
|
|
camera->view[0] = xaxis.x;
|
|
camera->view[1] = yaxis.x;
|
|
camera->view[2] = zaxis.x;
|
|
camera->view[3] = 0.0f;
|
|
camera->view[4] = xaxis.y;
|
|
camera->view[5] = yaxis.y;
|
|
camera->view[6] = zaxis.y;
|
|
camera->view[7] = 0.0f;
|
|
camera->view[8] = xaxis.z;
|
|
camera->view[9] = yaxis.z;
|
|
camera->view[10] = zaxis.z;
|
|
camera->view[11] = 0;
|
|
camera->view[12] = -vec3_dot(&xaxis, &camera->location);
|
|
camera->view[13] = -vec3_dot(&yaxis, &camera->location);
|
|
camera->view[14] = -vec3_dot(&zaxis, &camera->location);
|
|
camera->view[15] = 1.0f;
|
|
}
|
|
|
|
void
|
|
camera_view_matrix_rh_vulkan(Camera* __restrict camera) noexcept
|
|
{
|
|
v3_f32 zaxis = { -camera->front.x, -camera->front.y, -camera->front.z };
|
|
|
|
v3_f32 xaxis;
|
|
vec3_cross(&xaxis, &zaxis, &camera->world_up);
|
|
vec3_normalize(&xaxis);
|
|
|
|
v3_f32 yaxis;
|
|
vec3_cross(&yaxis, &zaxis, &xaxis);
|
|
|
|
// We tested if it would make sense to create a vec3_dot_sse version for the 3 dot products
|
|
// The result was that it is not faster, only if we would do 4 dot products would we see an improvement
|
|
camera->view[0] = xaxis.x;
|
|
camera->view[1] = yaxis.x;
|
|
camera->view[2] = zaxis.x;
|
|
camera->view[3] = 0.0f;
|
|
camera->view[4] = xaxis.y;
|
|
camera->view[5] = yaxis.y;
|
|
camera->view[6] = zaxis.y;
|
|
camera->view[7] = 0.0f;
|
|
camera->view[8] = xaxis.z;
|
|
camera->view[9] = yaxis.z;
|
|
camera->view[10] = zaxis.z;
|
|
camera->view[11] = 0;
|
|
camera->view[12] = -vec3_dot(&xaxis, &camera->location);
|
|
camera->view[13] = -vec3_dot(&yaxis, &camera->location);
|
|
camera->view[14] = -vec3_dot(&zaxis, &camera->location);
|
|
camera->view[15] = 1.0f;
|
|
}
|
|
|
|
inline
|
|
f32 camera_step_closer(GpuApiType type, f32 value) noexcept {
|
|
// WARNING: The value depends on the near and far plane.
|
|
// The reason for this is they will get smaller and smaller with increasing zfar values
|
|
// until the difference effectively becomes 0 -> vertices occupy the same zindex -> zfighting
|
|
// For safety reasons we calculate a rather generous offset.
|
|
// @performance Maybe it makes sense in the future to just pick a small CONST epsilon value
|
|
switch (type) {
|
|
case GPU_API_TYPE_NONE:
|
|
return value + (nextafterf(value, -INFINITY) - value) * 1000;
|
|
case GPU_API_TYPE_OPENGL:
|
|
return value + (nextafterf(value, -INFINITY) - value) * 1000;
|
|
case GPU_API_TYPE_VULKAN:
|
|
return value + (nextafterf(value, -INFINITY) - value) * 1000;
|
|
case GPU_API_TYPE_DIRECTX:
|
|
return value + (nextafterf(value, -INFINITY) - value) * 1000;
|
|
default:
|
|
UNREACHABLE();
|
|
}
|
|
}
|
|
|
|
inline
|
|
f32 camera_step_away(GpuApiType type, f32 value) noexcept {
|
|
// WARNING: The value depends on the near and far plane.
|
|
// The reason for this is they will get smaller and smaller with increasing zfar values
|
|
// until the difference effectively becomes 0 -> vertices occupy the same zindex -> zfighting
|
|
// For safety reasons we calculate a rather generous offset.
|
|
// @performance Maybe it makes sense in the future to just pick a small CONST epsilon value
|
|
switch (type) {
|
|
case GPU_API_TYPE_NONE:
|
|
return value + (nextafterf(value, INFINITY) - value) * 1000;
|
|
case GPU_API_TYPE_OPENGL:
|
|
return value + (nextafterf(value, INFINITY) - value) * 1000;
|
|
case GPU_API_TYPE_VULKAN:
|
|
return value + (nextafterf(value, INFINITY) - value) * 1000;
|
|
case GPU_API_TYPE_DIRECTX:
|
|
return value + (nextafterf(value, INFINITY) - value) * 1000;
|
|
default:
|
|
UNREACHABLE();
|
|
}
|
|
}
|
|
|
|
#if OPENGL
|
|
#define camera_projection_matrix(camera) camera_projection_matrix_rh_opengl((camera))
|
|
#define camera_orth_matrix(camera) camera_orth_matrix_rh_opengl((camera))
|
|
#define camera_view_matrix(camera) camera_view_matrix_rh_opengl((camera))
|
|
#define camera_translation_matrix_sparse(camera, translation) camera_translation_matrix_sparse_rh((camera), (translation))
|
|
#elif VULKAN
|
|
#define camera_projection_matrix(camera) camera_projection_matrix_rh_vulkan((camera))
|
|
#define camera_orth_matrix(camera) camera_orth_matrix_rh_vulkan((camera))
|
|
#define camera_view_matrix(camera) camera_view_matrix_rh_vulkan((camera))
|
|
#define camera_translation_matrix_sparse(camera, translation) camera_translation_matrix_sparse_rh((camera), (translation))
|
|
#elif DIRECTX
|
|
#define camera_projection_matrix(camera) camera_projection_matrix_lh((camera))
|
|
#define camera_orth_matrix(camera) camera_orth_matrix_lh((camera))
|
|
#define camera_view_matrix(camera) camera_view_matrix_lh((camera))
|
|
#define camera_translation_matrix_sparse(camera, translation) camera_translation_matrix_sparse_lh((camera), (translation))
|
|
#endif
|
|
|
|
#endif |