# Best Practices Guidelines for using OMath effectively and avoiding common pitfalls. --- ## Code Organization ### Use Type Aliases Define clear type aliases for commonly used types: ```cpp // Good: Clear and concise using Vec3f = omath::Vector3; using Vec2f = omath::Vector2; using Mat4 = omath::Mat4X4; Vec3f position{1.0f, 2.0f, 3.0f}; ``` ```cpp // Avoid: Verbose and repetitive omath::Vector3 position{1.0f, 2.0f, 3.0f}; omath::Vector3 velocity{0.0f, 0.0f, 0.0f}; ``` ### Namespace Usage Be selective with `using namespace`: ```cpp // Good: Specific namespace for your engine using namespace omath::source_engine; // Good: Import specific types using omath::Vector3; using omath::Vector2; // Avoid: Too broad using namespace omath; // Imports everything ``` ### Include What You Use ```cpp // Good: Include specific headers #include #include // Okay for development #include // Production: Include only what you need // to reduce compile times ``` --- ## Error Handling ### Always Check Optional Results ```cpp // Good: Check before using if (auto screen = camera.world_to_screen(world_pos)) { draw_at(screen->x, screen->y); } else { // Handle point not visible } // Bad: Unchecked access can crash auto screen = camera.world_to_screen(world_pos); draw_at(screen->x, screen->y); // Undefined behavior if nullopt! ``` ### Handle Expected Errors ```cpp // Good: Handle error case if (auto angle = v1.angle_between(v2)) { use_angle(*angle); } else { switch (angle.error()) { case Vector3Error::IMPOSSIBLE_BETWEEN_ANGLE: // Handle zero-length vector break; } } // Bad: Assume success auto angle = v1.angle_between(v2); use_angle(*angle); // Throws if error! ``` ### Validate Inputs ```cpp // Good: Validate before expensive operations bool is_valid_projectile(const Projectile& proj) { return proj.speed > 0.0f && std::isfinite(proj.speed) && std::isfinite(proj.origin.length()); } if (is_valid_projectile(proj) && is_valid_target(target)) { auto aim = engine.maybe_calculate_aim_point(proj, target); } ``` --- ## Performance ### Use constexpr When Possible ```cpp // Good: Computed at compile time constexpr Vector3 gravity{0.0f, 0.0f, -9.81f}; constexpr float max_range = 1000.0f; constexpr float max_range_sq = max_range * max_range; // Use in runtime calculations if (position.length_sqr() < max_range_sq) { // ... } ``` ### Prefer Squared Distance ```cpp // Good: Avoids expensive sqrt constexpr float max_dist_sq = 100.0f * 100.0f; for (const auto& entity : entities) { if (entity.pos.distance_to_sqr(player_pos) < max_dist_sq) { // Process nearby entity } } // Avoid: Unnecessary sqrt calls constexpr float max_dist = 100.0f; for (const auto& entity : entities) { if (entity.pos.distance_to(player_pos) < max_dist) { // More expensive } } ``` ### Cache Expensive Calculations ```cpp // Good: Update camera once per frame void update_frame() { camera.update(current_position, current_angles); // All projections use cached matrices for (const auto& entity : entities) { if (auto screen = camera.world_to_screen(entity.pos)) { draw_entity(screen->x, screen->y); } } } // Bad: Camera recreated each call for (const auto& entity : entities) { Camera cam(pos, angles, viewport, fov, near, far); // Expensive! auto screen = cam.world_to_screen(entity.pos); } ``` ### Choose the Right Engine ```cpp // Good: Use AVX2 when available #ifdef __AVX2__ using Engine = ProjPredEngineAVX2; #else using Engine = ProjPredEngineLegacy; #endif Engine prediction_engine; // Or runtime detection Engine* create_best_engine() { if (cpu_supports_avx2()) { return new ProjPredEngineAVX2(); } return new ProjPredEngineLegacy(); } ``` ### Minimize Allocations ```cpp // Good: Reuse vectors std::vector> positions; positions.reserve(expected_count); // In loop positions.clear(); // Doesn't deallocate for (...) { positions.push_back(compute_position()); } // Bad: Allocate every time for (...) { std::vector> positions; // Allocates each iteration // ... } ``` --- ## Type Safety ### Use Strong Angle Types ```cpp // Good: Type-safe angles PitchAngle pitch = PitchAngle::from_degrees(45.0f); YawAngle yaw = YawAngle::from_degrees(90.0f); ViewAngles angles{pitch, yaw, RollAngle::from_degrees(0.0f)}; // Bad: Raw floats lose safety float pitch = 45.0f; // No range checking float yaw = 90.0f; // Can go out of bounds ``` ### Match Engine Types ```cpp // Good: Use matching types from same engine using namespace omath::source_engine; Camera camera = /* ... */; ViewAngles angles = /* ... */; // Bad: Mixing engine types using UnityCamera = omath::unity_engine::Camera; using SourceAngles = omath::source_engine::ViewAngles; UnityCamera camera{pos, SourceAngles{}, ...}; // May cause issues! ``` ### Template Type Parameters ```cpp // Good: Explicit and clear Vector3 position; Vector3 high_precision_pos; // Okay: Use default float Vector3<> position; // Defaults to float // Avoid: Mixing types unintentionally Vector3 a; Vector3 b; auto result = a + b; // Type mismatch! ``` --- ## Testing & Validation ### Test Edge Cases ```cpp void test_projection() { Camera camera = setup_camera(); // Test normal case assert(camera.world_to_screen({100, 100, 100}).has_value()); // Test edge cases assert(!camera.world_to_screen({0, 0, -100}).has_value()); // Behind assert(!camera.world_to_screen({1e10, 0, 0}).has_value()); // Too far // Test boundaries Vector3 at_near{0, 0, camera.near_plane() + 0.1f}; assert(camera.world_to_screen(at_near).has_value()); } ``` ### Validate Assumptions ```cpp void validate_game_data() { // Validate FOV float fov = read_game_fov(); assert(fov > 1.0f && fov < 179.0f); // Validate positions Vector3 pos = read_player_position(); assert(std::isfinite(pos.x)); assert(std::isfinite(pos.y)); assert(std::isfinite(pos.z)); // Validate viewport ViewPort vp = read_viewport(); assert(vp.width > 0 && vp.height > 0); } ``` ### Use Assertions ```cpp // Good: Catch errors early in development void shoot_projectile(const Projectile& proj) { assert(proj.speed > 0.0f && "Projectile speed must be positive"); assert(std::isfinite(proj.origin.length()) && "Invalid projectile origin"); // Continue with logic } // Add debug-only checks #ifndef NDEBUG if (!is_valid_input(data)) { std::cerr << "Warning: Invalid input detected\n"; } #endif ``` --- ## Memory & Resources ### RAII for Resources ```cpp // Good: Automatic cleanup class GameOverlay { Camera camera_; std::vector entities_; public: GameOverlay(/* ... */) : camera_(/* ... */) { entities_.reserve(1000); } // Resources cleaned up automatically ~GameOverlay() = default; }; ``` ### Avoid Unnecessary Copies ```cpp // Good: Pass by const reference void draw_entities(const std::vector>& positions) { for (const auto& pos : positions) { // Process position } } // Bad: Copies entire vector void draw_entities(std::vector> positions) { // Expensive copy! } // Good: Move when transferring ownership std::vector> compute_positions(); auto positions = compute_positions(); // Move, not copy ``` ### Use Structured Bindings ```cpp // Good: Clear and concise if (auto [success, screen_pos] = try_project(world_pos); success) { draw_at(screen_pos.x, screen_pos.y); } // Good: Decompose results auto [x, y, z] = position.as_tuple(); ``` --- ## Documentation ### Document Assumptions ```cpp // Good: Clear documentation /** * Projects world position to screen space. * * @param world_pos Position in world coordinates (meters) * @return Screen position if visible, nullopt if behind camera or out of view * * @note Assumes camera.update() was called this frame * @note Screen coordinates are in viewport space [0, width] x [0, height] */ std::optional> project(const Vector3& world_pos); ``` ### Explain Non-Obvious Code ```cpp // Good: Explain the math // Use squared distance to avoid expensive sqrt // max_range = 100.0 → max_range_sq = 10000.0 constexpr float max_range_sq = 100.0f * 100.0f; if (dist_sq < max_range_sq) { // Entity is in range } // Explain engine-specific quirks // Source Engine uses Z-up coordinates, but angles are in degrees // Pitch: [-89, 89], Yaw: [-180, 180], Roll: [-180, 180] ViewAngles angles{pitch, yaw, roll}; ``` --- ## Debugging ### Add Debug Visualization ```cpp #ifndef NDEBUG void debug_draw_projection() { // Draw camera frustum draw_frustum(camera); // Draw world axes draw_line({0,0,0}, {100,0,0}, Color::Red); // X draw_line({0,0,0}, {0,100,0}, Color::Green); // Y draw_line({0,0,0}, {0,0,100}, Color::Blue); // Z // Draw projected points for (const auto& entity : entities) { if (auto screen = camera.world_to_screen(entity.pos)) { draw_cross(screen->x, screen->y); } } } #endif ``` ### Log Important Values ```cpp void debug_projection_failure(const Vector3& pos) { std::cerr << "Projection failed for position: " << pos.x << ", " << pos.y << ", " << pos.z << "\n"; auto view_matrix = camera.get_view_matrix(); std::cerr << "View matrix:\n"; // Print matrix... std::cerr << "Camera position: " << camera.position().x << ", " << camera.position().y << ", " << camera.position().z << "\n"; } ``` ### Use Debug Builds ```cmake # CMakeLists.txt if(CMAKE_BUILD_TYPE STREQUAL "Debug") target_compile_definitions(your_target PRIVATE DEBUG_PROJECTION=1 VALIDATE_INPUTS=1 ) endif() ``` ```cpp #ifdef DEBUG_PROJECTION std::cout << "Projecting: " << world_pos << "\n"; #endif #ifdef VALIDATE_INPUTS assert(std::isfinite(world_pos.length())); #endif ``` --- ## Platform Considerations ### Cross-Platform Code ```cpp // Good: Platform-agnostic constexpr float PI = 3.14159265359f; // Avoid: Platform-specific #ifdef _WIN32 // Windows-only code #endif ``` ### Handle Different Compilers ```cpp // Good: Compiler-agnostic #if defined(_MSC_VER) // MSVC-specific #elif defined(__GNUC__) // GCC/Clang-specific #endif // Use OMath's built-in compatibility // It handles compiler differences automatically ``` --- ## Summary **Key principles:** 1. **Safety first**: Always check optional/expected results 2. **Performance matters**: Use constexpr, avoid allocations, cache results 3. **Type safety**: Use strong types, match engine types 4. **Clear code**: Use aliases, document assumptions, explain non-obvious logic 5. **Test thoroughly**: Validate inputs, test edge cases, add assertions 6. **Debug effectively**: Add visualization, log values, use debug builds --- ## See Also - [Troubleshooting Guide](troubleshooting.md) - [FAQ](faq.md) - [API Overview](api_overview.md) - [Tutorials](tutorials.md) --- *Last updated: 1 Nov 2025*