Skip to content

Troubleshooting

Solutions to common problems when using OMath.


Build & Compilation Issues

Error: C++20 features not available

Problem: Compiler doesn't support C++20.

Solution: Upgrade your compiler: - GCC: Version 10 or newer - Clang: Version 11 or newer
- MSVC: Visual Studio 2019 16.10 or newer

Set C++20 in CMakeLists.txt:

set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

Error: std::expected not found

Problem: Using C++20 without C++23's std::expected.

Solutions:

  1. Upgrade to C++23 (recommended): cmake set(CMAKE_CXX_STANDARD 23)

  2. Use a backport library: cmake find_package(tl-expected CONFIG REQUIRED) target_link_libraries(your_target PRIVATE tl::expected)

Error: omath/omath.hpp not found

Problem: OMath not installed or not in include path.

Solution:

Check installation:

# vcpkg
vcpkg list | grep omath

# Check if files exist
ls /path/to/vcpkg/installed/x64-linux/include/omath

In CMakeLists.txt:

find_package(omath CONFIG REQUIRED)
target_link_libraries(your_target PRIVATE omath::omath)

Linker errors with AVX2 engine

Problem: Undefined references to AVX2 functions.

Solution:

Enable AVX2 in your build:

if(MSVC)
    target_compile_options(your_target PRIVATE /arch:AVX2)
else()
    target_compile_options(your_target PRIVATE -mavx2)
endif()

Or use the legacy engine instead:

// Use this instead of ProjPredEngineAVX2
ProjPredEngineLegacy engine;

Runtime Issues

world_to_screen() always returns nullopt

Common causes:

  1. Point behind camera cpp // Point is behind the camera Vector3<float> behind = camera_pos - Vector3<float>{0, 0, 100}; auto result = camera.world_to_screen(behind); // Returns nullopt

Fix: Only project points in front of camera. Check Z-coordinate in view space.

  1. Invalid near/far planes ```cpp // Bad: near >= far Camera camera(pos, angles, viewport, fov, 100.0f, 1.0f);

// Good: near < far Camera camera(pos, angles, viewport, fov, 0.1f, 1000.0f); ```

  1. Invalid FOV ```cpp // Bad: FOV out of range auto fov = FieldOfView::from_degrees(0.0f); // Too small auto fov = FieldOfView::from_degrees(180.0f); // Too large

// Good: FOV in valid range auto fov = FieldOfView::from_degrees(90.0f); ```

  1. Uninitialized camera cpp // Make sure camera is properly initialized camera.update(current_position, current_angles);

Debugging:

Vector3<float> world_pos{100, 100, 100};

// Check projection step by step
std::cout << "World pos: " << world_pos.x << ", " 
          << world_pos.y << ", " << world_pos.z << "\n";

auto view_matrix = camera.get_view_matrix();
// Transform to view space manually and check if Z > 0

if (auto screen = camera.world_to_screen(world_pos)) {
    std::cout << "Success: " << screen->x << ", " << screen->y << "\n";
} else {
    std::cout << "Failed - check if point is behind camera\n";
}

Angles wrapping incorrectly

Problem: Angles not normalizing to expected ranges.

Solution:

Use proper angle types:

// Wrong: using raw floats
float pitch = 95.0f;  // Out of valid range!

// Right: using typed angles
auto pitch = PitchAngle::from_degrees(89.0f);  // Clamped to valid range

For custom ranges:

// Define custom angle with wrapping
auto angle = Angle<float, -180.0f, 180.0f, AngleFlags::Normalized>::from_degrees(270.0f);
// Result: -90° (wrapped)

Projection appears mirrored or inverted

Problem: Using wrong engine trait for your game.

Solution:

Different engines have different coordinate systems:

