Clean up and some optimising
This commit is contained in:
parent
bdf6228679
commit
5b5c04e368
@ -5,8 +5,11 @@
|
||||
#include "../World/DbgCube.hpp"
|
||||
#include "../World/Tank.hpp"
|
||||
|
||||
#define SF_COLOR_4CHEX(h) sf::Color((uint32_t)h)
|
||||
|
||||
//#define DISABLE_AABB_CLIPPING
|
||||
//#define DISABLE_TRIANGLE_CLIPPING
|
||||
//#define DISABLE_WIREFRAME_MODE
|
||||
|
||||
|
||||
// Rendering pipeline:
|
||||
@ -58,7 +61,7 @@ Graphic3DRenderer::~Graphic3DRenderer() {}
|
||||
|
||||
void Graphic3DRenderer::SetRTSize(unsigned int w, unsigned int 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) {
|
||||
@ -107,13 +110,16 @@ void Graphic3DRenderer::Draw(sf::RenderTexture& context) {
|
||||
M3D_BoundingFrustum camFrustrum(projMat, false);
|
||||
camFrustrum.Transform(camFrustrum, invViewMat);
|
||||
|
||||
const float sgRatio = ComputeSGRatio();
|
||||
// -= Draw the sky =-
|
||||
// To avoid unfilled pixels on screen, the "sky-plane" will be rendered
|
||||
// all over the screen.
|
||||
// It's will be useless to use and compute a specific rectangle from the
|
||||
// size of the screen!
|
||||
// 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 =-
|
||||
// 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.
|
||||
// Like the sky, the ground have an infinite z-depth (any objects will
|
||||
// be rendered over).
|
||||
const float sgRatio = ComputeSGRatio();
|
||||
#ifdef DISABLE_WIREFRAME_MODE
|
||||
sf::RectangleShape gndRect;
|
||||
if (mMainCamera->GetPos3f().y >= 0) {
|
||||
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 {
|
||||
gndRect.setSize(sf::Vector2f(mRTSize.x, mRTSize.y * (1.f - sgRatio)));
|
||||
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);
|
||||
#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
|
||||
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[2].position = sf::Vector2f(M3D_V4GetX(V3), M3D_V4GetY(V3));
|
||||
v_tri[3] = v_tri[0];
|
||||
//context.draw(v_tri, 4, sf::LineStrip, sRS);
|
||||
context.draw(v_tri, 3, sf::Triangles, sRS);
|
||||
#ifdef DISABLE_WIREFRAME_MODE
|
||||
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)
|
||||
inline float Graphic3DRenderer::ComputeSGRatio() {
|
||||
// FoV angle for Y axis is recovered using frustrum FoV and apply RT screen ratio to it
|
||||
const float fovYAngleDiv2 = M3D_Deg2Rad(mMainCamera->GetFoV()) * 0.5f;
|
||||
// Get the camera pitch angle over camera FoV ratio
|
||||
const float theta = M3D_ScalarASinEst(-mMainCamera->GetLook3f().y);
|
||||
// Get the camera altitude from the ground
|
||||
const float altitude = mMainCamera->GetPos3f().y;
|
||||
float Graphic3DRenderer::ComputeSGRatio() {
|
||||
static double sgRatio = 0.5f;
|
||||
static float fovCos = 0.f;
|
||||
static float fovSin = 0.f;
|
||||
static float thetaCos = 0.f;
|
||||
static float fovThetaSin = 0.f;
|
||||
const bool isCamMoved = mMainCamera->IsCameraMoved();
|
||||
const bool isFUpdated = mMainCamera->IsFrustrumUpdated();
|
||||
if (isCamMoved || isFUpdated) {
|
||||
mMainCamera->ResetUpdateFlags();
|
||||
|
||||
// FoV angle for Y axis is recovered using frustrum FoV and apply RT screen ratio to it
|
||||
const float fovYAngleDiv2 = M3D_Deg2Rad(mMainCamera->GetFoV()) * 0.5f;
|
||||
// Get the camera pitch angle over camera FoV ratio
|
||||
const float theta = M3D_ScalarASinEst(-mMainCamera->GetLook3f().y);
|
||||
// Get the camera altitude from the ground
|
||||
const float altitude = mMainCamera->GetPos3f().y;
|
||||
|
||||
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.
|
||||
double sgRatio = -(altitude * M3D_ScalarCosEst(fovYAngleDiv2) - mMainCamera->GetFarZ() * M3D_ScalarSinEst(fovYAngleDiv2 + theta))
|
||||
/ (2.f * mMainCamera->GetFarZ() * M3D_ScalarSinEst(fovYAngleDiv2) * M3D_ScalarCosEst(theta));
|
||||
// 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
|
||||
if (sgRatio > 1.f)
|
||||
|
@ -11,6 +11,7 @@ void Camera::SetFrustrum(float fov, float r, float zn, float zf) {
|
||||
this->zf = zf;
|
||||
M3D_MATRIX pMat = M3D_TransformMatrixFrustrumFovLH(M3D_Deg2Rad(fov), r, zn, zf);
|
||||
M3D_V4StoreF4x4(&mProjMat, pMat);
|
||||
frustrumUpdated = true;
|
||||
}
|
||||
|
||||
void Camera::UpdateCamView() {
|
||||
@ -19,6 +20,7 @@ void Camera::UpdateCamView() {
|
||||
M3D_VECTOR U = M3D_V4LoadF3(&mUp);
|
||||
|
||||
M3D_V4StoreF4x4(&mViewMat, M3D_TransformMatrixCamLookToLH(P, L, U));
|
||||
camMoved = true;
|
||||
}
|
||||
|
||||
void Camera::LookAt(M3D_VECTOR pos, M3D_VECTOR target, M3D_VECTOR worldUp) {
|
||||
|
@ -15,6 +15,8 @@ public:
|
||||
|
||||
float GetFoV() const { return fov; }
|
||||
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_F3 GetPos3f() const { return mPos; }
|
||||
M3D_VECTOR GetLook() const { return M3D_V4LoadF3(&mLook); }
|
||||
@ -39,10 +41,15 @@ public:
|
||||
void Pitch(float angle);
|
||||
void Yaw(float angle);
|
||||
|
||||
void ResetUpdateFlags() { frustrumUpdated = false; camMoved = false;} //TODO: need a proper way to manage this flags
|
||||
|
||||
private:
|
||||
float fov; // It's the Y-FoV!
|
||||
float zf;
|
||||
|
||||
bool frustrumUpdated = true;
|
||||
bool camMoved = true;
|
||||
|
||||
M3D_F4X4 mProjMat = M3D_MIdentity4x4();
|
||||
M3D_F4X4 mViewMat = M3D_MIdentity4x4();
|
||||
|
||||
|
@ -605,6 +605,7 @@ float M3D_ScalarASin(float Value) noexcept;
|
||||
float M3D_ScalarASinEst(float Value) noexcept;
|
||||
float M3D_ScalarACos(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
|
||||
|
@ -163,4 +163,36 @@ inline float M3D_ScalarACosEst(float Value) noexcept {
|
||||
|
||||
// acos(x) = pi - acos(-x) when x < 0
|
||||
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;
|
||||
}
|
||||
|
@ -1,38 +1,6 @@
|
||||
#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 {
|
||||
#ifdef DISABLE_INTRINSICS
|
||||
// Round to nearest (even) a.k.a. banker's rounding
|
||||
|
24
README.md
24
README.md
@ -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.
|
||||
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.
|
||||
|
||||
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)
|
||||
- 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
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user