Skip to content

Latest commit

 

History

1 Commit

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Tiny Fast Math

Português (Brasil)

Tiny Fast Math (tinyfm) is a small C++17 math library for real-time graphics, simulations, and games. It is distributed as a single public header, src/tinyfm.h, and exposed by CMake as the tinyfm INTERFACE target. You can integrate it with CMake or simply download tinyfm.h and place it in your project's include path if you only need the library header.

The API lives in the tfm namespace and includes:

  • integer and floating-point vectors: vec2i, vec3i, vec4i, vec2f, vec3f, vec4f
  • quaternions: quatf
  • matrices: mat3f, mat4f
  • common operations such as dot, cross, length, normalize, distance, project, reflect, lerp, mix, min, max, clamp, transpose, inverse, and transformation helpers
  • camera and projection helpers, including perspective, orthographic, frustum, look-at, and look-to matrix builders
  • optional code paths that use intrinsics when TFM_FORCE_INTRINSICS is defined on supported SSE/AVX or NEON platforms

Requirements

  • C++17-compatible compiler
  • CMake 3.14 or newer
  • Git, when cloning the repository or using CMake FetchContent

The library target itself is header-only. Tests, benchmarks, and graphics examples have additional dependencies that are downloaded or detected only when those components are enabled.

Repository Layout

src/tinyfm.h                 Public single-header library
src/test/                    GoogleTest-based scalar and SIMD tests
src/benchmark/               Google Benchmark comparisons
src/examples/                Cube transformation examples for graphics APIs
3rdparty/                    CMake integration for optional dependencies
CMakeLists.txt               Root CMake project

Downloading the Repository

git clone https://github.com/arabasso/tinyfm.git
cd tinyfm

No Git submodules are required. Optional dependencies are handled by CMake FetchContent when the corresponding project options are enabled.

Minimal Usage

#include <tinyfm.h>

int main() {
    tfm::vec3f position(1.0f, 2.0f, 3.0f);
    tfm::vec3f up = tfm::vec3f::unit_y();
    float height = tfm::dot(position, up);

    tfm::mat4f model = tfm::mat4f::identity()
        .translate(0.0f, 1.0f, 0.0f)
        .rotate_y(tfm::radians(45.0f));
}

The public header includes all standard library headers required by its API, so it can be included directly.

Model-View-Projection Examples

TinyFM follows the same composition convention used by the graphics examples in this repository: build the model, view, and projection matrices separately, then compose them as model * view * projection.

Perspective projection with right-handed coordinates and a zero-to-one clip depth range:

#include <tinyfm.h>

tfm::mat4f make_perspective_mvp(float aspect_ratio) {
    const tfm::mat4f model = tfm::mat4f::identity()
        .translate(0.0f, 0.0f, 0.0f)
        .rotate_y(tfm::radians(45.0f));

    const tfm::mat4f view = tfm::mat4f::look_at_rh(
        tfm::vec3f(0.0f, 0.0f, 5.0f),
        tfm::vec3f(0.0f, 0.0f, 0.0f),
        tfm::vec3f::unit_y());

    const tfm::mat4f projection =
        tfm::mat4f::perspective_fov_rh_zo(tfm::radians(60.0f), aspect_ratio, 0.1f, 100.0f);

    return model * view * projection;
}

Orthographic projection with right-handed coordinates and a negative-one-to-one clip depth range:

#include <tinyfm.h>

tfm::mat4f make_orthographic_mvp(float width, float height) {
    const tfm::mat4f model = tfm::mat4f::identity()
        .translate(0.0f, 0.0f, 0.0f);

    const tfm::mat4f view = tfm::mat4f::look_at_rh(
        tfm::vec3f(0.0f, 0.0f, 5.0f),
        tfm::vec3f(0.0f, 0.0f, 0.0f),
        tfm::vec3f::unit_y());

    const tfm::mat4f projection =
        tfm::mat4f::ortho_rh_no(width, height, 0.1f, 100.0f);

    return model * view * projection;
}

For interactive scenes, a small free-look camera can own the view and projection state and expose a reusable view-projection matrix:

#include <tinyfm.h>

class freelook_camera {
public:
    tfm::vec3f position;
    tfm::vec3f front = tfm::vec3f(0.0f, 0.0f, 1.0f);
    tfm::vec3f up{};
    tfm::vec3f right{};
    tfm::vec3f world_up = tfm::vec3f(0.0f, 1.0f, 0.0f);

