Building & CMake

Easy3D / Guide / Building & CMake

Easy3D is a normal compiled STATIC CMake library (easy3d, alias easy3d::easy3d), built with C++23 and CMake ≥ 3.20. The one interesting part of the build is how it resolves CNA linkage — this page covers all three modes, every option, and which APIs need CNA linked at runtime.

Why linkage matters at all

CNA's math types (Vector3, Matrix, …) are declared in CNA's headers but defined in CNA's compiled .cpp files. This has two consequences:

Building full CNA pulls in heavy dependencies (SHARP_RUNTIME, SDL3, ffmpeg, a graphics backend), so Easy3D keeps its default build free of them and resolves CNA linkage in one of three ways.

The three CNA linkage modes

1. Parent project provides the CNA target (the game path)

If a parent project already defines the CNA target before adding Easy3D — the normal setup for a game that does add_subdirectory(../cna) first — Easy3D detects the existing target and links it automatically. No option needed:

add_subdirectory(../cna       cna)        # defines the CNA target (+ chosen backend)
add_subdirectory(../easy-3d   easy-3d)    # detects CNA, links it automatically

add_executable(galaxy_eggbert src/main.cpp)
target_link_libraries(galaxy_eggbert PRIVATE CNA easy3d)

Because the game already selects CNA's backend and options, Easy3D does not need EASY3D_LINK_CNA here — it simply links the CNA target the game created. This is the path games use.

2. Standalone with -DEASY3D_LINK_CNA=ON

When building Easy3D by itself but wanting cameras to actually run (example + full test suite), Easy3D builds CNA itself from EASY3D_CNA_DIR (default ../cna), selecting a graphics backend via EASY3D_CNA_BACKEND (default EASY_GL) and disabling CNA's own demos/tests:

cmake -S . -B build -DEASY3D_LINK_CNA=ON          # backend defaults to EASY_GL
cmake --build build
ctest --test-dir build                            # runs all tests incl. camera, batches, cube_mesh

If there is no CNA CMake project at EASY3D_CNA_DIR, configuration fails with a clear error; fix it with -DEASY3D_CNA_DIR=/path/to/cna.

3. Default: headers only

Otherwise Easy3D compiles against CNA's headers and leaves linking to the consuming application. The default build stays light and self-contained: it produces the easy3d library plus the CNA-free tests, and requires nothing beyond a compiler, CMake, and CNA's headers.

cmake -S . -B build
cmake --build build
ctest --test-dir build      # basics + texture_atlas
When CNA is linked (mode 1 or 2), Easy3D defines EASY3D_HAS_CNA_LINK as a PUBLIC compile definition, and the camera example and the camera/batch/cube-mesh tests are built as runnable executables. Your own code can #ifdef EASY3D_HAS_CNA_LINK if it needs to know.

CMake options

OptionDefaultMeaning
EASY3D_BUILD_EXAMPLESONBuild the examples/ targets (the camera example needs CNA linked).
EASY3D_BUILD_TESTSONBuild the tests/ targets and register them with CTest.
EASY3D_LINK_CNAOFFStandalone only: build CNA from EASY3D_CNA_DIR and link it. Ignored (unnecessary) when a parent project already provides the CNA target.
EASY3D_CNA_DIR../cnaPath to the CNA repository (provides headers under <dir>/include).
EASY3D_CNA_BACKENDEASY_GLCNA graphics backend to enable when Easy3D builds CNA itself: SDL_RENDERER / EASY_GL / BGFX / VULKAN.

Backend selection notes

CNA requires exactly one graphics backend to be selected; EASY3D_CNA_BACKEND picks which one Easy3D enables when it builds CNA itself (mode 2). On Linux, EASY_GL is the lightest, most-tested choice. The value is ignored in mode 1, where the parent project already chose a backend.

ValueBackend
SDL_RENDERERSDL3's built-in 2D renderer backend.
EASY_GLOpenGL backend via easy-gl (default; lightest on Linux).
BGFXbgfx backend.
VULKANVulkan backend.

Which APIs need CNA linked?

Only code that executes CNA math needs the link. Storing CNA types by value inside Easy3D containers is fine without it — but note that your code constructing a Vector3(x, y, z) to pass in already calls a CNA compiled constructor.

APINeeds CNA link at runtime?
VersionString() / VersionNumber()No — completely CNA-free.
TextureAtlas (all of it)No — deliberately CNA-free; deals only in plain pixel rects and UVs.
Camera3D::GetViewMatrix() / GetProjectionMatrix()Yes — calls Matrix::CreateLookAt / Matrix::CreatePerspectiveFieldOfView.
OrbitCamera::ComputePosition() / ApplyTo()Yes — constructs CNA Vector3 values.
FollowCamera::Update() / ApplyTo()Yes — uses CNA Vector3 math (Vector3::Lerp).
BillboardBatch / CubeBatch / DebugDrawThe classes only store data, but constructing the Vector3/Vector2 values you pass in runs CNA constructors — so in practice, yes.
AppendCubeMesh() / BuildCubeMesh()Yes — performs Vector3 arithmetic.
If you hit linker errors like undefined reference to Microsoft::Xna::Framework::Vector3::Vector3(float, float, float), you are running CNA math without linking CNA. Either link the CNA target in your application, or (standalone) configure with -DEASY3D_LINK_CNA=ON.

What the build produces

TargetKindWhen
easy3d (alias easy3d::easy3d)Static libraryAlways.
easy3d_test_basics, easy3d_test_texture_atlasTest executablesWith EASY3D_BUILD_TESTS=ON (always runnable — CNA-free).
easy3d_test_camera, easy3d_test_batches, easy3d_test_cube_meshTest executablesOnly when CNA is linked; otherwise compile-checked as OBJECT libraries (see Testing).
Minimal exampleExample executableWith EASY3D_BUILD_EXAMPLES=ON and CNA linked (see Minimal Example).

Include paths

The easy3d target exports its include/ directory as a PUBLIC include path, so consumers just write:

#include <Easy3D/Easy3D.hpp>        // umbrella header — everything public
#include <Easy3D/Camera3D.hpp>      // or pick individual headers

When CNA is linked, CNA propagates its own PUBLIC include directories (the Microsoft/Xna headers) transitively through easy3d. In the headers-only mode, Easy3D adds EASY3D_CNA_DIR/include to its public include path so CNA headers resolve for consumers too.