Symptom Likely Issue Fix
Upside down Y-axis inverted Try different engine or negate Y
Left-right flipped Wrong handedness Check engine documentation
Rotated 90° Axis swap Verify engine coordinate system
// Try different engine traits
using namespace omath::source_engine;  // Z-up, left-handed
using namespace omath::unity_engine;   // Y-up, left-handed  
using namespace omath::unreal_engine;  // Z-up, left-handed (different conventions)
using namespace omath::opengl_engine;  // Y-up, right-handed

If still wrong, manually transform coordinates:

// Example: swap Y and Z for Y-up to Z-up conversion
Vector3<float> convert_y_up_to_z_up(const Vector3<float>& pos) {
    return Vector3<float>{pos.x, pos.z, pos.y};
}

Projectile Prediction Issues

maybe_calculate_aim_point() returns nullopt

Common causes:

  1. Target moving too fast ```cpp Target target; target.velocity = Vector3{1000, 0, 0}; // Very fast!

Projectile proj; proj.speed = 500.0f; // Too slow to catch target

// Returns nullopt - projectile can't catch target ```

Fix: Check if projectile speed > target speed in the direction of motion.

  1. Zero projectile speed ```cpp Projectile proj; proj.speed = 0.0f; // Invalid!

// Returns nullopt ```

Fix: Ensure proj.speed > 0.

  1. Invalid positions ```cpp // NaN or infinite values target.position = Vector3{NAN, 0, 0};

// Returns nullopt ```

Fix: Validate all input values are finite.

  1. Target out of range ```cpp // Target very far away float distance = shooter_pos.distance_to(target.position); float max_range = proj.speed * max_flight_time;

if (distance > max_range) { // Will return nullopt } ```

Debugging:

Projectile proj{/* ... */};
Target target{/* ... */};

// Check inputs
assert(proj.speed > 0);
assert(std::isfinite(target.position.length()));
assert(std::isfinite(target.velocity.length()));

// Check if target is reachable
float distance = proj.origin.distance_to(target.position);
float target_speed = target.velocity.length();

std::cout << "Distance: " << distance << "\n";
std::cout << "Projectile speed: " << proj.speed << "\n";
std::cout << "Target speed: " << target_speed << "\n";

if (target_speed >= proj.speed) {
    std::cout << "Target may be too fast!\n";
}

Aim point is inaccurate

Problem: Calculated aim point doesn't hit target.

Possible causes:

  1. Unit mismatch ```cpp // All units must match! proj.speed = 800.0f; // meters per second target.velocity = Vector3{2, 1, 0}; // Must also be m/s!

// If using different units (e.g., game units vs meters), convert: float game_units_to_meters = 0.01905f; // Example for Source target.velocity = game_velocity * game_units_to_meters; ```

  1. Wrong gravity vector ```cpp // Source Engine: Z-up proj.gravity = Vector3{0, 0, -9.81f};

// Unity: Y-up proj.gravity = Vector3{0, -9.81f, 0}; ```

  1. Target velocity not updated cpp // Update target velocity each frame target.velocity = current_velocity; // Not last frame's velocity!

Pattern Scanning Issues

Pattern not found when it should be

Problem: pattern_scan() returns nullopt but pattern exists.

Solutions:

  1. Pattern syntax error ```cpp // Wrong: missing spaces PatternView pattern{"488B05????????"};

// Right: spaces between bytes PatternView pattern{"48 8B 05 ?? ?? ?? ??"}; ```

  1. Pattern too specific ```cpp // May fail if any byte is different PatternView pattern{"48 8B 05 01 02 03 04 48 85 C0"};

// Better: use wildcards for variable bytes PatternView pattern{"48 8B 05 ?? ?? ?? ?? 48 85 C0"}; ```

  1. Searching wrong memory region cpp // Make sure you're scanning the right memory std::vector<uint8_t> code_section = get_code_section(); auto result = pattern_scan(code_section, pattern);

  2. Pattern might have multiple matches cpp // Find all matches instead of just first size_t offset = 0; while (offset < memory.size()) { auto result = pattern_scan( std::span(memory.begin() + offset, memory.end()), pattern ); if (result) { std::cout << "Match at: " << offset + result->offset << "\n"; offset += result->offset + 1; } else { break; } }