    float yaw;
    float pitch;
    float movement_speed = 2.5f;
    float mouse_sensitivity = 0.05f;
    float zoom = 45.0f;
    float fov;
    float znear;
    float zfar;
    float zclip_range;
    float aspect_ratio;

    freelook_camera(float x, float y, float z, float pitch = 0.0f, float yaw = -90.0f, float movement_speed = 2.5f, float mouse_sensitivity = 0.05f, float fov = 45.0f, float znear = 0.1f, float zfar = 100.0f, float aspect_ratio = 16.0f / 9.0f)
        : position(tfm::vec3f(x, y, z)), pitch(pitch), yaw(yaw), movement_speed(movement_speed), mouse_sensitivity(mouse_sensitivity), fov(fov), znear(znear), zfar(zfar), aspect_ratio(aspect_ratio), zclip_range(zfar - znear) {
        update_vectors();
    }

    void move_dxy(int dx, int dy, float max_pitch = 89.0f) {
        yaw += dx * mouse_sensitivity;
        pitch -= dy * mouse_sensitivity;

        if (pitch > max_pitch) pitch = max_pitch;
        if (pitch < -max_pitch) pitch = -max_pitch;

        update_vectors();
    }

    void move_front(float frame_time) { position += front * (frame_time * movement_speed); }
    void move_back(float frame_time) { position -= front * (frame_time * movement_speed); }
    void move_left(float frame_time) { position -= right * (frame_time * movement_speed); }
    void move_right(float frame_time) { position += right * (frame_time * movement_speed); }

    tfm::mat4f view_matrix() const {
        return tfm::mat4f::look_at_rh(position, position + front, up);
    }

    tfm::mat4f projection_matrix() const {
        return tfm::mat4f::perspective_fov_rh_zo(tfm::radians(fov), aspect_ratio, znear, zfar);
    }

    tfm::mat4f view_projection_matrix() const {
        return projection_matrix() * view_matrix();
    }

private:
    void update_vectors() {
        const tfm::vec3f direction(
            std::cos(tfm::radians(yaw)) * std::cos(tfm::radians(pitch)),
            std::sin(tfm::radians(pitch)),
            std::sin(tfm::radians(yaw)) * std::cos(tfm::radians(pitch)));

        front = direction.normalize();
        right = tfm::cross(front, world_up).normalize();
        up = tfm::cross(right, front).normalize();
    }
};

tfm::mat4f make_camera_mvp(float aspect_ratio, float dt, int mouse_dx, int mouse_dy) {
    freelook_camera camera(0.0f, 0.0f, 5.0f, 0.0f, -90.0f, 2.5f, 0.05f, 45.0f, 0.1f, 100.0f, aspect_ratio);
    camera.move_dxy(mouse_dx, mouse_dy);
    camera.move_front(dt);

    const tfm::mat4f model = tfm::mat4f::identity()
        .rotate_y(tfm::radians(45.0f));

    return model * camera.view_projection_matrix();
}

The suffixes describe both the handedness and the normalized depth range. Use rh_zo for right-handed projections that target zero-to-one clip depth, which is the usual convention for Direct3D and Vulkan-style pipelines. Use rh_no for right-handed projections that target negative-one-to-one clip depth, which is OpenGL's default convention. If a backend, shader, viewport transform, or extension changes the expected clip-space depth range, choose the matching projection variant; mixing zo and no typically results in incorrect depth testing, clipping, or object placement along the Z-axis.

Using TinyFM with FetchContent

When TinyFM is fetched as a dependency, tests, benchmarks, and examples are disabled by default. No explicit cache overrides are needed; fetch the repository and link against the tinyfm target.

cmake_minimum_required(VERSION 3.14)

