Compare commits

...

33 Commits

Author SHA1 Message Date
0d1cc2088f Merge pull request #155 from orange-cpp/feaute/examples-folders
retructurized examples
2026-02-20 08:55:20 +03:00
260b3b97f9 fix 2026-02-20 08:53:03 +03:00
e0904690e6 fixed formating 2026-02-20 08:52:26 +03:00
7021d9d365 fixed formating 2026-02-20 08:46:43 +03:00
862d52593a retructurized examples 2026-02-20 07:48:19 +03:00
97a3111791 fixed code style 2026-02-20 04:02:26 +03:00
a9bec00973 Merge pull request #154 from luadebug/example1
Example with opengl triangle
2026-02-20 03:37:58 +03:00
Saikari
66debb46fa Refactor to use Color class and Triangle structure for better clarity in drawing functions 2026-02-20 03:01:34 +03:00
Saikari
b8323d3bc0 simplify complexity 2026-02-20 02:56:27 +03:00
Saikari
f363fa6f1a Fix text 2026-02-20 02:53:20 +03:00
Saikari
0546272493 Use dot not quad 2026-02-20 02:50:55 +03:00
Saikari
58801bfab3 Add one more example 2026-02-20 02:43:27 +03:00
4567cfa318 updated read me 2026-02-19 12:03:19 +03:00
1db9340dbf updated read me 2026-02-19 08:42:58 +03:00
7b9a181b0e Merge pull request #153 from orange-cpp/feature/cryengine-support
Feature/cryengine support
2026-02-19 08:34:33 +03:00
0876af3c14 added warning back 2026-02-19 08:34:21 +03:00
e46a369ddc added more tests 2026-02-19 08:06:34 +03:00
630ffa69f6 clang format 2026-02-19 08:00:23 +03:00
609970cdfe fix 2026-02-19 07:57:04 +03:00
e935155022 improved tests 2026-02-19 07:47:23 +03:00
800082e4b3 added mesh 2026-02-19 06:35:08 +03:00
02d909f77d added mesh trait 2026-02-19 01:06:08 +03:00
49319a1c7c added more files 2026-02-19 01:05:09 +03:00
d0f3e5059a added formulas 2026-02-19 01:05:09 +03:00
c9874f30d0 add 2026-02-19 01:05:09 +03:00
3080faeaa9 hotfix: projectile prediction 2026-02-19 01:01:38 +03:00
af36c909a4 improved simple game 2026-02-18 07:38:04 +03:00
1041256fac Revert "removed useless include"
This reverts commit 88012e2524.
2026-02-18 06:07:27 +03:00
88012e2524 removed useless include 2026-02-18 05:53:16 +03:00
4561dbe0e7 fix 2026-02-18 05:51:46 +03:00
b50f759675 more template stuff 2026-02-18 05:51:18 +03:00
85b672fdf3 Merge pull request #152 from orange-cpp/feature/camera_improvement
added more methods
2026-02-17 07:57:35 +03:00
360734f252 added more methods 2026-02-17 07:39:45 +03:00
41 changed files with 1584 additions and 106 deletions

4
.idea/editor.xml generated
View File

@@ -17,7 +17,7 @@
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppBoostFormatTooManyArgs/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppCStyleCast/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppCVQualifierCanNotBeAppliedToReference/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppClassCanBeFinal/@EntryIndexedValue" value="DO_NOT_SHOW" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppClassCanBeFinal/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppClassIsIncomplete/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppClassNeedsConstructorBecauseOfUninitializedMember/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppClassNeverUsed/@EntryIndexedValue" value="WARNING" type="string" />
@@ -110,7 +110,7 @@
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppLambdaCaptureNeverUsed/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppLocalVariableMayBeConst/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppLocalVariableMightNotBeInitialized/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppLocalVariableWithNonTrivialDtorIsNeverUsed/@EntryIndexedValue" value="DO_NOT_SHOW" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppLocalVariableWithNonTrivialDtorIsNeverUsed/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppLongFloat/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppMemberFunctionMayBeConst/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppMemberFunctionMayBeStatic/@EntryIndexedValue" value="SUGGESTION" type="string" />

View File

@@ -130,11 +130,10 @@ set_target_properties(
CXX_STANDARD 23
CXX_STANDARD_REQUIRED ON)
if (OMATH_STATIC_MSVC_RUNTIME_LIBRARY)
set_target_properties(${PROJECT_NAME} PROPERTIES
MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>"
)
endif ()
if(OMATH_STATIC_MSVC_RUNTIME_LIBRARY)
set_target_properties(${PROJECT_NAME} PROPERTIES MSVC_RUNTIME_LIBRARY
"MultiThreaded$<$<CONFIG:Debug>:Debug>")
endif()
if(OMATH_USE_AVX2)
if(MSVC)

View File

