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:
Error: std::expected not found
Problem: Using C++20 without C++23's std::expected.
Solutions:
-
Upgrade to C++23 (recommended):
-
Use a backport library:
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:
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:
Runtime Issues
world_to_screen() always returns nullopt
Common causes:
- Point behind camera
Fix: Only project points in front of camera. Check Z-coordinate in view space.
-
Invalid near/far planes
-
Invalid FOV
-
Uninitialized camera
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:
- Target moving too fast
Fix: Check if projectile speed > target speed in the direction of motion.
- Zero projectile speed
Fix: Ensure proj.speed > 0.
- Invalid positions
Fix: Validate all input values are finite.
- Target out of range
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:
-
Unit mismatch
// All units must match! proj.speed = 800.0f; // meters per second target.velocity = Vector3<float>{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; -
Wrong gravity vector
-
Target velocity not updated
Pattern Scanning Issues
Pattern not found when it should be
Problem: pattern_scan() returns nullopt but pattern exists.
Solutions:
-
Pattern syntax error
-
Pattern too specific
-
Searching wrong memory region
-
Pattern might have multiple matches
// 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:
-
Make pattern more specific
-
Verify found address
-
Use multiple patterns
Vector & Math Issues
normalized() returns zero vector
Problem: Normalizing a zero-length vector.
Behavior:
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
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
- Order matters
Performance Issues
Code is slower than expected
Solutions:
-
Enable optimizations
-
Use AVX2 engine
-
Avoid unnecessary operations
// 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) { /* ... */ } } -
Cache matrices
// 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:
- Check the docs: API Overview, Tutorials
- Search GitHub issues: Issues page
- Ask on Discord: Join community
- Open a new issue: Include:
- OMath version
- Compiler and version
- Minimal reproducible example
- What you expected vs what happened
Last updated: 1 Nov 2025