project(my_app LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

include(FetchContent)

FetchContent_Declare(
    tinyfm
    GIT_REPOSITORY https://github.com/arabasso/tinyfm.git
    GIT_TAG master
    GIT_SHALLOW TRUE
)

FetchContent_MakeAvailable(tinyfm)

add_executable(my_app src/main.cpp)
target_link_libraries(my_app PRIVATE tinyfm)

# Optional: compile the intrinsic code paths guarded by tinyfm.h.
# target_compile_definitions(my_app PRIVATE TFM_FORCE_INTRINSICS)

The target name is tinyfm, not tinyfm::tinyfm. The target adds the public include directory and CPU-specific compiler options through CMake INTERFACE properties.

CMake Options

Option Default Effect
ENABLE_TESTS ON for the root project; OFF as a dependency Fetches GoogleTest, builds tinyfm_test_scalar and tinyfm_test_simd, and enables CTest.
ENABLE_BENCHMARKS ON for the root project; OFF as a dependency Fetches Google Benchmark, RTM, GLM, Eigen, and SimpleMath on Windows, then builds tinyfm_benchmark.
ENABLE_EXAMPLES ON for the root project; OFF as a dependency Builds available graphics examples and fetches their dependencies such as fmt, tinywm, GLAD, vk-bootstrap, DirectX-Headers, or Metal C++ support.

To configure the repository directly with the root project's defaults:

cmake -S . -B build
cmake --build build --config Release

When TinyFM is added through FetchContent, its dependency defaults are selected automatically; the three -D...=OFF options are not necessary in the consumer project.

Because tinyfm is header-only, a library-only build mainly validates the CMake configuration and creates the INTERFACE target. It may not produce a standalone library file when tests, benchmarks, and examples are disabled.

Building on Windows

Install:

  • Git
  • CMake
  • Visual Studio 2022 or Visual Studio Build Tools with the Desktop development with C++ workload
  • Windows SDK

From a Developer PowerShell or Developer Command Prompt:

git clone https://github.com/arabasso/tinyfm.git
cd tinyfm

cmake -S . -B build -G "Visual Studio 17 2022" -A x64

cmake --build build --config Release

Graphics examples are enabled by default when building the repository directly. CMake enables Direct3D 11, Direct3D 12, OpenGL, and Vulkan examples only when the matching SDKs or headers are available. Example targets include cube_transform_d3d11, cube_transform_d3d12, cube_transform_gl4, and cube_transform_vk1.

Building on Linux

Install a C++17 compiler, CMake, and Git. Ninja is optional but recommended.

git clone https://github.com/arabasso/tinyfm.git
cd tinyfm

cmake -S . -B build -DCMAKE_BUILD_TYPE=Release

cmake --build build --parallel

Graphics examples are enabled by default when building the repository directly. Install the platform packages needed by the APIs you want to build. OpenGL examples require OpenGL development packages, and Vulkan examples require a Vulkan SDK or development package discoverable by CMake.

Building on macOS

Install Xcode command line tools and CMake:

xcode-select --install

Then build with the Xcode generator:

git clone https://github.com/arabasso/tinyfm.git
cd tinyfm

cmake -S . -B build -G Xcode

cmake --build build --config Release

You can also use Ninja or Unix Makefiles:

cmake -S . -B build -DCMAKE_BUILD_TYPE=Release

cmake --build build --parallel

When examples are enabled on macOS, CMake can build Metal examples and any OpenGL or Vulkan examples whose dependencies are available. Example targets include cube_transform_mtl, cube_transform_gl4, and cube_transform_vk1.

Tests and Benchmarks

Tests and benchmarks are optional. They are enabled by default when building this repository directly and disabled by default when TinyFM is a dependency.

To build and run tests without benchmarks or examples:

cmake -S . -B build-tests -DENABLE_TESTS=ON -DENABLE_BENCHMARKS=OFF -DENABLE_EXAMPLES=OFF
cmake --build build-tests --config Release
ctest --test-dir build-tests -C Release --output-on-failure

To build benchmarks without tests or examples:

cmake -S . -B build-bench -DENABLE_TESTS=OFF -DENABLE_BENCHMARKS=ON -DENABLE_EXAMPLES=OFF
cmake --build build-bench --config Release --target tinyfm_benchmark

SIMD and Intrinsics

The CMake target selects CPU-specific compiler options based on the detected processor:

  • x86/x64: AVX2, FMA, and fast floating-point options where supported
  • ARM64: NEON-compatible fast floating-point options
  • other processors: no explicit SIMD option

The header compiles the intrinsic implementations only when TFM_FORCE_INTRINSICS is defined. Without that definition, the scalar implementation is used.

License

Tiny Fast Math is released under the MIT License. See LICENSE.

About

Tiny Fast math

Resources

Stars

5 stars

Watchers

0 watching

Forks

Releases

Contributors

Languages