@@ -80,9 +80,10 @@ if (auto screen = camera.world_to_screen(world_position)) {
- **Collision Detection**: Production ready code to handle collision detection by using simple interfaces.
- **No Additional Dependencies**: No additional dependencies need to use OMath except unit test execution
- **Ready for meta-programming**: Omath use templates for common types like Vectors, Matrixes etc, to handle all types!
- **Engine support**: Supports coordinate systems of **Source, Unity, Unreal, Frostbite, IWEngine and canonical OpenGL**.
- **Engine support**: Supports coordinate systems of **Source, Unity, Unreal, Frostbite, IWEngine, CryEngine and canonical OpenGL**.
- **Cross platform**: Supports Windows, MacOS and Linux.
- **Algorithms**: Has ability to scan for byte pattern with wildcards in PE files/modules, binary slices, works even with Wine apps.
- **Algorithms**: Has ability to scan for byte pattern with wildcards in ELF/Mach-O/PE files/modules, binary slices, works even with Wine apps.
- **Battle tested**: It's already used by some big players on the market like wraith.su and bluedream.ltd
<div align = center>
# Gallery

View File

@@ -1,35 +1,7 @@
project(examples)
add_executable(example_projection_matrix_builder example_proj_mat_builder.cpp)
set_target_properties(
example_projection_matrix_builder
PROPERTIES CXX_STANDARD 23
ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/out/${CMAKE_BUILD_TYPE}"
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/out/${CMAKE_BUILD_TYPE}"
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/out/${CMAKE_BUILD_TYPE}")
target_link_libraries(example_projection_matrix_builder PRIVATE omath::omath)
add_executable(example_signature_scan example_signature_scan.cpp)
set_target_properties(
example_signature_scan
PROPERTIES CXX_STANDARD 23
ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/out/${CMAKE_BUILD_TYPE}"
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/out/${CMAKE_BUILD_TYPE}"
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/out/${CMAKE_BUILD_TYPE}")
target_link_libraries(example_signature_scan PRIVATE omath::omath)
add_executable(example_glfw3 example_glfw3.cpp)
set_target_properties(
example_glfw3
PROPERTIES CXX_STANDARD 23
ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/out/${CMAKE_BUILD_TYPE}"
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/out/${CMAKE_BUILD_TYPE}"
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/out/${CMAKE_BUILD_TYPE}")
find_package(OpenGL)
find_package(GLEW REQUIRED)
find_package(glfw3 CONFIG REQUIRED)
target_link_libraries(example_glfw3 PRIVATE omath::omath GLEW::GLEW glfw OpenGL::GL)
add_subdirectory(example_barycentric)
add_subdirectory(example_glfw3)
add_subdirectory(example_proj_mat_builder)
add_subdirectory(example_signature_scan)
if(OMATH_ENABLE_VALGRIND)
omath_setup_valgrind(example_projection_matrix_builder)

View File

@@ -0,0 +1,14 @@
project(example_barycentric)
add_executable(${PROJECT_NAME} example_barycentric.cpp)
set_target_properties(
${PROJECT_NAME}
PROPERTIES CXX_STANDARD 23
ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/out/${CMAKE_BUILD_TYPE}"
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/out/${CMAKE_BUILD_TYPE}"
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/out/${CMAKE_BUILD_TYPE}")
find_package(OpenGL)
find_package(GLEW REQUIRED)
find_package(glfw3 CONFIG REQUIRED)
target_link_libraries(${PROJECT_NAME} PRIVATE omath::omath GLEW::GLEW glfw OpenGL::GL)

View File

@@ -0,0 +1,427 @@
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <cmath>
#include <iostream>
#include <omath/omath.hpp>
#include <vector>
using omath::Color;
using omath::Triangle;
using omath::Vector3;
static const char* vertexShaderSource = R"(
#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aColor;
layout (location = 2) in float aPointSize;
layout (location = 3) in float aIsLine;
out vec3 vColor;
out float vIsLine;
void main() {
gl_Position = vec4(aPos, 1.0);
vColor = aColor;
gl_PointSize = aPointSize;
vIsLine = aIsLine;
}
)";
static const char* fragmentShaderSource = R"(
#version 330 core
in vec3 vColor;
in float vIsLine;
out vec4 FragColor;
void main() {
if (vIsLine < 0.5) {
// Calculate distance from center of the point
vec2 coord = gl_PointCoord - vec2(0.5);
if(length(coord) > 0.5)
discard;
}
FragColor = vec4(vColor, 1.0);
}
)";
GLuint compileShader(GLenum type, const char* src)
{
GLuint shader = glCreateShader(type);
glShaderSource(shader, 1, &src, nullptr);
glCompileShader(shader);
GLint ok;
glGetShaderiv(shader, GL_COMPILE_STATUS, &ok);
if (!ok)
{
char log[1024];
glGetShaderInfoLog(shader, sizeof(log), nullptr, log);
std::cerr << "Shader error: " << log << std::endl;
}
return shader;
}
void drawChar(char c, float x, float y, float scale, const Color& color, std::vector<float>& lines)
{
float w = 0.5f * scale;
float h = 1.0f * scale;
auto add = [&](float x1, float y1, float x2, float y2)
{
lines.push_back(x + x1 * w);
lines.push_back(y + y1 * h);
lines.push_back(0.0f);
lines.push_back(color.x);
lines.push_back(color.y);
lines.push_back(color.z);
lines.push_back(1.0f); // size
lines.push_back(1.0f); // isLine
lines.push_back(x + x2 * w);
lines.push_back(y + y2 * h);
lines.push_back(0.0f);
lines.push_back(color.x);
lines.push_back(color.y);
lines.push_back(color.z);
lines.push_back(1.0f); // size
lines.push_back(1.0f); // isLine
};
switch (c)
{
case '0':
add(0, 0, 1, 0);
add(1, 0, 1, 1);
add(1, 1, 0, 1);
add(0, 1, 0, 0);
break;
case '1':
add(0.5f, 0, 0.5f, 1);
add(0.25f, 0.75f, 0.5f, 1);
add(0.25f, 0, 0.75f, 0);
break;
case '2':
add(0, 1, 1, 1);
add(1, 1, 1, 0.5f);
add(1, 0.5f, 0, 0.5f);
add(0, 0.5f, 0, 0);
add(0, 0, 1, 0);
break;
case '3':
add(0, 1, 1, 1);
add(1, 1, 1, 0);
add(1, 0, 0, 0);
add(0, 0.5f, 1, 0.5f);
break;
case '4':
add(0, 1, 0, 0.5f);
add(0, 0.5f, 1, 0.5f);
add(1, 1, 1, 0);
break;
case '5':
add(1, 1, 0, 1);
add(0, 1, 0, 0.5f);
add(0, 0.5f, 1, 0.5f);
add(1, 0.5f, 1, 0);
add(1, 0, 0, 0);
break;
case '6':
add(1, 1, 0, 1);
add(0, 1, 0, 0);
add(0, 0, 1, 0);
add(1, 0, 1, 0.5f);
add(1, 0.5f, 0, 0.5f);
break;
case '7':
add(0, 1, 1, 1);
add(1, 1, 0.5f, 0);
break;
case '8':
add(0, 0, 1, 0);
add(1, 0, 1, 1);
add(1, 1, 0, 1);
add(0, 1, 0, 0);
add(0, 0.5f, 1, 0.5f);
break;
case '9':
add(1, 0.5f, 0, 0.5f);
add(0, 0.5f, 0, 1);
add(0, 1, 1, 1);
add(1, 1, 1, 0);
add(1, 0, 0, 0);
break;
case '.':
add(0.4f, 0, 0.6f, 0);
add(0.6f, 0, 0.6f, 0.2f);
add(0.6f, 0.2f, 0.4f, 0.2f);
add(0.4f, 0.2f, 0.4f, 0);
break;
}
}
void drawText(const std::string& text, float x, float y, float scale, const Color& color, std::vector<float>& lines)
{
float cursor = x;
for (char c : text)
{
drawChar(c, cursor, y, scale, color, lines);
cursor += (c == '.' ? 0.3f : 0.7f) * scale;
}
}
GLFWwindow* initWindow(int width, int height, const char* title)
{
if (!glfwInit())
{
std::cerr << "Failed to initialize GLFW\n";
return nullptr;
}
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
GLFWwindow* window = glfwCreateWindow(width, height, title, NULL, NULL);
if (!window)
{
std::cerr << "Failed to create GLFW window\n";
glfwTerminate();
return nullptr;
}
glfwMakeContextCurrent(window);
// Check if context is valid using standard GL
const GLubyte* renderer = glGetString(GL_RENDERER);
const GLubyte* version = glGetString(GL_VERSION);
if (renderer && version)
{
std::cout << "Renderer: " << renderer << "\n";
std::cout << "OpenGL version supported: " << version << "\n";
}
else
{
std::cerr << "Failed to get GL_RENDERER or GL_VERSION. Context might be invalid.\n";
}
glewExperimental = GL_TRUE;
GLenum glewErr = glewInit();
if (glewErr != GLEW_OK)
{
// Ignore GLEW_ERROR_NO_GLX_DISPLAY if we have a valid context (e.g. Wayland)
if (glewErr == GLEW_ERROR_NO_GLX_DISPLAY && renderer)
{
std::cerr << "GLEW warning: " << glewGetErrorString(glewErr) << " (Ignored because context seems valid)\n";
}
else
{
std::cerr << "Failed to initialize GLEW: " << glewGetErrorString(glewErr) << "\n";
glfwTerminate();
return nullptr;
}
}
return window;
}
GLuint createShaderProgram()
{
GLuint vs = compileShader(GL_VERTEX_SHADER, vertexShaderSource);
GLuint fs = compileShader(GL_FRAGMENT_SHADER, fragmentShaderSource);
GLuint shaderProgram = glCreateProgram();
glAttachShader(shaderProgram, vs);
glAttachShader(shaderProgram, fs);
glLinkProgram(shaderProgram);
return shaderProgram;
}
void generatePointCloud(std::vector<float>& pointCloud, const Triangle<Vector3<float>>& triangle)
{
const auto& A = triangle.m_vertex1;
const auto& B = triangle.m_vertex2;
const auto& C = triangle.m_vertex3;
// Iterating over barycentric coordinates (u, v, w) from 0.0 to 1.0
for (float u = 0.0f; u <= 1.0f; u += 0.015f)
{
for (float v = 0.0f; v <= 1.0f - u; v += 0.015f)
{
float w = 1.0f - u - v;
if (w >= 0.0f && w <= 1.0f)
{
Vector3<float> P = A * u + B * v + C * w;
pointCloud.push_back(P.x);
pointCloud.push_back(P.y);
pointCloud.push_back(P.z);
pointCloud.push_back(u);
pointCloud.push_back(v);
pointCloud.push_back(w);
pointCloud.push_back(2.0f); // size
pointCloud.push_back(0.0f); // isLine
}
}
}
}
void setupBuffers(GLuint& VAO_cloud, GLuint& VBO_cloud, const std::vector<float>& pointCloud, GLuint& VAO_dyn,
GLuint& VBO_dyn)
{
glGenVertexArrays(1, &VAO_cloud);
glGenBuffers(1, &VBO_cloud);
glBindVertexArray(VAO_cloud);
glBindBuffer(GL_ARRAY_BUFFER, VBO_cloud);
glBufferData(GL_ARRAY_BUFFER, pointCloud.size() * sizeof(float), pointCloud.data(), GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(3 * sizeof(float)));
glEnableVertexAttribArray(1);
glVertexAttribPointer(2, 1, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(6 * sizeof(float)));
glEnableVertexAttribArray(2);
glVertexAttribPointer(3, 1, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(7 * sizeof(float)));
glEnableVertexAttribArray(3);
glGenVertexArrays(1, &VAO_dyn);
glGenBuffers(1, &VBO_dyn);
glBindVertexArray(VAO_dyn);
glBindBuffer(GL_ARRAY_BUFFER, VBO_dyn);
glBufferData(GL_ARRAY_BUFFER, 1000 * 8 * sizeof(float), NULL, GL_DYNAMIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(3 * sizeof(float)));
glEnableVertexAttribArray(1);
glVertexAttribPointer(2, 1, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(6 * sizeof(float)));
glEnableVertexAttribArray(2);
glVertexAttribPointer(3, 1, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(7 * sizeof(float)));
glEnableVertexAttribArray(3);
}
void updateDynamicData(std::vector<float>& dynData, float u, float v, float w, const Vector3<float>& P,
const Triangle<Vector3<float>>& triangle)
{
const auto& A = triangle.m_vertex1;
const auto& B = triangle.m_vertex2;
const auto& C = triangle.m_vertex3;
float sizeA = 10.0f + u * 30.0f;
float sizeB = 10.0f + v * 30.0f;
float sizeC = 10.0f + w * 30.0f;
float sizeP = 12.0f;
dynData = {// Lines from P to A, B, C
P.x, P.y, P.z, u, v, w, 1.0f, 1.0f, A.x, A.y, A.z, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f,
P.x, P.y, P.z, u, v, w, 1.0f, 1.0f, B.x, B.y, B.z, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f,
P.x, P.y, P.z, u, v, w, 1.0f, 1.0f, C.x, C.y, C.z, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f,
// The animated dot itself (White)
P.x, P.y, P.z, 1.0f, 1.0f, 1.0f, sizeP, 0.0f,
// The 3 corner dots
A.x, A.y, A.z, 1.0f, 0.0f, 0.0f, sizeA, 0.0f, B.x, B.y, B.z, 0.0f, 1.0f, 0.0f, sizeB, 0.0f, C.x, C.y,
C.z, 0.0f, 0.0f, 1.0f, sizeC, 0.0f};
char bufA[16], bufB[16], bufC[16];
snprintf(bufA, sizeof(bufA), "%.2f", u);
snprintf(bufB, sizeof(bufB), "%.2f", v);
snprintf(bufC, sizeof(bufC), "%.2f", w);
// Keep text at a fixed distance from the dots
float distA = 0.13f;
float distB = 0.13f;
float distC = 0.13f;
drawText(bufA, A.x - 0.05f, A.y + distA, 0.1f, Color(1, 0, 0, 1), dynData);
drawText(bufB, B.x - 0.15f - distB, B.y - 0.05f - distB, 0.1f, Color(0, 1, 0, 1), dynData);
drawText(bufC, C.x + 0.05f + distC, C.y - 0.05f - distC, 0.1f, Color(0, 0, 1, 1), dynData);
}
int main()
{
GLFWwindow* window = initWindow(800, 800, "Barycentric Coordinates");
if (!window)
return -1;
GLuint shaderProgram = createShaderProgram();
// Triangle vertices as shown in the picture (Red, Green, Blue)
// Scaled down slightly to leave room for text
Triangle<Vector3<float>> triangle(Vector3<float>(0.0f, 0.6f, 0.0f), // Red dot (top)
Vector3<float>(-0.6f, -0.6f, 0.0f), // Green dot (bottom left)
Vector3<float>(0.6f, -0.6f, 0.0f) // Blue dot (bottom right)
);
std::vector<float> pointCloud;
generatePointCloud(pointCloud, triangle);
GLuint VAO_cloud, VBO_cloud, VAO_dyn, VBO_dyn;
setupBuffers(VAO_cloud, VBO_cloud, pointCloud, VAO_dyn, VBO_dyn);
glEnable(GL_PROGRAM_POINT_SIZE);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
while (!glfwWindowShouldClose(window))
{
glClearColor(0.02f, 0.02f, 0.02f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
glUseProgram(shaderProgram);
// Draw the point cloud (the iterated points)
glBindVertexArray(VAO_cloud);
glDrawArrays(GL_POINTS, 0, pointCloud.size() / 8);
// Animate the white dot to simulate dragging
float t = glfwGetTime();
float u = (std::sin(t * 1.5f) * 0.5f + 0.5f);
float v = (std::cos(t * 1.1f) * 0.5f + 0.5f);
if (u + v > 1.0f)
{
u = 1.0f - u;
v = 1.0f - v;
}
float w = 1.0f - u - v;
if (w > 1.0f)
{
float diff = w - 1.0f;
w = 1.0f;
u += diff / 2.0f;
v += diff / 2.0f;
}
else if (w < 0.0f)
{
float diff = -w;
w = 0.0f;
u -= diff / 2.0f;
v -= diff / 2.0f;
}
Vector3<float> P = triangle.m_vertex1 * u + triangle.m_vertex2 * v + triangle.m_vertex3 * w;
std::vector<float> dynData;
updateDynamicData(dynData, u, v, w, P, triangle);
glBindVertexArray(VAO_dyn);
glBindBuffer(GL_ARRAY_BUFFER, VBO_dyn);
glBufferSubData(GL_ARRAY_BUFFER, 0, dynData.size() * sizeof(float), dynData.data());
// Draw lines
glDrawArrays(GL_LINES, 0, 6);
// Draw text lines
int numTextVertices = (dynData.size() / 8) - 10;
if (numTextVertices > 0)
{
glDrawArrays(GL_LINES, 10, numTextVertices);
}
// Draw dots
glDrawArrays(GL_POINTS, 6, 4);
glfwSwapBuffers(window);
glfwPollEvents();
}
glfwTerminate();
return 0;
}

View File

@@ -0,0 +1,14 @@
project(example_glfw3)
add_executable(${PROJECT_NAME} example_glfw3.cpp)
set_target_properties(
${PROJECT_NAME}
PROPERTIES CXX_STANDARD 23
ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/out/${CMAKE_BUILD_TYPE}"
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/out/${CMAKE_BUILD_TYPE}"
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/out/${CMAKE_BUILD_TYPE}")
find_package(OpenGL)
find_package(GLEW REQUIRED)
find_package(glfw3 CONFIG REQUIRED)
target_link_libraries(${PROJECT_NAME} PRIVATE omath::omath GLEW::GLEW glfw OpenGL::GL)

View File

@@ -149,10 +149,13 @@ int main()
// Check if context is valid using standard GL
const GLubyte* renderer = glGetString(GL_RENDERER);
const GLubyte* version = glGetString(GL_VERSION);
if (renderer && version) {
if (renderer && version)
{
std::cout << "Renderer: " << renderer << "\n";
std::cout << "OpenGL version supported: " << version << "\n";
} else {
}
else
{
std::cerr << "Failed to get GL_RENDERER or GL_VERSION. Context might be invalid.\n";
}
@@ -162,11 +165,14 @@ int main()
if (glewErr != GLEW_OK)
{
// Ignore NO_GLX_DISPLAY if we have a valid context
if (glewErr == GLEW_ERROR_NO_GLX_DISPLAY && renderer) {
std::cerr << "GLEW warning: " << glewGetErrorString(glewErr) << " (Ignored because context seems valid)\n";
} else {
if (glewErr == GLEW_ERROR_NO_GLX_DISPLAY && renderer)
{
std::cerr << "GLEW warning: " << glewGetErrorString(glewErr) << " (Ignored because context seems valid)\n";
}
else
{
std::cerr << "Failed to initialize GLEW: " << reinterpret_cast<const char*>(glewGetErrorString(glewErr))
<< "\n";
<< "\n";
glfwTerminate();
return -1;
}
@@ -300,11 +306,65 @@ int main()
static float old_frame_time = glfwGetTime();
float lastX = 640.0f / 2.0f;
float lastY = 480.0f / 2.0f;
bool firstMouse = true;
bool mouse_capture = false;
float mouseSensitivity = 0.1f;
// ---------- Main loop ----------
static double old_mouse_time = glfwGetTime();
while (!glfwWindowShouldClose(window))
{
glfwPollEvents();
omath::Vector3<float> move_dir;
if (glfwGetKey(window, GLFW_KEY_W))
move_dir += camera.get_forward();
if (glfwGetKey(window, GLFW_KEY_A))
move_dir -= camera.get_right();
if (glfwGetKey(window, GLFW_KEY_S))
move_dir -= camera.get_forward();
if (glfwGetKey(window, GLFW_KEY_D))
move_dir += camera.get_right();
if (glfwGetKey(window, GLFW_KEY_SPACE))
move_dir += camera.get_up();
if (glfwGetKey(window, GLFW_KEY_LEFT_CONTROL))
move_dir -= camera.get_up();
auto delta = glfwGetTime() - old_mouse_time;
if (glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_LEFT) && delta > 0.4)
{
old_mouse_time = glfwGetTime();
mouse_capture = !mouse_capture;
glfwSetInputMode(window, GLFW_CURSOR, mouse_capture ? GLFW_CURSOR_HIDDEN : GLFW_CURSOR_NORMAL);
}
if (mouse_capture)
{
int x, y;
glfwGetWindowSize(window, &x, &y);
camera.set_origin(camera.get_origin() + (move_dir.normalized() * 0.4f));
double xpos, ypos;
glfwGetCursorPos(window, &xpos, &ypos);
float xoffset = (float)xpos - x / 2.f;
float yoffset = y / 2.f - ypos ; // reversed: y-coordinates go bottom->top for pitch
xoffset *= mouseSensitivity;
yoffset *= mouseSensitivity;
auto new_angles = camera.get_view_angles();
new_angles.pitch += decltype(new_angles.pitch)::from_degrees(yoffset);
new_angles.yaw -= decltype(new_angles.yaw)::from_degrees(xoffset);
camera.set_view_angles(new_angles);
glfwSetCursorPos(window, x / 2., y / 2);
}
float currentTime = glfwGetTime();
float deltaTime = currentTime - old_frame_time;
old_frame_time = currentTime;

View File

@@ -0,0 +1,10 @@
project(example_projection_matrix_builder)
add_executable(${PROJECT_NAME} example_proj_mat_builder.cpp)
set_target_properties(
${PROJECT_NAME}
PROPERTIES CXX_STANDARD 23
ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/out/${CMAKE_BUILD_TYPE}"
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/out/${CMAKE_BUILD_TYPE}"
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/out/${CMAKE_BUILD_TYPE}")
target_link_libraries(${PROJECT_NAME} PRIVATE omath::omath)

View File

@@ -0,0 +1,10 @@
project(example_signature_scan)
add_executable(${PROJECT_NAME} example_signature_scan.cpp)
set_target_properties(
${PROJECT_NAME}
PROPERTIES CXX_STANDARD 23
ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/out/${CMAKE_BUILD_TYPE}"
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/out/${CMAKE_BUILD_TYPE}"
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/out/${CMAKE_BUILD_TYPE}")
target_link_libraries(${PROJECT_NAME} PRIVATE omath::omath)

View File

@@ -13,11 +13,11 @@
namespace omath::collision
{
template<class V>
concept EpaVector = requires(const V& a, const V& b, float s) {
template<class V, class FloatingType>
concept EpaVector = requires(const V& a, const V& b, FloatingType s) {
{ a - b } -> std::same_as<V>;
{ a.cross(b) } -> std::same_as<V>;
{ a.dot(b) } -> std::same_as<float>;
{ a.dot(b) } -> std::same_as<FloatingType>;
{ -a } -> std::same_as<V>;
{ a * s } -> std::same_as<V>;
{ a / s } -> std::same_as<V>;
@@ -28,13 +28,18 @@ namespace omath::collision
{
public:
using VectorType = ColliderInterfaceType::VectorType;
static_assert(EpaVector<VectorType>, "VertexType must satisfy EpaVector concept");
static_assert(EpaVector<VectorType, typename VectorType::ContainedType>,
"VertexType must satisfy EpaVector concept");
private:
using FloatingType = VectorType::ContainedType;
public:
struct Result final
{
VectorType normal{}; // from A to B
VectorType penetration_vector;
float depth{0.0f};
FloatingType depth{0.0};
int iterations{0};
int num_vertices{0};
int num_faces{0};
@@ -43,7 +48,7 @@ namespace omath::collision
struct Params final
{
int max_iterations{64};
float tolerance{1e-4f}; // absolute tolerance on distance growth
FloatingType tolerance{1e-4}; // absolute tolerance on distance growth
};
// Precondition: simplex.size()==4 and contains the origin.
[[nodiscard]]
@@ -75,13 +80,13 @@ namespace omath::collision
if (heap.empty())
break;
//FIXME: STORE REF VALUE, DO NOT USE
// AFTER IF STATEMENT BLOCK
// FIXME: STORE REF VALUE, DO NOT USE
// AFTER IF STATEMENT BLOCK
const Face& face = faces[heap.top().idx];
// Get the furthest point in face normal direction
const VectorType p = support_point(a, b, face.n);
const float p_dist = face.n.dot(p);
const auto p_dist = face.n.dot(p);
// Converged if we cant push the face closer than tolerance
if (p_dist - face.d <= params.tolerance)
@@ -136,7 +141,7 @@ namespace omath::collision
{
int i0, i1, i2;
VectorType n; // unit outward normal
float d; // n · v0 (>=0 ideally because origin is inside)
FloatingType d; // n · v0 (>=0 ideally because origin is inside)
};
struct Edge final
@@ -146,7 +151,7 @@ namespace omath::collision
struct HeapItem final
{
float d;
FloatingType d;
int idx;
};
struct HeapCmp final
@@ -178,7 +183,7 @@ namespace omath::collision
static bool visible_from(const Face& f, const VectorType& p)
{
// positive if p is in front of the face
return f.n.dot(p) - f.d > 1e-7f;
return f.n.dot(p) - f.d > static_cast<FloatingType>(1e-7);
}
static void add_edge_boundary(std::pmr::vector<Edge>& boundary, int a, int b)
@@ -198,19 +203,20 @@ namespace omath::collision
const VectorType& a1 = vertexes[i1];
const VectorType& a2 = vertexes[i2];
VectorType n = (a1 - a0).cross(a2 - a0);
if (n.dot(n) <= 1e-30f)
if (n.dot(n) <= static_cast<FloatingType>(1e-30))
{
n = any_perp_vec(a1 - a0); // degenerate guard
}
// Ensure normal points outward (away from origin): require n·a0 >= 0
if (n.dot(a0) < 0.0f)
if (n.dot(a0) < static_cast<FloatingType>(0.0))
{
std::swap(i1, i2);
n = -n;
}
const float inv_len = 1.0f / std::sqrt(std::max(n.dot(n), 1e-30f));
const auto inv_len =
static_cast<FloatingType>(1.0) / std::sqrt(std::max(n.dot(n), static_cast<FloatingType>(1e-30)));
n = n * inv_len;
const float d = n.dot(a0);
const auto d = n.dot(a0);
return {i0, i1, i2, n, d};
}
@@ -223,7 +229,7 @@ namespace omath::collision
template<class V>
[[nodiscard]]
static constexpr bool near_zero_vec(const V& v, const float eps = 1e-7f)
static constexpr bool near_zero_vec(const V& v, const FloatingType eps = 1e-7f)
{
return v.dot(v) <= eps * eps;
}

View File

@@ -0,0 +1,13 @@
//
// Created by Vlad on 3/22/2025.
//
#pragma once
#include "omath/engines/cry_engine/constants.hpp"
#include "omath/projection/camera.hpp"
#include "traits/camera_trait.hpp"
namespace omath::cry_engine
{
using Camera = projection::Camera<Mat4X4, ViewAngles, CameraTrait>;
} // namespace omath::cry_engine

View File

@@ -0,0 +1,25 @@
//
// Created by Vlad on 10/21/2025.
//
#pragma once
#include "omath/linear_algebra/mat.hpp"
#include "omath/linear_algebra/vector3.hpp"
#include <omath/trigonometry/angle.hpp>
#include <omath/trigonometry/view_angles.hpp>
namespace omath::cry_engine
{
constexpr Vector3<float> k_abs_up = {0, 0, 1};
constexpr Vector3<float> k_abs_right = {1, 0, 0};
constexpr Vector3<float> k_abs_forward = {0, 1, 0};
using Mat4X4 = Mat<4, 4, float, MatStoreType::ROW_MAJOR>;
using Mat3X3 = Mat<4, 4, float, MatStoreType::ROW_MAJOR>;
using Mat1X3 = Mat<1, 3, float, MatStoreType::ROW_MAJOR>;
using PitchAngle = Angle<float, -90.f, 90.f, AngleFlags::Clamped>;
using YawAngle = Angle<float, -180.f, 180.f, AngleFlags::Normalized>;
using RollAngle = Angle<float, -180.f, 180.f, AngleFlags::Normalized>;
using ViewAngles = omath::ViewAngles<PitchAngle, YawAngle, RollAngle>;
} // namespace omath::cry_engine

View File

@@ -0,0 +1,74 @@
//
// Created by Vlad on 3/22/2025.
//
#pragma once
#include "omath/engines/cry_engine/constants.hpp"
namespace omath::cry_engine
{
[[nodiscard]]
Vector3<float> forward_vector(const ViewAngles& angles) noexcept;
[[nodiscard]]
Vector3<float> right_vector(const ViewAngles& angles) noexcept;
[[nodiscard]]
Vector3<float> up_vector(const ViewAngles& angles) noexcept;
[[nodiscard]] Mat4X4 calc_view_matrix(const ViewAngles& angles, const Vector3<float>& cam_origin) noexcept;
[[nodiscard]]
Mat4X4 rotation_matrix(const ViewAngles& angles) noexcept;
[[nodiscard]]
Mat4X4 calc_perspective_projection_matrix(float field_of_view, float aspect_ratio, float near, float far) noexcept;
template<class FloatingType>
requires std::is_floating_point_v<FloatingType>
[[nodiscard]]
constexpr FloatingType units_to_centimeters(const FloatingType& units)
{
return units / static_cast<FloatingType>(100);
}
template<class FloatingType>
requires std::is_floating_point_v<FloatingType>
[[nodiscard]]
constexpr FloatingType units_to_meters(const FloatingType& units)
{
return units;
}
template<class FloatingType>
requires std::is_floating_point_v<FloatingType>
[[nodiscard]]
constexpr FloatingType units_to_kilometers(const FloatingType& units)
{
return units_to_meters(units) / static_cast<FloatingType>(1000);
}
template<class FloatingType>
requires std::is_floating_point_v<FloatingType>
[[nodiscard]]
constexpr FloatingType centimeters_to_units(const FloatingType& centimeters)
{
return centimeters * static_cast<FloatingType>(100);
}
template<class FloatingType>
requires std::is_floating_point_v<FloatingType>
[[nodiscard]]
constexpr FloatingType meters_to_units(const FloatingType& meters)
{
return meters;
}
template<class FloatingType>
requires std::is_floating_point_v<FloatingType>
[[nodiscard]]
constexpr FloatingType kilometers_to_units(const FloatingType& kilometers)
{
return meters_to_units(kilometers * static_cast<FloatingType>(1000));
}
} // namespace omath::cry_engine

View File

@@ -0,0 +1,12 @@
//
// Created by Vladislav on 09.11.2025.
//
#pragma once
#include "constants.hpp"
#include "omath/3d_primitives/mesh.hpp"
#include "traits/mesh_trait.hpp"
namespace omath::cry_engine
{
using Mesh = primitives::Mesh<Mat4X4, ViewAngles, MeshTrait>;
}

View File

@@ -0,0 +1,24 @@
//
// Created by Vlad on 8/10/2025.
//
#pragma once
#include "omath/engines/cry_engine/formulas.hpp"
#include "omath/projection/camera.hpp"
namespace omath::cry_engine
{
class CameraTrait final
{
public:
[[nodiscard]]
static ViewAngles calc_look_at_angle(const Vector3<float>& cam_origin, const Vector3<float>& look_at) noexcept;
[[nodiscard]]
static Mat4X4 calc_view_matrix(const ViewAngles& angles, const Vector3<float>& cam_origin) noexcept;
[[nodiscard]]
static Mat4X4 calc_projection_matrix(const projection::FieldOfView& fov, const projection::ViewPort& view_port,
float near, float far) noexcept;
};
} // namespace omath::cry_engine

View File

@@ -0,0 +1,19 @@
//
// Created by Vladislav on 09.11.2025.
//
#pragma once
#include <omath/engines/cry_engine/constants.hpp>
#include <omath/engines/cry_engine/formulas.hpp>
namespace omath::cry_engine
{
class MeshTrait final
{
public:
[[nodiscard]]
static Mat4X4 rotation_matrix(const ViewAngles& rotation)
{
return cry_engine::rotation_matrix(rotation);
}
};
} // namespace omath::cry_engine

View File

@@ -0,0 +1,76 @@
//
// Created by Vlad on 8/6/2025.
//
#pragma once
#include "omath/engines/cry_engine/formulas.hpp"
#include "omath/projectile_prediction/projectile.hpp"
#include "omath/projectile_prediction/target.hpp"
#include <optional>
namespace omath::cry_engine
{
class PredEngineTrait final
{
public:
constexpr static Vector3<float> predict_projectile_position(const projectile_prediction::Projectile& projectile,
const float pitch, const float yaw,
const float time, const float gravity) noexcept
{
auto current_pos = projectile.m_origin
+ forward_vector({PitchAngle::from_degrees(-pitch), YawAngle::from_degrees(yaw),
RollAngle::from_degrees(0)})
* projectile.m_launch_speed * time;
current_pos.z -= (gravity * projectile.m_gravity_scale) * (time * time) * 0.5f;
return current_pos;
}
[[nodiscard]]
static constexpr Vector3<float> predict_target_position(const projectile_prediction::Target& target,
const float time, const float gravity) noexcept
{
auto predicted = target.m_origin + target.m_velocity * time;
if (target.m_is_airborne)
predicted.z -= gravity * (time * time) * 0.5f;
return predicted;
}
[[nodiscard]]
static float calc_vector_2d_distance(const Vector3<float>& delta) noexcept
{
return std::sqrt(delta.x * delta.x + delta.y * delta.y);
}
[[nodiscard]]
constexpr static float get_vector_height_coordinate(const Vector3<float>& vec) noexcept
{
return vec.z;
}
[[nodiscard]]
static Vector3<float> calc_viewpoint_from_angles(const projectile_prediction::Projectile& projectile,
Vector3<float> predicted_target_position,
const std::optional<float> projectile_pitch) noexcept
{
const auto delta2d = calc_vector_2d_distance(predicted_target_position - projectile.m_origin);
const auto height = delta2d * std::tan(angles::degrees_to_radians(projectile_pitch.value()));
return {predicted_target_position.x, predicted_target_position.y, projectile.m_origin.z + height};
}
// Due to specification of maybe_calculate_projectile_launch_pitch_angle, pitch angle must be:
// 89 look up, -89 look down
[[nodiscard]]
static float calc_direct_pitch_angle(const Vector3<float>& origin, const Vector3<float>& view_to) noexcept
{
const auto direction = (view_to - origin).normalized();
return angles::radians_to_degrees(std::asin(direction.z));
}
[[nodiscard]]
static float calc_direct_yaw_angle(const Vector3<float>& origin, const Vector3<float>& view_to) noexcept
{
const auto direction = (view_to - origin).normalized();
return angles::radians_to_degrees(-std::atan2(direction.x, direction.y));
};
};
} // namespace omath::cry_engine

View File

@@ -55,7 +55,7 @@ namespace omath::frostbite_engine
const auto delta2d = calc_vector_2d_distance(predicted_target_position - projectile.m_origin);
const auto height = delta2d * std::tan(angles::degrees_to_radians(projectile_pitch.value()));
return {predicted_target_position.x, predicted_target_position.y + height, projectile.m_origin.z};
return {predicted_target_position.x, projectile.m_origin.y + height, predicted_target_position.z};
}
// Due to specification of maybe_calculate_projectile_launch_pitch_angle, pitch angle must be:
// 89 look up, -89 look down

View File

@@ -8,5 +8,5 @@
namespace omath::opengl_engine
{
using Camera = projection::Camera<Mat4X4, ViewAngles, CameraTrait>;
using Camera = projection::Camera<Mat4X4, ViewAngles, CameraTrait, true>;
} // namespace omath::opengl_engine

View File

@@ -55,7 +55,7 @@ namespace omath::opengl_engine
const auto delta2d = calc_vector_2d_distance(predicted_target_position - projectile.m_origin);
const auto height = delta2d * std::tan(angles::degrees_to_radians(projectile_pitch.value()));
return {predicted_target_position.x, predicted_target_position.y + height, projectile.m_origin.z};
return {predicted_target_position.x, projectile.m_origin.y + height, predicted_target_position.z};
}
// Due to specification of maybe_calculate_projectile_launch_pitch_angle, pitch angle must be:
// 89 look up, -89 look down

View File

@@ -55,7 +55,7 @@ namespace omath::unity_engine
const auto delta2d = calc_vector_2d_distance(predicted_target_position - projectile.m_origin);
const auto height = delta2d * std::tan(angles::degrees_to_radians(projectile_pitch.value()));
return {predicted_target_position.x, predicted_target_position.y + height, projectile.m_origin.z};
return {predicted_target_position.x, projectile.m_origin.y + height, predicted_target_position.z};
}
// Due to specification of maybe_calculate_projectile_launch_pitch_angle, pitch angle must be:
// 89 look up, -89 look down

View File

@@ -220,6 +220,12 @@ namespace omath
{
return std::make_tuple(x, y);
}
[[nodiscard]]
constexpr std::array<Type, 2> as_array() const noexcept
{
return {x, y};
}
#ifdef OMATH_IMGUI_INTEGRATION
[[nodiscard]]
constexpr ImVec2 to_im_vec2() const noexcept

View File

@@ -4,8 +4,8 @@
#pragma once
#include "omath/trigonometry/angle.hpp"
#include "omath/linear_algebra/vector2.hpp"
#include "omath/trigonometry/angle.hpp"
#include <cstdint>
#include <expected>
#include <functional>
@@ -233,7 +233,8 @@ namespace omath
return Angle<float, 0.f, 180.f, AngleFlags::Clamped>::from_radians(std::acos(dot(other) / bottom));
}
[[nodiscard]] bool is_perpendicular(const Vector3& other, Type epsilon = static_cast<Type>(0.0001)) const noexcept
[[nodiscard]] bool is_perpendicular(const Vector3& other,
Type epsilon = static_cast<Type>(0.0001)) const noexcept
{
if (const auto angle = angle_between(other))
return std::abs(angle->as_degrees() - static_cast<Type>(90)) <= epsilon;
@@ -274,6 +275,12 @@ namespace omath
{
return length() >= other.length();
}
[[nodiscard]]
constexpr std::array<Type, 3> as_array() const noexcept
{
return {this->x, this->y, z};
}
};
} // namespace omath

View File

@@ -3,8 +3,8 @@
//
#pragma once
#include <algorithm>
#include "omath/linear_algebra/vector3.hpp"
#include <algorithm>
namespace omath
{
@@ -183,6 +183,12 @@ namespace omath
return length() >= other.length();
}
[[nodiscard]]
constexpr std::array<Type, 4> as_array() const noexcept
{
return {this->x, this->y, this->z, w};
}
#ifdef OMATH_IMGUI_INTEGRATION
[[nodiscard]]
constexpr ImVec4 to_im_vec4() const noexcept
@@ -200,7 +206,7 @@ namespace omath
return {static_cast<Type>(other.x), static_cast<Type>(other.y), static_cast<Type>(other.z)};
}
#endif
};
};
} // namespace omath
template<> struct std::hash<omath::Vector4<float>>

View File

@@ -52,7 +52,7 @@ namespace omath::projection
requires noexcept(T::calc_projection_matrix(fov, viewport, znear, zfar));
};
template<class Mat4X4Type, class ViewAnglesType, class TraitClass>
template<class Mat4X4Type, class ViewAnglesType, class TraitClass, bool inverted_z = false>
requires CameraEngineConcept<TraitClass, Mat4X4Type, ViewAnglesType>
class Camera final
{
@@ -80,59 +80,96 @@ namespace omath::projection
{
m_view_angles = TraitClass::calc_look_at_angle(m_origin, target);
m_view_projection_matrix = std::nullopt;
m_view_matrix = std::nullopt;
}
protected:
[[nodiscard]] Mat4X4Type calc_view_projection_matrix() const noexcept
[[nodiscard]]
Vector3<float> get_forward() const noexcept
{
return TraitClass::calc_projection_matrix(m_field_of_view, m_view_port, m_near_plane_distance,
m_far_plane_distance)
* TraitClass::calc_view_matrix(m_view_angles, m_origin);
const auto& view_matrix = get_view_matrix();
if constexpr (inverted_z)
return -Vector3<float>{view_matrix[2, 0], view_matrix[2, 1], view_matrix[2, 2]};
return {view_matrix[2, 0], view_matrix[2, 1], view_matrix[2, 2]};
}
[[nodiscard]]
Vector3<float> get_right() const noexcept
{
const auto& view_matrix = get_view_matrix();
return {view_matrix[0, 0], view_matrix[0, 1], view_matrix[0, 2]};
}
[[nodiscard]]
Vector3<float> get_up() const noexcept
{
const auto& view_matrix = get_view_matrix();
return {view_matrix[1, 0], view_matrix[1, 1], view_matrix[1, 2]};
}
public:
[[nodiscard]] const Mat4X4Type& get_view_projection_matrix() const noexcept
{
if (!m_view_projection_matrix.has_value())
m_view_projection_matrix = calc_view_projection_matrix();
m_view_projection_matrix = get_projection_matrix() * get_view_matrix();
return m_view_projection_matrix.value();
}
[[nodiscard]] const Mat4X4Type& get_view_matrix() const noexcept
{
if (!m_view_matrix.has_value())
m_view_matrix = TraitClass::calc_view_matrix(m_view_angles, m_origin);
return m_view_matrix.value();
}
[[nodiscard]] const Mat4X4Type& get_projection_matrix() const noexcept
{
if (!m_projection_matrix.has_value())
m_projection_matrix = TraitClass::calc_projection_matrix(m_field_of_view, m_view_port,
m_near_plane_distance, m_far_plane_distance);
return m_projection_matrix.value();
}
void set_field_of_view(const FieldOfView& fov) noexcept
{
m_field_of_view = fov;
m_view_projection_matrix = std::nullopt;
m_projection_matrix = std::nullopt;
}
void set_near_plane(const float near) noexcept
{
m_near_plane_distance = near;
m_view_projection_matrix = std::nullopt;
m_projection_matrix = std::nullopt;
}
void set_far_plane(const float far) noexcept
{
m_far_plane_distance = far;
m_view_projection_matrix = std::nullopt;
m_projection_matrix = std::nullopt;
}
void set_view_angles(const ViewAnglesType& view_angles) noexcept
{
m_view_angles = view_angles;
m_view_projection_matrix = std::nullopt;
m_view_matrix = std::nullopt;
}
void set_origin(const Vector3<float>& origin) noexcept
{
m_origin = origin;
m_view_projection_matrix = std::nullopt;
m_view_matrix = std::nullopt;
}
void set_view_port(const ViewPort& view_port) noexcept
{
m_view_port = view_port;
m_view_projection_matrix = std::nullopt;
m_projection_matrix = std::nullopt;
}
[[nodiscard]] const FieldOfView& get_field_of_view() const noexcept
@@ -283,7 +320,8 @@ namespace omath::projection
Angle<float, 0.f, 180.f, AngleFlags::Clamped> m_field_of_view;
mutable std::optional<Mat4X4Type> m_view_projection_matrix;
mutable std::optional<Mat4X4Type> m_projection_matrix;
mutable std::optional<Mat4X4Type> m_view_matrix;
float m_far_plane_distance;
float m_near_plane_distance;

View File

@@ -15,7 +15,10 @@ endif()
set(EXAMPLES_BIN_DIR "${PROJECT_ROOT}/out/${CMAKE_BUILD_TYPE}")
if(NOT EXISTS "${EXAMPLES_BIN_DIR}")
message(FATAL_ERROR "Examples binary directory not found: ${EXAMPLES_BIN_DIR}. Please build the project first.")
message(
FATAL_ERROR
"Examples binary directory not found: ${EXAMPLES_BIN_DIR}. Please build the project first."
)
endif()
message(STATUS "Looking for benchmark executables in: ${EXAMPLES_BIN_DIR}")
@@ -43,16 +46,13 @@ foreach(EXAMPLE_PATH ${EXAMPLE_FILES})
endif()
# On Linux/macOS, check permissions or just try to run it.
message(STATUS "-------------------------------------------------")
message(STATUS "Running benchmark: ${FILENAME}")
message(STATUS "-------------------------------------------------")
execute_process(
COMMAND "${EXAMPLE_PATH}"
WORKING_DIRECTORY "${PROJECT_ROOT}"
RESULT_VARIABLE EXIT_CODE
)
execute_process(COMMAND "${EXAMPLE_PATH}" WORKING_DIRECTORY "${PROJECT_ROOT}"
RESULT_VARIABLE EXIT_CODE)
if(NOT EXIT_CODE EQUAL 0)
message(WARNING "Benchmark ${FILENAME} exited with error code: ${EXIT_CODE}")

View File

@@ -15,7 +15,10 @@ endif()
set(EXAMPLES_BIN_DIR "${PROJECT_ROOT}/out/${CMAKE_BUILD_TYPE}")
if(NOT EXISTS "${EXAMPLES_BIN_DIR}")
message(FATAL_ERROR "Examples binary directory not found: ${EXAMPLES_BIN_DIR}. Please build the project first.")
message(
FATAL_ERROR
"Examples binary directory not found: ${EXAMPLES_BIN_DIR}. Please build the project first."
)
endif()
message(STATUS "Looking for example executables in: ${EXAMPLES_BIN_DIR}")
@@ -43,16 +46,13 @@ foreach(EXAMPLE_PATH ${EXAMPLE_FILES})
endif()
# On Linux/macOS, check permissions or just try to run it.
message(STATUS "-------------------------------------------------")
message(STATUS "Running example: ${FILENAME}")
message(STATUS "-------------------------------------------------")
execute_process(
COMMAND "${EXAMPLE_PATH}"
WORKING_DIRECTORY "${PROJECT_ROOT}"
RESULT_VARIABLE EXIT_CODE
)
execute_process(COMMAND "${EXAMPLE_PATH}" WORKING_DIRECTORY "${PROJECT_ROOT}"
RESULT_VARIABLE EXIT_CODE)
if(NOT EXIT_CODE EQUAL 0)
message(WARNING "Example ${FILENAME} exited with error code: ${EXIT_CODE}")

View File

@@ -15,7 +15,10 @@ endif()
set(EXAMPLES_BIN_DIR "${PROJECT_ROOT}/out/${CMAKE_BUILD_TYPE}")
if(NOT EXISTS "${EXAMPLES_BIN_DIR}")
message(FATAL_ERROR "Examples binary directory not found: ${EXAMPLES_BIN_DIR}. Please build the project first.")
message(
FATAL_ERROR
"Examples binary directory not found: ${EXAMPLES_BIN_DIR}. Please build the project first."
)
endif()
message(STATUS "Looking for unit test executables in: ${EXAMPLES_BIN_DIR}")
@@ -43,16 +46,13 @@ foreach(EXAMPLE_PATH ${EXAMPLE_FILES})
endif()
# On Linux/macOS, check permissions or just try to run it.
message(STATUS "-------------------------------------------------")
message(STATUS "Running unit_tests: ${FILENAME}")
message(STATUS "-------------------------------------------------")
execute_process(
COMMAND "${EXAMPLE_PATH}"
WORKING_DIRECTORY "${PROJECT_ROOT}"
RESULT_VARIABLE EXIT_CODE
)
execute_process(COMMAND "${EXAMPLE_PATH}" WORKING_DIRECTORY "${PROJECT_ROOT}"
RESULT_VARIABLE EXIT_CODE)
if(NOT EXIT_CODE EQUAL 0)
message(WARNING "Example ${FILENAME} exited with error code: ${EXIT_CODE}")

15
scripts/cmake-format.sh Normal file
View File

@@ -0,0 +1,15 @@
#!/usr/bin/env bash
set -euo pipefail
# Format all CMakeLists.txt and *.cmake files in the repo (excluding common build dirs)
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
cd "$REPO_ROOT"
find . \
-path "./build" -prune -o \
-path "./cmake-build*" -prune -o \
-path "./out" -prune -o \
-path "./.git" -prune -o \
\( -name "CMakeLists.txt" -o -name "*.cmake" \) -print0 \
| xargs -0 cmake-format -i

View File

@@ -0,0 +1,26 @@
//
// Created by Vlad on 8/11/2025.
//
#include "omath/engines/cry_engine/traits/camera_trait.hpp"
namespace omath::cry_engine
{
ViewAngles CameraTrait::calc_look_at_angle(const Vector3<float>& cam_origin, const Vector3<float>& look_at) noexcept
{
const auto direction = (look_at - cam_origin).normalized();
return {PitchAngle::from_radians(std::asin(direction.z)),
YawAngle::from_radians(-std::atan2(direction.x, direction.y)), RollAngle::from_radians(0.f)};
}
Mat4X4 CameraTrait::calc_view_matrix(const ViewAngles& angles, const Vector3<float>& cam_origin) noexcept
{
return cry_engine::calc_view_matrix(angles, cam_origin);
}
Mat4X4 CameraTrait::calc_projection_matrix(const projection::FieldOfView& fov,
const projection::ViewPort& view_port, const float near,
const float far) noexcept
{
return calc_perspective_projection_matrix(fov.as_degrees(), view_port.aspect_ratio(), near, far);
}
} // namespace omath::unity_engine

View File

@@ -0,0 +1,42 @@
//
// Created by Vlad on 3/22/2025.
//
#include "omath/engines/cry_engine/formulas.hpp"
namespace omath::cry_engine
{
Vector3<float> forward_vector(const ViewAngles& angles) noexcept
{
const auto vec = rotation_matrix(angles) * mat_column_from_vector(k_abs_forward);
return {vec.at(0, 0), vec.at(1, 0), vec.at(2, 0)};
}
Vector3<float> right_vector(const ViewAngles& angles) noexcept
{
const auto vec = rotation_matrix(angles) * mat_column_from_vector(k_abs_right);
return {vec.at(0, 0), vec.at(1, 0), vec.at(2, 0)};
}
Vector3<float> up_vector(const ViewAngles& angles) noexcept
{
const auto vec = rotation_matrix(angles) * mat_column_from_vector(k_abs_up);
return {vec.at(0, 0), vec.at(1, 0), vec.at(2, 0)};
}
Mat4X4 calc_view_matrix(const ViewAngles& angles, const Vector3<float>& cam_origin) noexcept
{
return mat_camera_view<float, MatStoreType::ROW_MAJOR>(forward_vector(angles), right_vector(angles),
up_vector(angles), cam_origin);
}
Mat4X4 rotation_matrix(const ViewAngles& angles) noexcept
{
return mat_rotation_axis_z<float, MatStoreType::ROW_MAJOR>(angles.yaw)
* mat_rotation_axis_y<float, MatStoreType::ROW_MAJOR>(angles.roll)
* mat_rotation_axis_x<float, MatStoreType::ROW_MAJOR>(angles.pitch);
}
Mat4X4 calc_perspective_projection_matrix(const float field_of_view, const float aspect_ratio, const float near,
const float far) noexcept
{
return mat_perspective_left_handed(field_of_view, aspect_ratio, near, far);
}
} // namespace omath::unity_engine

View File

@@ -0,0 +1,240 @@
//
// Created by Vladislav on 19.02.2026.
//
#include <gtest/gtest.h>
#include <omath/engines/cry_engine/camera.hpp>
#include <omath/engines/cry_engine/constants.hpp>
#include <omath/engines/cry_engine/formulas.hpp>
#include <random>
#include <ranges>
using namespace omath;
TEST(unit_test_cry_engine, look_at_forward)
{
const auto angles = cry_engine::CameraTrait::calc_look_at_angle({}, cry_engine::k_abs_forward);
// ReSharper disable once CppTooWideScopeInitStatement
const auto dir_vector = cry_engine::forward_vector(angles);
for (const auto& [result, etalon] : std::views::zip(dir_vector.as_array(), cry_engine::k_abs_forward.as_array()))
EXPECT_NEAR(result, etalon, 0.0001f);
}
TEST(unit_test_cry_engine, look_at_right)
{
const auto angles = cry_engine::CameraTrait::calc_look_at_angle({}, cry_engine::k_abs_right);
// ReSharper disable once CppTooWideScopeInitStatement
const auto dir_vector = cry_engine::forward_vector(angles);
for (const auto& [result, etalon] : std::views::zip(dir_vector.as_array(), cry_engine::k_abs_right.as_array()))
EXPECT_NEAR(result, etalon, 0.0001f);
}
TEST(unit_test_cry_engine, look_at_up)
{
const auto angles = cry_engine::CameraTrait::calc_look_at_angle({}, cry_engine::k_abs_right);
// ReSharper disable once CppTooWideScopeInitStatement
const auto dir_vector = cry_engine::forward_vector(angles);
for (const auto& [result, etalon] : std::views::zip(dir_vector.as_array(), cry_engine::k_abs_right.as_array()))
EXPECT_NEAR(result, etalon, 0.0001f);
}
TEST(unit_test_cry_engine, look_at_back)
{
const auto angles = cry_engine::CameraTrait::calc_look_at_angle({}, -cry_engine::k_abs_forward);
// ReSharper disable once CppTooWideScopeInitStatement
const auto dir_vector = cry_engine::forward_vector(angles);
for (const auto& [result, etalon] : std::views::zip(dir_vector.as_array(), (-cry_engine::k_abs_forward).as_array()))
EXPECT_NEAR(result, etalon, 0.0001f);
}
TEST(unit_test_cry_engine, look_at_left)
{
const auto angles = cry_engine::CameraTrait::calc_look_at_angle({}, -cry_engine::k_abs_right);
// ReSharper disable once CppTooWideScopeInitStatement
const auto dir_vector = cry_engine::forward_vector(angles);
for (const auto& [result, etalon] : std::views::zip(dir_vector.as_array(), (-cry_engine::k_abs_right).as_array()))
EXPECT_NEAR(result, etalon, 0.0001f);
}
TEST(unit_test_cry_engine, look_at_down)
{
const auto angles = cry_engine::CameraTrait::calc_look_at_angle({}, -cry_engine::k_abs_up);
// ReSharper disable once CppTooWideScopeInitStatement
const auto dir_vector = cry_engine::forward_vector(angles);
for (const auto& [result, etalon] : std::views::zip(dir_vector.as_array(), (-cry_engine::k_abs_up).as_array()))
EXPECT_NEAR(result, etalon, 0.0001f);
}
TEST(unit_test_cry_engine, RightVector)
{
const auto right = omath::cry_engine::right_vector({});
EXPECT_EQ(right, omath::cry_engine::k_abs_right);
}
TEST(unit_test_cry_engine, UpVector)
{
const auto up = omath::cry_engine::up_vector({});
EXPECT_EQ(up, omath::cry_engine::k_abs_up);
}
TEST(unit_test_cry_engine, ProjectTargetMovedFromCamera)
{
constexpr auto fov = omath::projection::FieldOfView::from_degrees(60.f);
const auto cam = omath::cry_engine::Camera({0, 0, 0}, {}, {1280.f, 720.f}, fov, 0.01f, 1000.f);
for (float distance = 0.02f; distance < 100.f; distance += 0.01f)
{
const auto projected = cam.world_to_screen({0, distance, 0});
EXPECT_TRUE(projected.has_value());
if (!projected.has_value())
continue;
EXPECT_NEAR(projected->x, 640, 0.00001f);
EXPECT_NEAR(projected->y, 360, 0.00001f);
}
}
TEST(unit_test_cry_engine, CameraSetAndGetFov)
{
constexpr auto fov = omath::projection::FieldOfView::from_degrees(90.f);
auto cam = omath::cry_engine::Camera({0, 0, 0}, {}, {1920.f, 1080.f}, fov, 0.01f, 1000.f);
EXPECT_EQ(cam.get_field_of_view().as_degrees(), 90.f);
cam.set_field_of_view(omath::projection::FieldOfView::from_degrees(50.f));
EXPECT_EQ(cam.get_field_of_view().as_degrees(), 50.f);
}
TEST(unit_test_cry_engine, CameraSetAndGetOrigin)
{
auto cam = omath::cry_engine::Camera({0, 0, 0}, {}, {1920.f, 1080.f}, {}, 0.01f, 1000.f);
EXPECT_EQ(cam.get_origin(), omath::Vector3<float>{});
cam.set_field_of_view(omath::projection::FieldOfView::from_degrees(50.f));
EXPECT_EQ(cam.get_field_of_view().as_degrees(), 50.f);
}
TEST(unit_test_cry_engine, loook_at_random_all_axis)
{
std::mt19937 gen(std::random_device{}()); // Seed with a non-deterministic source
std::uniform_real_distribution<float> dist(-1000.f, 1000.f);
constexpr auto fov = omath::projection::FieldOfView::from_degrees(90.f);
auto cam = omath::cry_engine::Camera({0, 0, 0}, {}, {1920.f, 1080.f}, fov, 0.001f, 10000.f);
std::size_t failed_points = 0;
for (int i = 0; i < 1000; i++)
{
const auto position_to_look = omath::Vector3<float>{dist(gen), dist(gen), dist(gen)};
if (cam.get_origin().distance_to(position_to_look) < 10)
continue;
cam.look_at(position_to_look);
auto projected_pos = cam.world_to_view_port(position_to_look);
EXPECT_TRUE(projected_pos.has_value());
if (!projected_pos)
continue;
if (std::abs(projected_pos->x - 0.f) >= 0.0001f || std::abs(projected_pos->y - 0.f) >= 0.0001f)
failed_points++;
}
EXPECT_LE(failed_points, 100);
}
TEST(unit_test_cry_engine, loook_at_random_x_axis)
{
std::mt19937 gen(std::random_device{}()); // Seed with a non-deterministic source
std::uniform_real_distribution<float> dist(-1000.f, 1000.f);
constexpr auto fov = omath::projection::FieldOfView::from_degrees(90.f);
auto cam = omath::cry_engine::Camera({0, 0, 0}, {}, {1920.f, 1080.f}, fov, 0.001f, 10000.f);
std::size_t failed_points = 0;
for (int i = 0; i < 1000; i++)
{
const auto position_to_look = omath::Vector3<float>{dist(gen), 0.f, 0.f};
if (cam.get_origin().distance_to(position_to_look) < 10)
continue;
cam.look_at(position_to_look);
auto projected_pos = cam.world_to_view_port(position_to_look);
EXPECT_TRUE(projected_pos.has_value());
if (!projected_pos)
continue;
if (std::abs(projected_pos->x - 0.f) >= 0.001f || std::abs(projected_pos->y - 0.f) >= 0.001f)
failed_points++;
}
EXPECT_LE(failed_points, 100);
}
TEST(unit_test_cry_engine, loook_at_random_y_axis)
{
std::mt19937 gen(std::random_device{}()); // Seed with a non-deterministic source
std::uniform_real_distribution<float> dist(-1000.f, 1000.f);
constexpr auto fov = omath::projection::FieldOfView::from_degrees(90.f);
auto cam = omath::cry_engine::Camera({0, 0, 0}, {}, {1920.f, 1080.f}, fov, 0.001f, 10000.f);
std::size_t failed_points = 0;
for (int i = 0; i < 1000; i++)
{
const auto position_to_look = omath::Vector3<float>{0.f, dist(gen), 0.f};
if (cam.get_origin().distance_to(position_to_look) < 10)
continue;
cam.look_at(position_to_look);
auto projected_pos = cam.world_to_view_port(position_to_look);
EXPECT_TRUE(projected_pos.has_value());
if (!projected_pos)
continue;
if (std::abs(projected_pos->x - 0.f) >= 0.01f || std::abs(projected_pos->y - 0.f) >= 0.01f)
failed_points++;
}
EXPECT_LE(failed_points, 100);
}
TEST(unit_test_cry_engine, loook_at_random_z_axis)
{
std::mt19937 gen(std::random_device{}()); // Seed with a non-deterministic source
std::uniform_real_distribution<float> dist(-1000.f, 1000.f);
constexpr auto fov = omath::projection::FieldOfView::from_degrees(90.f);
auto cam = omath::cry_engine::Camera({0, 0, 0}, {}, {1920.f, 1080.f}, fov, 0.001f, 10000.f);
std::size_t failed_points = 0;
for (int i = 0; i < 1000; i++)
{
const auto position_to_look = omath::Vector3<float>{0.f, 0.f, dist(gen)};
if (cam.get_origin().distance_to(position_to_look) < 10)
continue;
cam.look_at(position_to_look);
auto projected_pos = cam.world_to_view_port(position_to_look);
EXPECT_TRUE(projected_pos.has_value());
if (!projected_pos)
continue;
if (std::abs(projected_pos->x - 0.f) >= 0.01f || std::abs(projected_pos->y - 0.f) >= 0.01f)
failed_points++;
}
EXPECT_LE(failed_points, 100);
}

View File

@@ -7,6 +7,7 @@
#include <omath/engines/frostbite_engine/formulas.hpp>
#include <print>
#include <random>
#include <ranges>
TEST(unit_test_frostbite_engine, UnitsToCentimeters_BasicValues)
{
@@ -352,4 +353,55 @@ TEST(unit_test_frostbite_engine, loook_at_random_z_axis)
failed_points++;
}
EXPECT_LE(failed_points, 100);
}
}
TEST(unit_test_frostbite_engine, look_at_right)
{
const auto angles = omath::frostbite_engine::CameraTrait::calc_look_at_angle({}, omath::frostbite_engine::k_abs_right);
// ReSharper disable once CppTooWideScopeInitStatement
const auto dir_vector = omath::frostbite_engine::forward_vector(angles);
for (const auto& [result, etalon] :
std::views::zip(dir_vector.as_array(), omath::frostbite_engine::k_abs_right.as_array()))
EXPECT_NEAR(result, etalon, 0.0001f);
}
TEST(unit_test_frostbite_engine, look_at_up)
{
const auto angles = omath::frostbite_engine::CameraTrait::calc_look_at_angle({}, omath::frostbite_engine::k_abs_right);
// ReSharper disable once CppTooWideScopeInitStatement
const auto dir_vector = omath::frostbite_engine::forward_vector(angles);
for (const auto& [result, etalon] :
std::views::zip(dir_vector.as_array(), omath::frostbite_engine::k_abs_right.as_array()))
EXPECT_NEAR(result, etalon, 0.0001f);
}
TEST(unit_test_frostbite_engine, look_at_back)
{
const auto angles = omath::frostbite_engine::CameraTrait::calc_look_at_angle({}, -omath::frostbite_engine::k_abs_forward);
// ReSharper disable once CppTooWideScopeInitStatement
const auto dir_vector = omath::frostbite_engine::forward_vector(angles);
for (const auto& [result, etalon] :
std::views::zip(dir_vector.as_array(), (-omath::frostbite_engine::k_abs_forward).as_array()))
EXPECT_NEAR(result, etalon, 0.0001f);
}
TEST(unit_test_frostbite_engine, look_at_left)
{
const auto angles = omath::frostbite_engine::CameraTrait::calc_look_at_angle({}, -omath::frostbite_engine::k_abs_right);
// ReSharper disable once CppTooWideScopeInitStatement
const auto dir_vector = omath::frostbite_engine::forward_vector(angles);
for (const auto& [result, etalon] :
std::views::zip(dir_vector.as_array(), (-omath::frostbite_engine::k_abs_right).as_array()))
EXPECT_NEAR(result, etalon, 0.0001f);
}
TEST(unit_test_frostbite_engine, look_at_down)
{
const auto angles = omath::frostbite_engine::CameraTrait::calc_look_at_angle({}, -omath::frostbite_engine::k_abs_up);
// ReSharper disable once CppTooWideScopeInitStatement
const auto dir_vector = omath::frostbite_engine::forward_vector(angles);
for (const auto& [result, etalon] :
std::views::zip(dir_vector.as_array(), (-omath::frostbite_engine::k_abs_up).as_array()))
EXPECT_NEAR(result, etalon, 0.0001f);
}

View File

@@ -6,6 +6,7 @@
#include <omath/engines/iw_engine/constants.hpp>
#include <omath/engines/iw_engine/formulas.hpp>
#include <random>
#include <ranges>
TEST(unit_test_iw_engine, ForwardVector)
{
@@ -223,4 +224,60 @@ TEST(unit_test_iw_engine, loook_at_random_z_axis)
failed_points++;
}
EXPECT_LE(failed_points, 100);
}
TEST(unit_test_iw_engine, look_at_forward)
{
const auto angles = omath::iw_engine::CameraTrait::calc_look_at_angle({}, omath::iw_engine::k_abs_forward);
// ReSharper disable once CppTooWideScopeInitStatement
const auto dir_vector = omath::iw_engine::forward_vector(angles);
for (const auto& [result, etalon] : std::views::zip(dir_vector.as_array(), omath::iw_engine::k_abs_forward.as_array()))
EXPECT_NEAR(result, etalon, 0.0001f);
}
TEST(unit_test_iw_engine, look_at_right)
{
const auto angles = omath::iw_engine::CameraTrait::calc_look_at_angle({}, omath::iw_engine::k_abs_right);
// ReSharper disable once CppTooWideScopeInitStatement
const auto dir_vector = omath::iw_engine::forward_vector(angles);
for (const auto& [result, etalon] : std::views::zip(dir_vector.as_array(), omath::iw_engine::k_abs_right.as_array()))
EXPECT_NEAR(result, etalon, 0.0001f);
}
TEST(unit_test_iw_engine, look_at_up)
{
const auto angles = omath::iw_engine::CameraTrait::calc_look_at_angle({}, omath::iw_engine::k_abs_right);
// ReSharper disable once CppTooWideScopeInitStatement
const auto dir_vector = omath::iw_engine::forward_vector(angles);
for (const auto& [result, etalon] : std::views::zip(dir_vector.as_array(), omath::iw_engine::k_abs_right.as_array()))
EXPECT_NEAR(result, etalon, 0.0001f);
}
TEST(unit_test_iw_engine, look_at_back)
{
const auto angles = omath::iw_engine::CameraTrait::calc_look_at_angle({}, -omath::iw_engine::k_abs_forward);
// ReSharper disable once CppTooWideScopeInitStatement
const auto dir_vector = omath::iw_engine::forward_vector(angles);
for (const auto& [result, etalon] : std::views::zip(dir_vector.as_array(), (-omath::iw_engine::k_abs_forward).as_array()))
EXPECT_NEAR(result, etalon, 0.0001f);
}
TEST(unit_test_iw_engine, look_at_left)
{
const auto angles = omath::iw_engine::CameraTrait::calc_look_at_angle({}, -omath::iw_engine::k_abs_right);
// ReSharper disable once CppTooWideScopeInitStatement
const auto dir_vector = omath::iw_engine::forward_vector(angles);
for (const auto& [result, etalon] : std::views::zip(dir_vector.as_array(), (-omath::iw_engine::k_abs_right).as_array()))
EXPECT_NEAR(result, etalon, 0.0001f);
}
TEST(unit_test_iw_engine, look_at_down)
{
const auto angles = omath::iw_engine::CameraTrait::calc_look_at_angle({}, -omath::iw_engine::k_abs_up);
// ReSharper disable once CppTooWideScopeInitStatement
const auto dir_vector = omath::iw_engine::forward_vector(angles);
EXPECT_NEAR(dir_vector.z, -0.99984f, 0.0001f);
EXPECT_NEAR(dir_vector.x,- 0.017f, 0.01f);
EXPECT_NEAR(dir_vector.y, 0.f, 0.001f);
}

View File

@@ -6,6 +6,7 @@
#include <omath/engines/opengl_engine/constants.hpp>
#include <omath/engines/opengl_engine/formulas.hpp>
#include <random>
#include <ranges>
TEST(unit_test_opengl, UnitsToCentimeters_BasicValues)
{
@@ -337,4 +338,60 @@ TEST(unit_test_opengl_engine, loook_at_random_z_axis)
failed_points++;
}
EXPECT_LE(failed_points, 100);
}
TEST(unit_test_opengl_engine, look_at_forward)
{
const auto angles = omath::opengl_engine::CameraTrait::calc_look_at_angle({}, omath::opengl_engine::k_abs_forward);
// ReSharper disable once CppTooWideScopeInitStatement
const auto dir_vector = omath::opengl_engine::forward_vector(angles);
for (const auto& [result, etalon] : std::views::zip(dir_vector.as_array(), omath::opengl_engine::k_abs_forward.as_array()))
EXPECT_NEAR(result, etalon, 0.0001f);
}
TEST(unit_test_opengl_engine, look_at_right)
{
const auto angles = omath::opengl_engine::CameraTrait::calc_look_at_angle({}, omath::opengl_engine::k_abs_right);
// ReSharper disable once CppTooWideScopeInitStatement
const auto dir_vector = omath::opengl_engine::forward_vector(angles);
for (const auto& [result, etalon] : std::views::zip(dir_vector.as_array(), omath::opengl_engine::k_abs_right.as_array()))
EXPECT_NEAR(result, etalon, 0.0001f);
}
TEST(unit_test_opengl_engine, look_at_up)
{
const auto angles = omath::opengl_engine::CameraTrait::calc_look_at_angle({}, omath::opengl_engine::k_abs_right);
// ReSharper disable once CppTooWideScopeInitStatement
const auto dir_vector = omath::opengl_engine::forward_vector(angles);
for (const auto& [result, etalon] : std::views::zip(dir_vector.as_array(), omath::opengl_engine::k_abs_right.as_array()))
EXPECT_NEAR(result, etalon, 0.0001f);
}
TEST(unit_test_opengl_engine, look_at_back)
{
const auto angles = omath::opengl_engine::CameraTrait::calc_look_at_angle({}, -omath::opengl_engine::k_abs_forward);
// ReSharper disable once CppTooWideScopeInitStatement
const auto dir_vector = omath::opengl_engine::forward_vector(angles);
for (const auto& [result, etalon] : std::views::zip(dir_vector.as_array(), (-omath::opengl_engine::k_abs_forward).as_array()))
EXPECT_NEAR(result, etalon, 0.0001f);
}
TEST(unit_test_opengl_engine, look_at_left)
{
const auto angles = omath::opengl_engine::CameraTrait::calc_look_at_angle({}, -omath::opengl_engine::k_abs_right);
// ReSharper disable once CppTooWideScopeInitStatement
const auto dir_vector = omath::opengl_engine::forward_vector(angles);
for (const auto& [result, etalon] : std::views::zip(dir_vector.as_array(), (-omath::opengl_engine::k_abs_right).as_array()))
EXPECT_NEAR(result, etalon, 0.0001f);
}
TEST(unit_test_opengl_engine, look_at_down)
{
const auto angles = omath::opengl_engine::CameraTrait::calc_look_at_angle({}, -omath::opengl_engine::k_abs_up);
// ReSharper disable once CppTooWideScopeInitStatement
const auto dir_vector = omath::opengl_engine::forward_vector(angles);
for (const auto& [result, etalon] : std::views::zip(dir_vector.as_array(), (-omath::opengl_engine::k_abs_up).as_array()))
EXPECT_NEAR(result, etalon, 0.0001f);
}

View File

@@ -6,6 +6,7 @@
#include <omath/engines/source_engine/constants.hpp>
#include <omath/engines/source_engine/formulas.hpp>
#include <random>
#include <ranges>
TEST(unit_test_source_engine_units, HammerUnitsToCentimeters_BasicValues)
{
@@ -365,4 +366,60 @@ TEST(unit_test_source_engine, loook_at_random_z_axis)
failed_points++;
}
EXPECT_LE(failed_points, 100);
}
TEST(unit_test_source_engine, look_at_forward)
{
const auto angles = omath::source_engine::CameraTrait::calc_look_at_angle({}, omath::source_engine::k_abs_forward);
// ReSharper disable once CppTooWideScopeInitStatement
const auto dir_vector = omath::source_engine::forward_vector(angles);
for (const auto& [result, etalon] : std::views::zip(dir_vector.as_array(), omath::source_engine::k_abs_forward.as_array()))
EXPECT_NEAR(result, etalon, 0.0001f);
}
TEST(unit_test_source_engine, look_at_right)
{
const auto angles = omath::source_engine::CameraTrait::calc_look_at_angle({}, omath::source_engine::k_abs_right);
// ReSharper disable once CppTooWideScopeInitStatement
const auto dir_vector = omath::source_engine::forward_vector(angles);
for (const auto& [result, etalon] : std::views::zip(dir_vector.as_array(), omath::source_engine::k_abs_right.as_array()))
EXPECT_NEAR(result, etalon, 0.0001f);
}
TEST(unit_test_source_engine, look_at_up)
{
const auto angles = omath::source_engine::CameraTrait::calc_look_at_angle({}, omath::source_engine::k_abs_right);
// ReSharper disable once CppTooWideScopeInitStatement
const auto dir_vector = omath::source_engine::forward_vector(angles);
for (const auto& [result, etalon] : std::views::zip(dir_vector.as_array(), omath::source_engine::k_abs_right.as_array()))
EXPECT_NEAR(result, etalon, 0.0001f);
}
TEST(unit_test_source_engine, look_at_back)
{
const auto angles = omath::source_engine::CameraTrait::calc_look_at_angle({}, -omath::source_engine::k_abs_forward);
// ReSharper disable once CppTooWideScopeInitStatement
const auto dir_vector = omath::source_engine::forward_vector(angles);
for (const auto& [result, etalon] : std::views::zip(dir_vector.as_array(), (-omath::source_engine::k_abs_forward).as_array()))
EXPECT_NEAR(result, etalon, 0.0001f);
}
TEST(unit_test_source_engine, look_at_left)
{
const auto angles = omath::source_engine::CameraTrait::calc_look_at_angle({}, -omath::source_engine::k_abs_right);
// ReSharper disable once CppTooWideScopeInitStatement
const auto dir_vector = omath::source_engine::forward_vector(angles);
for (const auto& [result, etalon] : std::views::zip(dir_vector.as_array(), (-omath::source_engine::k_abs_right).as_array()))
EXPECT_NEAR(result, etalon, 0.0001f);
}
TEST(unit_test_source_engine, look_at_down)
{
const auto angles = omath::source_engine::CameraTrait::calc_look_at_angle({}, -omath::source_engine::k_abs_up);
// ReSharper disable once CppTooWideScopeInitStatement
const auto dir_vector = omath::source_engine::forward_vector(angles);
EXPECT_NEAR(dir_vector.z, -0.99984f, 0.0001f);
EXPECT_NEAR(dir_vector.x,- 0.017f, 0.01f);
EXPECT_NEAR(dir_vector.y, 0.f, 0.001f);
}