Pattern found at wrong location

Problem: Pattern matches unintended code.

Solutions:

  1. Make pattern more specific ```cpp // Too generic PatternView pattern{"48 8B"};

// More specific - include more context PatternView pattern{"48 8B 05 ?? ?? ?? ?? 48 85 C0 74 ??"}; ```

  1. Verify found address ```cpp if (auto result = pattern_scan(memory, pattern)) { // Verify by checking nearby bytes size_t offset = result->offset;

    // Check if instruction makes sense if (memory[offset] == 0x48 && memory[offset + 1] == 0x8B) { // Looks good } } ```

  2. Use multiple patterns ```cpp // Find reference function first auto ref_pattern = PatternView{"E8 ?? ?? ?? ?? 85 C0"}; auto ref_result = pattern_scan(memory, ref_pattern);

// Then search near that location // This provides context validation ```


Vector & Math Issues

normalized() returns zero vector

Problem: Normalizing a zero-length vector.

Behavior:

Vector3<float> zero{0, 0, 0};
auto result = zero.normalized();  // Returns {0, 0, 0}

This is intentional to avoid NaN. Check vector length first:

if (v.length() > 0.001f) {
    auto normalized = v.normalized();
    // Use normalized vector
} else {
    // Handle zero-length case
}

angle_between() returns error

Problem: One or both vectors have zero length.

Solution:

auto angle_result = v1.angle_between(v2);

if (angle_result) {
    float degrees = angle_result->as_degrees();
} else {
    // Handle error - one or both vectors have zero length
    std::cerr << "Cannot compute angle between zero-length vectors\n";
}

Cross product seems wrong

Problem: Unexpected cross product result.

Check: 1. Right-handed system cpp Vector3<float> x{1, 0, 0}; Vector3<float> y{0, 1, 0}; auto z = x.cross(y); // Should be {0, 0, 1} in right-handed system

  1. Order matters cpp auto cross1 = a.cross(b); // {x1, y1, z1} auto cross2 = b.cross(a); // {-x1, -y1, -z1} (opposite direction!)

Performance Issues

Code is slower than expected

Solutions:

  1. Enable optimizations cmake # CMakeLists.txt target_compile_options(your_target PRIVATE $<$<CONFIG:Release>:-O3> $<$<CONFIG:Release>:-march=native> )

  2. Use AVX2 engine ```cpp // Instead of ProjPredEngineLegacy engine;

// Use ProjPredEngineAVX2 engine; ```

  1. Avoid unnecessary operations ```cpp // Bad: recompute every frame for (auto& entity : entities) { float dist = entity.pos.distance_to(player_pos); // Expensive sqrt! if (dist < 100.0f) { / ... / } }

// Good: use squared distance constexpr float max_dist_sq = 100.0f * 100.0f; for (auto& entity : entities) { float dist_sq = entity.pos.distance_to_sqr(player_pos); // No sqrt! if (dist_sq < max_dist_sq) { / ... / } } ```

  1. Cache matrices ```cpp // Bad: recompute matrix every call for (auto& pos : positions) { auto screen = camera.world_to_screen(pos); // Recomputes matrices! }

// Good: matrices are cached in camera automatically camera.update(pos, angles); // Updates matrices once for (auto& pos : positions) { auto screen = camera.world_to_screen(pos); // Uses cached matrices } ```


Getting More Help

If your issue isn't covered here:

  1. Check the docs: API Overview, Tutorials
  2. Search GitHub issues: Issues page
  3. Ask on Discord: Join community
  4. Open a new issue: Include:
  5. OMath version
  6. Compiler and version
  7. Minimal reproducible example
  8. What you expected vs what happened

Last updated: 1 Nov 2025