Clean up and some optimising

This commit is contained in:
JackCarterSmith 2024-12-29 13:37:16 +01:00
parent bdf6228679
commit 5b5c04e368
Signed by: JackCarterSmith
GPG Key ID: 832E52F4E23F8F24
7 changed files with 123 additions and 57 deletions

View File

@ -5,8 +5,11 @@
#include "../World/DbgCube.hpp" #include "../World/DbgCube.hpp"
#include "../World/Tank.hpp" #include "../World/Tank.hpp"
#define SF_COLOR_4CHEX(h) sf::Color((uint32_t)h)
//#define DISABLE_AABB_CLIPPING //#define DISABLE_AABB_CLIPPING
//#define DISABLE_TRIANGLE_CLIPPING //#define DISABLE_TRIANGLE_CLIPPING
//#define DISABLE_WIREFRAME_MODE
// Rendering pipeline: // Rendering pipeline:
@ -58,7 +61,7 @@ Graphic3DRenderer::~Graphic3DRenderer() {}
void Graphic3DRenderer::SetRTSize(unsigned int w, unsigned int h) { void Graphic3DRenderer::SetRTSize(unsigned int w, unsigned int h) {
mRTSize.x = w; mRTSize.y = h; mRTSize.x = w; mRTSize.y = h;
mMainCamera->SetFrustrum(90.0f, mRTSize.x/mRTSize.y, 1.0f, 100.f); mMainCamera->SetFrustrum(75.0f, mRTSize.x/mRTSize.y, 1.0f, 100.f);
} }
void Graphic3DRenderer::UpdateCamera(CAMERA_MOVE type, const float value) { void Graphic3DRenderer::UpdateCamera(CAMERA_MOVE type, const float value) {
@ -107,13 +110,16 @@ void Graphic3DRenderer::Draw(sf::RenderTexture& context) {
M3D_BoundingFrustum camFrustrum(projMat, false); M3D_BoundingFrustum camFrustrum(projMat, false);
camFrustrum.Transform(camFrustrum, invViewMat); camFrustrum.Transform(camFrustrum, invViewMat);
const float sgRatio = ComputeSGRatio();
// -= Draw the sky =- // -= Draw the sky =-
// To avoid unfilled pixels on screen, the "sky-plane" will be rendered // To avoid unfilled pixels on screen, the "sky-plane" will be rendered
// all over the screen. // all over the screen.
// It's will be useless to use and compute a specific rectangle from the // It's will be useless to use and compute a specific rectangle from the
// size of the screen! // size of the screen!
// The sky have an infinite z-depth (any objects will be rendered over). // The sky have an infinite z-depth (any objects will be rendered over).
context.clear(sf::Color::Cyan); #ifdef DISABLE_WIREFRAME_MODE
context.clear(SF_COLOR_4CHEX(0x00B5E2FF));
#endif
// -= Draw the ground =- // -= Draw the ground =-
// A simple rectangle shape is used to draw the ground over the sky-plane. // A simple rectangle shape is used to draw the ground over the sky-plane.
@ -121,17 +127,26 @@ void Graphic3DRenderer::Draw(sf::RenderTexture& context) {
// Depending of the camera pitch, the ratio sky/ground on screen vary. // Depending of the camera pitch, the ratio sky/ground on screen vary.
// Like the sky, the ground have an infinite z-depth (any objects will // Like the sky, the ground have an infinite z-depth (any objects will
// be rendered over). // be rendered over).
const float sgRatio = ComputeSGRatio(); #ifdef DISABLE_WIREFRAME_MODE
sf::RectangleShape gndRect; sf::RectangleShape gndRect;
if (mMainCamera->GetPos3f().y >= 0) { if (mMainCamera->GetPos3f().y >= 0) {
gndRect.setSize(sf::Vector2f(mRTSize.x, mRTSize.y * sgRatio)); gndRect.setSize(sf::Vector2f(mRTSize.x, mRTSize.y * sgRatio));
gndRect.setPosition(sf::Vector2f(0, mRTSize.y * (1.f - sgRatio))); gndRect.setPosition(sf::Vector2f(0, mRTSize.y * (1.f - sgRatio) - 1));
} else { } else {
gndRect.setSize(sf::Vector2f(mRTSize.x, mRTSize.y * (1.f - sgRatio))); gndRect.setSize(sf::Vector2f(mRTSize.x, mRTSize.y * (1.f - sgRatio)));
gndRect.setPosition(sf::Vector2f(0, 0)); gndRect.setPosition(sf::Vector2f(0, 0));
} }
gndRect.setFillColor(sf::Color::Green); gndRect.setFillColor(SF_COLOR_4CHEX(0x009A17FF));
//gndRect.setFillColor(SF_COLOR_4CHEX(0xD5C2A5FF));
context.draw(gndRect, sRS); context.draw(gndRect, sRS);
#else
sf::Vertex gndLine[2];
gndLine[0].position = sf::Vector2f(0, mRTSize.y * (1.f - sgRatio));
gndLine[0].color = sf::Color::White;
gndLine[1].position = sf::Vector2f(mRTSize.x - 1, mRTSize.y * (1.f - sgRatio));
gndLine[1].color = sf::Color::White;
context.draw(gndLine, 2, sf::Lines);
#endif
// Process scene's objects // Process scene's objects
for (auto& obj : mRenderList) { for (auto& obj : mRenderList) {
@ -209,8 +224,11 @@ void Graphic3DRenderer::Draw(sf::RenderTexture& context) {
v_tri[1].position = sf::Vector2f(M3D_V4GetX(V2), M3D_V4GetY(V2)); v_tri[1].position = sf::Vector2f(M3D_V4GetX(V2), M3D_V4GetY(V2));
v_tri[2].position = sf::Vector2f(M3D_V4GetX(V3), M3D_V4GetY(V3)); v_tri[2].position = sf::Vector2f(M3D_V4GetX(V3), M3D_V4GetY(V3));
v_tri[3] = v_tri[0]; v_tri[3] = v_tri[0];
//context.draw(v_tri, 4, sf::LineStrip, sRS); #ifdef DISABLE_WIREFRAME_MODE
context.draw(v_tri, 3, sf::Triangles, sRS); context.draw(v_tri, 4, sf::Triangles, sRS);
#else
context.draw(v_tri, 4, sf::LineStrip, sRS);
#endif
} }
} }
} }
@ -233,21 +251,41 @@ void Graphic3DRenderer::UpdateInternalTestObjects() {
} }
// Compute the screen ratio between the ground and the sky (aka. Line of Horizon) // Compute the screen ratio between the ground and the sky (aka. Line of Horizon)
inline float Graphic3DRenderer::ComputeSGRatio() { float Graphic3DRenderer::ComputeSGRatio() {
// FoV angle for Y axis is recovered using frustrum FoV and apply RT screen ratio to it static double sgRatio = 0.5f;
const float fovYAngleDiv2 = M3D_Deg2Rad(mMainCamera->GetFoV()) * 0.5f; static float fovCos = 0.f;
// Get the camera pitch angle over camera FoV ratio static float fovSin = 0.f;
const float theta = M3D_ScalarASinEst(-mMainCamera->GetLook3f().y); static float thetaCos = 0.f;
// Get the camera altitude from the ground static float fovThetaSin = 0.f;
const float altitude = mMainCamera->GetPos3f().y; const bool isCamMoved = mMainCamera->IsCameraMoved();
const bool isFUpdated = mMainCamera->IsFrustrumUpdated();
if (isCamMoved || isFUpdated) {
mMainCamera->ResetUpdateFlags();
// Ground/Sky screen ratio calculation using "simple" trigonometric properties of the // FoV angle for Y axis is recovered using frustrum FoV and apply RT screen ratio to it
// pinhole (frustrum) camera model. const float fovYAngleDiv2 = M3D_Deg2Rad(mMainCamera->GetFoV()) * 0.5f;
// The triangle made by the ground plane intersection with the frustum. This intersection // Get the camera pitch angle over camera FoV ratio
// cross the far plane at some point. Instead of computing the coordinate of the point, we const float theta = M3D_ScalarASinEst(-mMainCamera->GetLook3f().y);
// directly use the far plane length to get the corresponding ratio for the screen. // Get the camera altitude from the ground
double sgRatio = -(altitude * M3D_ScalarCosEst(fovYAngleDiv2) - mMainCamera->GetFarZ() * M3D_ScalarSinEst(fovYAngleDiv2 + theta)) const float altitude = mMainCamera->GetPos3f().y;
/ (2.f * mMainCamera->GetFarZ() * M3D_ScalarSinEst(fovYAngleDiv2) * M3D_ScalarCosEst(theta));
fovThetaSin = M3D_ScalarSinEst(fovYAngleDiv2 + theta);
if (isCamMoved)
thetaCos = M3D_ScalarCosEst(theta);
if (isFUpdated) {
fovCos = M3D_ScalarCosEst(fovYAngleDiv2);
fovSin = M3D_ScalarSinEst(fovYAngleDiv2);
}
// Ground/Sky screen ratio calculation using "simple" trigonometric properties of the
// pinhole (frustrum) camera model.
// The triangle made by the ground plane intersection with the frustum. This intersection
// cross the far plane at some point. Instead of computing the coordinate of the point, we
// directly use the far plane length to get the corresponding ratio for the screen.
sgRatio = -(altitude * fovCos - mMainCamera->GetFarZ() * fovThetaSin)
/ (2.f * mMainCamera->GetFarZ() * fovSin * thetaCos);
}
// Clamp // Clamp
if (sgRatio > 1.f) if (sgRatio > 1.f)

View File

@ -11,6 +11,7 @@ void Camera::SetFrustrum(float fov, float r, float zn, float zf) {
this->zf = zf; this->zf = zf;
M3D_MATRIX pMat = M3D_TransformMatrixFrustrumFovLH(M3D_Deg2Rad(fov), r, zn, zf); M3D_MATRIX pMat = M3D_TransformMatrixFrustrumFovLH(M3D_Deg2Rad(fov), r, zn, zf);
M3D_V4StoreF4x4(&mProjMat, pMat); M3D_V4StoreF4x4(&mProjMat, pMat);
frustrumUpdated = true;
} }
void Camera::UpdateCamView() { void Camera::UpdateCamView() {
@ -19,6 +20,7 @@ void Camera::UpdateCamView() {
M3D_VECTOR U = M3D_V4LoadF3(&mUp); M3D_VECTOR U = M3D_V4LoadF3(&mUp);
M3D_V4StoreF4x4(&mViewMat, M3D_TransformMatrixCamLookToLH(P, L, U)); M3D_V4StoreF4x4(&mViewMat, M3D_TransformMatrixCamLookToLH(P, L, U));
camMoved = true;
} }
void Camera::LookAt(M3D_VECTOR pos, M3D_VECTOR target, M3D_VECTOR worldUp) { void Camera::LookAt(M3D_VECTOR pos, M3D_VECTOR target, M3D_VECTOR worldUp) {

View File

@ -15,6 +15,8 @@ public:
float GetFoV() const { return fov; } float GetFoV() const { return fov; }
float GetFarZ() const { return zf; } float GetFarZ() const { return zf; }
const bool IsFrustrumUpdated() const { return frustrumUpdated; }
const bool IsCameraMoved() const { return camMoved; }
M3D_VECTOR GetPos() const { return M3D_V4LoadF3(&mPos); } M3D_VECTOR GetPos() const { return M3D_V4LoadF3(&mPos); }
M3D_F3 GetPos3f() const { return mPos; } M3D_F3 GetPos3f() const { return mPos; }
M3D_VECTOR GetLook() const { return M3D_V4LoadF3(&mLook); } M3D_VECTOR GetLook() const { return M3D_V4LoadF3(&mLook); }
@ -39,10 +41,15 @@ public:
void Pitch(float angle); void Pitch(float angle);
void Yaw(float angle); void Yaw(float angle);
void ResetUpdateFlags() { frustrumUpdated = false; camMoved = false;} //TODO: need a proper way to manage this flags
private: private:
float fov; // It's the Y-FoV! float fov; // It's the Y-FoV!
float zf; float zf;
bool frustrumUpdated = true;
bool camMoved = true;
M3D_F4X4 mProjMat = M3D_MIdentity4x4(); M3D_F4X4 mProjMat = M3D_MIdentity4x4();
M3D_F4X4 mViewMat = M3D_MIdentity4x4(); M3D_F4X4 mViewMat = M3D_MIdentity4x4();

View File

@ -605,6 +605,7 @@ float M3D_ScalarASin(float Value) noexcept;
float M3D_ScalarASinEst(float Value) noexcept; float M3D_ScalarASinEst(float Value) noexcept;
float M3D_ScalarACos(float Value) noexcept; float M3D_ScalarACos(float Value) noexcept;
float M3D_ScalarACosEst(float Value) noexcept; float M3D_ScalarACosEst(float Value) noexcept;
void M3D_ScalarSinCos(float* pSin, float* pCos, float Value) noexcept;
// //
// Common values for vector/matrix manipulation // Common values for vector/matrix manipulation

View File

@ -164,3 +164,35 @@ inline float M3D_ScalarACosEst(float Value) noexcept {
// acos(x) = pi - acos(-x) when x < 0 // acos(x) = pi - acos(-x) when x < 0
return (nonnegative ? result : M3D_PI - result); return (nonnegative ? result : M3D_PI - result);
} }
inline void M3D_ScalarSinCos(float* pSin, float* pCos, float Value) noexcept {
// Map Value to y in [-pi,pi], x = 2*pi*quotient + remainder.
float quotient = M3D_1DIV2PI * Value;
if (Value >= 0.0f)
quotient = static_cast<float>(static_cast<int>(quotient + 0.5f));
else
quotient = static_cast<float>(static_cast<int>(quotient - 0.5f));
float y = Value - M3D_2PI * quotient;
// Map y to [-pi/2,pi/2] with sin(y) = sin(Value).
float sign;
if (y > M3D_PIDIV2) {
y = M3D_PI - y;
sign = -1.0f;
} else if (y < -M3D_PIDIV2) {
y = -M3D_PI - y;
sign = -1.0f;
} else {
sign = +1.0f;
}
float y2 = y * y;
// 11-degree minimax approximation
*pSin = (((((-2.3889859e-08f * y2 + 2.7525562e-06f) * y2 - 0.00019840874f) * y2 + 0.0083333310f) * y2 - 0.16666667f) * y2 + 1.0f) * y;
// 10-degree minimax approximation
float p = ((((-2.6051615e-07f * y2 + 2.4760495e-05f) * y2 - 0.0013888378f) * y2 + 0.041666638f) * y2 - 0.5f) * y2 + 1.0f;
*pCos = sign * p;
}

View File

@ -1,38 +1,6 @@
#pragma once #pragma once
inline void M3D_ScalarSinCos(float* pSin, float* pCos, float Value) noexcept {
// Map Value to y in [-pi,pi], x = 2*pi*quotient + remainder.
float quotient = M3D_1DIV2PI * Value;
if (Value >= 0.0f)
quotient = static_cast<float>(static_cast<int>(quotient + 0.5f));
else
quotient = static_cast<float>(static_cast<int>(quotient - 0.5f));
float y = Value - M3D_2PI * quotient;
// Map y to [-pi/2,pi/2] with sin(y) = sin(Value).
float sign;
if (y > M3D_PIDIV2) {
y = M3D_PI - y;
sign = -1.0f;
} else if (y < -M3D_PIDIV2) {
y = -M3D_PI - y;
sign = -1.0f;
} else {
sign = +1.0f;
}
float y2 = y * y;
// 11-degree minimax approximation
*pSin = (((((-2.3889859e-08f * y2 + 2.7525562e-06f) * y2 - 0.00019840874f) * y2 + 0.0083333310f) * y2 - 0.16666667f) * y2 + 1.0f) * y;
// 10-degree minimax approximation
float p = ((((-2.6051615e-07f * y2 + 2.4760495e-05f) * y2 - 0.0013888378f) * y2 + 0.041666638f) * y2 - 0.5f) * y2 + 1.0f;
*pCos = sign * p;
}
namespace M3D_Internal { namespace M3D_Internal {
#ifdef DISABLE_INTRINSICS #ifdef DISABLE_INTRINSICS
// Round to nearest (even) a.k.a. banker's rounding // Round to nearest (even) a.k.a. banker's rounding

View File

@ -8,13 +8,31 @@ It (should be) a "very dirty" 80s-style arcade game with tanks.
I like big-canon, ballistics, retro and simulation, and that's where the initial concept came from. I like big-canon, ballistics, retro and simulation, and that's where the initial concept came from.
The similarity with Battlezone (Atari, 1980) or 3D Tank Duel (ZX Spectrum, 1984) only became apparent to me later in the development. The similarity with Battlezone (Atari, 1980), 3D Tank Duel (ZX Spectrum, 1984) and other tank-like arcade
games, their influence on development is limited to the graphic reference. In fact, I was only able to discover
some of them during my research phase.
The game itself isn't very polished. Mainly focused on game engine design, state-of-the-art rendering, and CPU-accelerated computing techniques. The game itself isn't very polished. Mainly focused on game engine design, state-of-the-art rendering, and CPU-accelerated computing techniques.
This code has been written with experimentation and education in mind. It is subject to the MIT license. This code has been written with experimentation and education in mind. It is subject to the MIT license.
I've used some libs for backend "boiler plates" like: I've used some libs for backend boilerplate like:
- SFML (window management) - SFML (window management)
- tinyobjloader (OBJ file loading) - tinyobjloader (OBJ file loading)
- Tweaked DirectXMath (Heavy math duty) - Tweaked DirectXMath (Heavy math duty)
## Why ... ?
Why using SFML? I've really wanted to focus on the projection stuff and geometry constructs in first place.
SFML is a light library, giving a drawing window access and pretty easy to use, nothing more. I'll look on
a direct interface for window management (X11 or gdi32) later...
Why using DirectX for math? Yeah I know GLM exist, but it lacks of SIMD ready functions. And with a MIT license,
the question of the proprietary code issue was quickly answered!
## About
*Actually, 72 coffees have been used to produce this code.*
Feel free to ask question about the code: jackcartersmith@jcsmith.fr