View File

@@ -7,6 +7,7 @@
#include <omath/engines/unity_engine/formulas.hpp>
#include <print>
#include <random>
#include <ranges>
TEST(unit_test_unity_engine, UnitsToCentimeters_BasicValues)
{
@@ -207,7 +208,8 @@ TEST(unit_test_unity_engine, Project)
constexpr auto fov = omath::projection::FieldOfView::from_degrees(60.f);
const auto cam = omath::unity_engine::Camera({0, 0, 0}, {}, {1280.f, 720.f}, fov, 0.03f, 1000.f);
const auto proj = cam.world_to_screen<omath::unity_engine::Camera::ScreenStart::BOTTOM_LEFT_CORNER>({10.f, 3, 10.f});
const auto proj =
cam.world_to_screen<omath::unity_engine::Camera::ScreenStart::BOTTOM_LEFT_CORNER>({10.f, 3, 10.f});
EXPECT_NEAR(proj->x, 1263.538, 0.001f);
EXPECT_NEAR(proj->y, 547.061f, 0.001f);
@@ -353,4 +355,65 @@ TEST(unit_test_unity_engine, loook_at_random_z_axis)
failed_points++;
}
EXPECT_LE(failed_points, 100);
}
}
TEST(unit_test_unity_engine, look_at_forward)
{
const auto angles = omath::unity_engine::CameraTrait::calc_look_at_angle({}, omath::unity_engine::k_abs_forward);
// ReSharper disable once CppTooWideScopeInitStatement
const auto dir_vector = omath::unity_engine::forward_vector(angles);
for (const auto& [result, etalon] :
std::views::zip(dir_vector.as_array(), omath::unity_engine::k_abs_forward.as_array()))
EXPECT_NEAR(result, etalon, 0.0001f);
}
TEST(unit_test_unity_engine, look_at_right)
{
const auto angles = omath::unity_engine::CameraTrait::calc_look_at_angle({}, omath::unity_engine::k_abs_right);
// ReSharper disable once CppTooWideScopeInitStatement
const auto dir_vector = omath::unity_engine::forward_vector(angles);
for (const auto& [result, etalon] :
std::views::zip(dir_vector.as_array(), omath::unity_engine::k_abs_right.as_array()))
EXPECT_NEAR(result, etalon, 0.0001f);
}
TEST(unit_test_unity_engine, look_at_up)
{
const auto angles = omath::unity_engine::CameraTrait::calc_look_at_angle({}, omath::unity_engine::k_abs_right);
// ReSharper disable once CppTooWideScopeInitStatement
const auto dir_vector = omath::unity_engine::forward_vector(angles);
for (const auto& [result, etalon] :
std::views::zip(dir_vector.as_array(), omath::unity_engine::k_abs_right.as_array()))
EXPECT_NEAR(result, etalon, 0.0001f);
}
TEST(unit_test_unity_engine, look_at_back)
{
const auto angles = omath::unity_engine::CameraTrait::calc_look_at_angle({}, -omath::unity_engine::k_abs_forward);
// ReSharper disable once CppTooWideScopeInitStatement
const auto dir_vector = omath::unity_engine::forward_vector(angles);
for (const auto& [result, etalon] :
std::views::zip(dir_vector.as_array(), (-omath::unity_engine::k_abs_forward).as_array()))
EXPECT_NEAR(result, etalon, 0.0001f);
}
TEST(unit_test_unity_engine, look_at_left)
{
const auto angles = omath::unity_engine::CameraTrait::calc_look_at_angle({}, -omath::unity_engine::k_abs_right);
// ReSharper disable once CppTooWideScopeInitStatement
const auto dir_vector = omath::unity_engine::forward_vector(angles);
for (const auto& [result, etalon] :
std::views::zip(dir_vector.as_array(), (-omath::unity_engine::k_abs_right).as_array()))
EXPECT_NEAR(result, etalon, 0.0001f);
}
TEST(unit_test_unity_engine, look_at_down)
{
const auto angles = omath::unity_engine::CameraTrait::calc_look_at_angle({}, -omath::unity_engine::k_abs_up);
// ReSharper disable once CppTooWideScopeInitStatement
const auto dir_vector = omath::unity_engine::forward_vector(angles);
for (const auto& [result, etalon] :
std::views::zip(dir_vector.as_array(), (-omath::unity_engine::k_abs_up).as_array()))
EXPECT_NEAR(result, etalon, 0.0001f);
}

