Add comprehensive documentation improvements

Co-authored-by: orange-cpp <59374393+orange-cpp@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot]
2025-11-01 09:51:38 +00:00
parent b8ed0bd5a5
commit 0b89c1d36d
7 changed files with 3078 additions and 2 deletions

525
docs/troubleshooting.md Normal file
View File

@@ -0,0 +1,525 @@
# 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<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.
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<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:
```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<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 |
```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<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<float>{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<float>{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<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;
```
2. **Wrong gravity vector**
```cpp
// Source Engine: Z-up
proj.gravity = Vector3<float>{0, 0, -9.81f};
// Unity: Y-up
proj.gravity = Vector3<float>{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<uint8_t> 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<float> 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<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
```
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
$<$<CONFIG:Release>:-O3>
$<$<CONFIG:Release>:-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*