# Troubleshooting Guide 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: ```cmake 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: ```bash # vcpkg vcpkg list | grep omath # Check if files exist ls /path/to/vcpkg/installed/x64-linux/include/omath ``` In CMakeLists.txt: ```cmake 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: ```cmake 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: ```cpp // 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 behind = camera_pos - Vector3{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. 2. **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); ``` 3. **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); ``` 4. **Uninitialized camera** ```cpp // Make sure camera is properly initialized camera.update(current_position, current_angles); ``` **Debugging:** ```cpp Vector3 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: ```cpp // 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: ```cpp // Define custom angle with wrapping auto angle = Angle::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 | ```cpp // 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: ```cpp // Example: swap Y and Z for Y-up to Z-up conversion Vector3 convert_y_up_to_z_up(const Vector3& pos) { return Vector3{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. 2. **Zero projectile speed** ```cpp Projectile proj; proj.speed = 0.0f; // Invalid! // Returns nullopt ``` **Fix:** Ensure `proj.speed > 0`. 3. **Invalid positions** ```cpp // NaN or infinite values target.position = Vector3{NAN, 0, 0}; // Returns nullopt ``` **Fix:** Validate all input values are finite. 4. **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:** ```cpp 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; ``` 2. **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}; ``` 3. **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 ?? ?? ?? ??"}; ``` 2. **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"}; ``` 3. **Searching wrong memory region** ```cpp // Make sure you're scanning the right memory std::vector code_section = get_code_section(); auto result = pattern_scan(code_section, pattern); ``` 4. **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 ??"}; ``` 2. **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 } } ``` 3. **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:** ```cpp Vector3 zero{0, 0, 0}; auto result = zero.normalized(); // Returns {0, 0, 0} ``` This is **intentional** to avoid NaN. Check vector length first: ```cpp 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:** ```cpp 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 x{1, 0, 0}; Vector3 y{0, 1, 0}; auto z = x.cross(y); // Should be {0, 0, 1} in right-handed system ``` 2. **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 $<$:-O3> $<$:-march=native> ) ``` 2. **Use AVX2 engine** ```cpp // Instead of ProjPredEngineLegacy engine; // Use ProjPredEngineAVX2 engine; ``` 3. **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) { /* ... */ } } ``` 4. **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](api_overview.md), [Tutorials](tutorials.md) 2. **Search GitHub issues**: [Issues page](https://github.com/orange-cpp/omath/issues) 3. **Ask on Discord**: [Join community](https://discord.gg/eDgdaWbqwZ) 4. **Open a new issue**: Include: - OMath version - Compiler and version - Minimal reproducible example - What you expected vs what happened --- *Last updated: 1 Nov 2025*