View File

@@ -7,6 +7,7 @@
#include <omath/engines/unreal_engine/formulas.hpp>
#include <print>
#include <random>
#include <ranges>
TEST(unit_test_unreal_engine, ForwardVector)
{
@@ -361,4 +362,59 @@ TEST(unit_test_unreal_engine, ConstexprConversions)
static_assert(cm == 100000.0, "units_to_centimeters constexpr failed");
static_assert(m == 1000.0, "units_to_meters constexpr failed");
static_assert(km == 1.0, "units_to_kilometers constexpr failed");
}
TEST(unit_test_unreal_engine, look_at_forward)
{
const auto angles = omath::unreal_engine::CameraTrait::calc_look_at_angle({}, omath::unreal_engine::k_abs_forward);
// ReSharper disable once CppTooWideScopeInitStatement
const auto dir_vector = omath::unreal_engine::forward_vector(angles);
for (const auto& [result, etalon] : std::views::zip(dir_vector.as_array(), omath::unreal_engine::k_abs_forward.as_array()))
EXPECT_NEAR(result, etalon, 0.0001f);
}
TEST(unit_test_unreal_engine, look_at_right)
{
const auto angles = omath::unreal_engine::CameraTrait::calc_look_at_angle({}, omath::unreal_engine::k_abs_right);
// ReSharper disable once CppTooWideScopeInitStatement
const auto dir_vector = omath::unreal_engine::forward_vector(angles);
for (const auto& [result, etalon] : std::views::zip(dir_vector.as_array(), omath::unreal_engine::k_abs_right.as_array()))
EXPECT_NEAR(result, etalon, 0.0001f);
}
TEST(unit_test_unreal_engine, look_at_up)
{
const auto angles = omath::unreal_engine::CameraTrait::calc_look_at_angle({}, omath::unreal_engine::k_abs_right);
// ReSharper disable once CppTooWideScopeInitStatement
const auto dir_vector = omath::unreal_engine::forward_vector(angles);
for (const auto& [result, etalon] : std::views::zip(dir_vector.as_array(), omath::unreal_engine::k_abs_right.as_array()))
EXPECT_NEAR(result, etalon, 0.0001f);
}
TEST(unit_test_unreal_engine, look_at_back)
{
const auto angles = omath::unreal_engine::CameraTrait::calc_look_at_angle({}, -omath::unreal_engine::k_abs_forward);
// ReSharper disable once CppTooWideScopeInitStatement
const auto dir_vector = omath::unreal_engine::forward_vector(angles);
for (const auto& [result, etalon] : std::views::zip(dir_vector.as_array(), (-omath::unreal_engine::k_abs_forward).as_array()))
EXPECT_NEAR(result, etalon, 0.0001f);
}
TEST(unit_test_unreal_engine, look_at_left)
{
const auto angles = omath::unreal_engine::CameraTrait::calc_look_at_angle({}, -omath::unreal_engine::k_abs_right);
// ReSharper disable once CppTooWideScopeInitStatement
const auto dir_vector = omath::unreal_engine::forward_vector(angles);
for (const auto& [result, etalon] : std::views::zip(dir_vector.as_array(), (-omath::unreal_engine::k_abs_right).as_array()))
EXPECT_NEAR(result, etalon, 0.0001f);
}
TEST(unit_test_unreal_engine, look_at_down)
{
const auto angles = omath::unreal_engine::CameraTrait::calc_look_at_angle({}, -omath::unreal_engine::k_abs_up);
// ReSharper disable once CppTooWideScopeInitStatement
const auto dir_vector = omath::unreal_engine::forward_vector(angles);
for (const auto& [result, etalon] : std::views::zip(dir_vector.as_array(), (-omath::unreal_engine::k_abs_up).as_array()))
EXPECT_NEAR(result, etalon, 0.0001f);
}