neraulgraphics 0.4.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. neraulgraphics-0.4.0/.github/workflows/release.yml +141 -0
  2. neraulgraphics-0.4.0/.gitignore +34 -0
  3. neraulgraphics-0.4.0/CHANGELOG.md +104 -0
  4. neraulgraphics-0.4.0/CMakeLists.txt +214 -0
  5. neraulgraphics-0.4.0/LICENSE +202 -0
  6. neraulgraphics-0.4.0/Makefile +36 -0
  7. neraulgraphics-0.4.0/PKG-INFO +167 -0
  8. neraulgraphics-0.4.0/README.md +135 -0
  9. neraulgraphics-0.4.0/RELEASING.md +85 -0
  10. neraulgraphics-0.4.0/cmake/neraulgraphicsConfig.cmake.in +15 -0
  11. neraulgraphics-0.4.0/cmake/version.h.in +12 -0
  12. neraulgraphics-0.4.0/docs/guide.md +334 -0
  13. neraulgraphics-0.4.0/docs/portability.md +73 -0
  14. neraulgraphics-0.4.0/docs/screenshot.png +0 -0
  15. neraulgraphics-0.4.0/examples/mujoco_humanoid.py +34 -0
  16. neraulgraphics-0.4.0/examples/newton_shapes.py +58 -0
  17. neraulgraphics-0.4.0/examples/view_model.py +49 -0
  18. neraulgraphics-0.4.0/include/neraulgraphics/mesh.h +82 -0
  19. neraulgraphics-0.4.0/include/neraulgraphics/renderer.h +145 -0
  20. neraulgraphics-0.4.0/include/neraulgraphics/scene.h +82 -0
  21. neraulgraphics-0.4.0/pyproject.toml +113 -0
  22. neraulgraphics-0.4.0/python/bindings.cpp +447 -0
  23. neraulgraphics-0.4.0/python/neraulgraphics/__init__.py +82 -0
  24. neraulgraphics-0.4.0/python/neraulgraphics/_adapter.py +131 -0
  25. neraulgraphics-0.4.0/python/neraulgraphics/loaders.py +503 -0
  26. neraulgraphics-0.4.0/python/neraulgraphics/mujoco.py +152 -0
  27. neraulgraphics-0.4.0/python/neraulgraphics/newton.py +177 -0
  28. neraulgraphics-0.4.0/python/neraulgraphics/py.typed +0 -0
  29. neraulgraphics-0.4.0/python/neraulgraphics/shapes.py +164 -0
  30. neraulgraphics-0.4.0/src/.gitkeep +0 -0
  31. neraulgraphics-0.4.0/src/backend.h +59 -0
  32. neraulgraphics-0.4.0/src/backend_api.h +91 -0
  33. neraulgraphics-0.4.0/src/bvh.cpp +205 -0
  34. neraulgraphics-0.4.0/src/bvh.h +67 -0
  35. neraulgraphics-0.4.0/src/cpu_backend.cpp +209 -0
  36. neraulgraphics-0.4.0/src/cuda_backend.cu +258 -0
  37. neraulgraphics-0.4.0/src/cuda_backend_stub.cpp +14 -0
  38. neraulgraphics-0.4.0/src/device_scene.h +89 -0
  39. neraulgraphics-0.4.0/src/geom.h +84 -0
  40. neraulgraphics-0.4.0/src/geom_dev.h +182 -0
  41. neraulgraphics-0.4.0/src/instances.cpp +47 -0
  42. neraulgraphics-0.4.0/src/instances.h +48 -0
  43. neraulgraphics-0.4.0/src/main.cpp +284 -0
  44. neraulgraphics-0.4.0/src/mesh.cpp +97 -0
  45. neraulgraphics-0.4.0/src/mesh_build.cpp +197 -0
  46. neraulgraphics-0.4.0/src/mesh_build.h +50 -0
  47. neraulgraphics-0.4.0/src/mesh_dev.h +254 -0
  48. neraulgraphics-0.4.0/src/render_core.h +217 -0
  49. neraulgraphics-0.4.0/src/renderer.cpp +193 -0
  50. neraulgraphics-0.4.0/tests/test_adapters.py +289 -0
  51. neraulgraphics-0.4.0/tests/test_loaders.py +294 -0
  52. neraulgraphics-0.4.0/tests/test_python.py +330 -0
  53. neraulgraphics-0.4.0/tests/test_renderer.cpp +769 -0
  54. neraulgraphics-0.4.0/tests/test_shapes.py +71 -0
@@ -0,0 +1,141 @@
1
+ name: release
2
+
3
+ # Pushing a tag vX.Y.Z builds the source archive and wheels, then publishes them as the
4
+ # GitHub Release for that tag, and to PyPI if that has been switched on (see RELEASING.md).
5
+ # Running it by hand (Actions > release > Run workflow) builds the same files without
6
+ # publishing anything, as a dry run.
7
+ #
8
+ # Wheels: Linux x86_64 carries the CUDA backend (and uses the CPU on a machine with no GPU);
9
+ # macOS, Windows and Linux aarch64 are CPU-only.
10
+ #
11
+ # See RELEASING.md for the steps around it.
12
+
13
+ on:
14
+ push:
15
+ tags: ["v*.*.*"]
16
+ workflow_dispatch:
17
+
18
+ permissions:
19
+ contents: read
20
+
21
+ jobs:
22
+ # Refuses to release unless the tag, CMakeLists.txt and CHANGELOG.md agree.
23
+ check-version:
24
+ runs-on: ubuntu-latest
25
+ outputs:
26
+ version: ${{ steps.version.outputs.version }}
27
+ steps:
28
+ - uses: actions/checkout@v7
29
+
30
+ - id: version
31
+ run: |
32
+ version=$(sed -nE 's/^project\(neraulgraphics VERSION ([0-9]+\.[0-9]+\.[0-9]+).*/\1/p' CMakeLists.txt)
33
+ if [ -z "$version" ]; then
34
+ echo "::error::no version found in CMakeLists.txt"
35
+ exit 1
36
+ fi
37
+ echo "version=$version" >> "$GITHUB_OUTPUT"
38
+
39
+ if [ "$GITHUB_REF_TYPE" = "tag" ] && [ "$GITHUB_REF_NAME" != "v$version" ]; then
40
+ echo "::error::tag $GITHUB_REF_NAME does not match version $version in CMakeLists.txt"
41
+ exit 1
42
+ fi
43
+ if ! grep -q "^## \[$version\]" CHANGELOG.md; then
44
+ echo "::error::CHANGELOG.md has no '## [$version]' section"
45
+ exit 1
46
+ fi
47
+ echo "version $version"
48
+
49
+ sdist:
50
+ needs: check-version
51
+ runs-on: ubuntu-latest
52
+ steps:
53
+ - uses: actions/checkout@v7
54
+ - run: pipx run build --sdist
55
+ - uses: actions/upload-artifact@v7
56
+ with:
57
+ name: sdist
58
+ path: dist/*.tar.gz
59
+
60
+ # Settings live in [tool.cibuildwheel] in pyproject.toml, shared with local builds.
61
+ # Linux x86_64, with the CUDA backend.
62
+ wheels:
63
+ needs: check-version
64
+ runs-on: ubuntu-latest
65
+ steps:
66
+ - uses: actions/checkout@v7
67
+ - uses: pypa/cibuildwheel@v4.3.5
68
+ with:
69
+ output-dir: wheelhouse
70
+ - uses: actions/upload-artifact@v7
71
+ with:
72
+ name: wheels-linux-cuda
73
+ path: wheelhouse/*.whl
74
+
75
+ # Everything without CUDA: the build finds no CUDA compiler and builds the CPU backend only.
76
+ wheels-cpu:
77
+ needs: check-version
78
+ strategy:
79
+ fail-fast: false
80
+ matrix:
81
+ include:
82
+ - { name: macos-arm64, os: macos-latest }
83
+ - { name: windows-amd64, os: windows-latest }
84
+ - { name: linux-aarch64, os: ubuntu-24.04-arm, archs: aarch64 }
85
+ runs-on: ${{ matrix.os }}
86
+ steps:
87
+ - uses: actions/checkout@v7
88
+ - uses: pypa/cibuildwheel@v4.3.5
89
+ env:
90
+ CIBW_ARCHS_LINUX: ${{ matrix.archs }}
91
+ with:
92
+ output-dir: wheelhouse
93
+ - uses: actions/upload-artifact@v7
94
+ with:
95
+ name: wheels-${{ matrix.name }}
96
+ path: wheelhouse/*.whl
97
+
98
+ publish:
99
+ if: github.ref_type == 'tag'
100
+ needs: [check-version, sdist, wheels, wheels-cpu]
101
+ runs-on: ubuntu-latest
102
+ permissions:
103
+ contents: write
104
+ steps:
105
+ - uses: actions/checkout@v7
106
+
107
+ - uses: actions/download-artifact@v8
108
+ with:
109
+ path: dist
110
+ merge-multiple: true
111
+
112
+ - name: Create the release
113
+ env:
114
+ GH_TOKEN: ${{ github.token }}
115
+ VERSION: ${{ needs.check-version.outputs.version }}
116
+ run: |
117
+ # The release notes are this version's section of CHANGELOG.md.
118
+ awk -v v="$VERSION" '
119
+ $0 ~ "^## \\[" v "\\]" { found = 1; next }
120
+ found && /^## \[/ { exit }
121
+ found { print }
122
+ ' CHANGELOG.md > notes.md
123
+ gh release create "$GITHUB_REF_NAME" dist/* \
124
+ --title "neraulgraphics $VERSION" \
125
+ --notes-file notes.md
126
+
127
+ # PyPI, by trusted publishing: no token is stored here. It does nothing until the repository
128
+ # variable PUBLISH_TO_PYPI is set to true, after the one-time setup on pypi.org in RELEASING.md.
129
+ pypi:
130
+ if: github.ref_type == 'tag' && vars.PUBLISH_TO_PYPI == 'true'
131
+ needs: [check-version, sdist, wheels, wheels-cpu, publish]
132
+ runs-on: ubuntu-latest
133
+ environment: pypi
134
+ permissions:
135
+ id-token: write
136
+ steps:
137
+ - uses: actions/download-artifact@v8
138
+ with:
139
+ path: dist
140
+ merge-multiple: true
141
+ - uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,34 @@
1
+ # Allowlist: ignore everything in this subproject by default...
2
+ *
3
+
4
+ # ...but keep descending into directories, so the rules below can reach inside
5
+ # them. Without this, git never looks inside src/ and the !/src/** below can
6
+ # never match.
7
+ !*/
8
+
9
+ # ...and un-ignore only what defines the project.
10
+ !/.gitignore
11
+ !/LICENSE
12
+ !/CMakeLists.txt
13
+ !/Makefile
14
+ !/src/**
15
+ !/include/**
16
+ !/cmake/**
17
+ !/python/**
18
+ !/tests/**
19
+ !/examples/**
20
+ !/.github/**
21
+ !/pyproject.toml
22
+ !/README.md
23
+ !/CHANGELOG.md
24
+ !/RELEASING.md
25
+ !/docs/**
26
+
27
+ # Build trees: re-ignored as whole directories so git doesn't walk them at all.
28
+ /build/
29
+ /cmake-build-*/
30
+ /dist/
31
+ /wheelhouse/
32
+ /.idea/
33
+ __pycache__/
34
+ *.egg-info/
@@ -0,0 +1,104 @@
1
+ # Changelog
2
+
3
+ Each release has a section here. The release workflow refuses to publish a version
4
+ without one, and uses the section as the release notes.
5
+
6
+ Versions follow [semantic versioning](https://semver.org). Before 1.0, a minor
7
+ version (0.x.0) may change the API.
8
+
9
+ ## [0.4.0]
10
+
11
+ Newton and MuJoCo adapters, model loading, textures, and a CPU backend so it runs on any machine.
12
+
13
+ ### Added
14
+ - A CPU backend. The renderer runs on every core of the machine when there is no NVIDIA GPU,
15
+ drawing the same picture as the GPU (the two run the same rendering code). `Renderer` takes
16
+ `device="auto" | "cuda" | "cpu"` (C++: `Device`), `Renderer.device` says which was chosen, and
17
+ `cuda_available()` says whether a GPU is usable. `NERAULGRAPHICS_DEVICE=cpu` overrides `auto`;
18
+ `NERAULGRAPHICS_THREADS` sets the CPU thread count. A machine with no GPU used to raise
19
+ `CudaError`; it now falls back to the CPU.
20
+ - CUDA is optional at build time (`-DNERAULGRAPHICS_CUDA=AUTO|ON|OFF`; AUTO uses it if a CUDA
21
+ compiler is found). Without it the library needs only a C++17 compiler and depends on no CUDA
22
+ library or driver.
23
+ - Wheels for macOS (Apple Silicon), Windows and Linux aarch64, CPU-only, alongside the Linux x86_64
24
+ CUDA wheels. The release workflow can publish to PyPI by trusted publishing, once enabled.
25
+ - `docs/portability.md`: what runs where, and how other GPUs could be supported.
26
+ - `neraulgraphics.newton.NewtonRenderer` and `neraulgraphics.mujoco.MuJoCoRenderer`:
27
+ render a Newton or MuJoCo simulation. Shapes become mesh instances once; each
28
+ `render(state)` only moves them. Sphere, box, capsule, cylinder, cone, ellipsoid,
29
+ plane and mesh shapes, with colours from the model.
30
+ - `neraulgraphics.shapes`: capsule, cylinder, cone, plane and sphere generators, and the
31
+ pose math (`pose_matrices`, `compose_poses`) the adapters use.
32
+ - `examples/` with Newton, MuJoCo and model-viewing scripts, and `mujoco` and `images` extras.
33
+ - Smooth shading and textures. `Mesh` takes optional per-vertex `normals` and `uvs`; a new
34
+ `Texture` (RGBA8, sRGB, bilinear filtered, repeating) can be put on an `Instance` with its
35
+ `texture` index, and multiplies into the material's diffuse colour. `Renderer` takes `textures`.
36
+ - `neraulgraphics.load`, `load_gltf` and `load_obj`: glTF 2.0 (`.gltf` and `.glb`) and
37
+ Wavefront OBJ with materials, colour textures, vertex normals and node transforms, returning a
38
+ `LoadedScene` whose `.renderer()` frames the model and lights it.
39
+ - `Renderer.ambient` (C++: `set_ambient`): light reaching every surface regardless of the lights,
40
+ so faces turned from every light are not black. Defaults to 0; the adapters and loaders
41
+ default to 0.2.
42
+ - Simulator planes draw as a checkerboard ground.
43
+
44
+ ### Changed
45
+ - `make_camera` computes in double precision on the CPU, so a camera is identical on both backends.
46
+ - Python wheel tests now render for real on build machines, on the CPU backend.
47
+
48
+ ### Known limits
49
+ - Textures are not mip-mapped, so distant fine patterns grain until samples accumulate.
50
+ - The CPU backend is roughly 40 to 100 times slower than a recent GPU (at 1280x720 a 16-core CPU takes 17 to 41 ms for 138k-triangle models).
51
+ - glTF: no animation or skinning (bind pose is drawn), normal maps, transparency or Draco.
52
+
53
+ ## [0.3.0]
54
+
55
+ Triangle meshes and dynamic scenes.
56
+
57
+ ### Added
58
+ - Apache-2.0 license.
59
+ - Triangle meshes. `Mesh` is geometry, built into a BVH once; `Instance` places a
60
+ mesh with its own `Transform` and `Material`. The number of instances is not
61
+ capped, unlike spheres. Pass them to `Renderer(..., meshes, instances)`.
62
+ - `Renderer::set_instance_transforms()` (Python: an `(N, 3, 4)` array or a list of
63
+ `Transform`) moves instances without touching the meshes. The tree over
64
+ instances is refitted in place and rebuilt only when it has loosened too far.
65
+ - `make_transform` (position, `(x, y, z, w)` quaternion, scale), `make_box` and
66
+ `make_icosphere`, in C++ and Python.
67
+ - `make raytracer` builds the viewer and links it as `./raytracer`; `make run`
68
+ and `make test` do the obvious. The viewer takes `--instances N` to spawn
69
+ spinning shapes.
70
+
71
+ ### Known limits
72
+ - Triangles are shaded flat, with no vertex normals or textures.
73
+ - Updating instances runs on the CPU: about 9 ms for 10,000 and 90 ms for 100,000.
74
+ - Ray-triangle tests are not watertight along an exact shared edge.
75
+
76
+ ## [0.2.0]
77
+
78
+ The renderer is now a package you can call from Python or C++.
79
+
80
+ ### Added
81
+ - Python package: `pip install`, then `import neraulgraphics`. `Renderer.render()`
82
+ returns a `(height, width, 3)` uint8 numpy array. Vectors are plain tuples,
83
+ lists or numpy arrays. Bad input raises `ValueError`; CUDA failures raise
84
+ `neraulgraphics.CudaError`. Type stubs included.
85
+ - Linux wheels for Python 3.10 and later that need only the NVIDIA driver, not
86
+ the CUDA toolkit, built for every GPU generation from Turing to Blackwell.
87
+ - `neraulgraphics/version.h`, generated from the version in `CMakeLists.txt`.
88
+ - C++ tests, run with `ctest`.
89
+
90
+ ### Changed
91
+ - Renamed everything to `neraulgraphics`: `find_package(neraulgraphics)`, the
92
+ `neraulgraphics::core` target, `#include <neraulgraphics/renderer.h>` and
93
+ `namespace neraulgraphics`. The viewer is now `neraulgraphics-viewer`.
94
+ - The public headers no longer include any CUDA header. Vectors are
95
+ `neraulgraphics::Vec3`, and `make_camera` is computed inside the library, so
96
+ cameras no longer depend on the caller's floating point flags.
97
+ - The `Renderer` floor argument is optional; the default is no floor.
98
+
99
+ ## [0.1.0]
100
+
101
+ - The renderer as a library: a `gpu::Renderer` class that owns its device
102
+ memory, reports errors as exceptions, and rejects bad input before it reaches
103
+ the GPU.
104
+ - CMake install and `find_package(raytracer)` support.
@@ -0,0 +1,214 @@
1
+ cmake_minimum_required(VERSION 3.28)
2
+
3
+ # Must precede project(): with no build type CMake adds no -O flag and GCC defaults to -O0.
4
+ # Only when built on its own; a project that pulls this one in keeps its own build type.
5
+ if (CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR AND NOT CMAKE_BUILD_TYPE
6
+ AND NOT CMAKE_CONFIGURATION_TYPES)
7
+ set(CMAKE_BUILD_TYPE Release CACHE STRING "Build Type" FORCE)
8
+ endif()
9
+
10
+ # 120 = compute capability 12.0 (RTX 50-series). "native" works on CUDA 12.6+.
11
+ # A default only: set before project(), which would otherwise cache its own (75
12
+ # with CUDA 13), so -DCMAKE_CUDA_ARCHITECTURES=... on the command line still wins.
13
+ # A build directory configured before this file existed may hold that 75; reset
14
+ # its cache once (CLion: Tools > CMake > Reset Cache and Reload Project).
15
+ # A pip install from source defaults to "native", the GPU of the machine building it.
16
+ if (NOT DEFINED CMAKE_CUDA_ARCHITECTURES)
17
+ if (DEFINED SKBUILD)
18
+ set(CMAKE_CUDA_ARCHITECTURES native CACHE STRING "CUDA architectures to compile for")
19
+ else()
20
+ set(CMAKE_CUDA_ARCHITECTURES 120 CACHE STRING "CUDA architectures to compile for")
21
+ endif()
22
+ endif()
23
+
24
+ # The one place the version is written. version.h, the find_package version file,
25
+ # the Python package metadata and neraulgraphics.__version__ all read it from here.
26
+ project(neraulgraphics VERSION 0.4.0 LANGUAGES CXX)
27
+
28
+ # CUDA is optional. Without it the library still builds and renders, on the CPU, with nothing but a
29
+ # C++17 compiler. AUTO uses CUDA when a CUDA compiler is found; ON requires it; OFF skips it.
30
+ set(NERAULGRAPHICS_CUDA AUTO CACHE STRING "Build the CUDA (NVIDIA GPU) backend: AUTO, ON or OFF")
31
+ set_property(CACHE NERAULGRAPHICS_CUDA PROPERTY STRINGS AUTO ON OFF)
32
+ set(NERAULGRAPHICS_WITH_CUDA OFF)
33
+ if (NERAULGRAPHICS_CUDA STREQUAL "ON")
34
+ enable_language(CUDA)
35
+ set(NERAULGRAPHICS_WITH_CUDA ON)
36
+ elseif (NERAULGRAPHICS_CUDA STREQUAL "AUTO")
37
+ include(CheckLanguage)
38
+ check_language(CUDA)
39
+ if (CMAKE_CUDA_COMPILER)
40
+ enable_language(CUDA)
41
+ set(NERAULGRAPHICS_WITH_CUDA ON)
42
+ endif()
43
+ endif()
44
+ message(STATUS "neraulgraphics: CUDA backend ${NERAULGRAPHICS_WITH_CUDA}; the CPU backend is always built")
45
+
46
+ set(CMAKE_CXX_STANDARD 17)
47
+ set(CMAKE_CXX_STANDARD_REQUIRED ON)
48
+ set(CMAKE_CUDA_STANDARD 17)
49
+ set(CMAKE_CUDA_STANDARD_REQUIRED ON)
50
+
51
+ # On by default only when this is the top-level project, so a project that uses
52
+ # the library through add_subdirectory or FetchContent never needs SDL2.
53
+ option(NERAULGRAPHICS_BUILD_VIEWER "Build the interactive SDL2 viewer" ${PROJECT_IS_TOP_LEVEL})
54
+ option(NERAULGRAPHICS_BUILD_TESTS "Build the C++ tests (run with ctest)" ${PROJECT_IS_TOP_LEVEL})
55
+ option(NERAULGRAPHICS_INSTALL "Generate install rules and the find_package config" ${PROJECT_IS_TOP_LEVEL})
56
+ option(NERAULGRAPHICS_BUILD_PYTHON "Build the Python module (pip install . sets this)" OFF)
57
+
58
+ # Shared links libcudart.so, which the machine running the program must have.
59
+ # Static bakes the runtime in and needs only the NVIDIA driver, which is what lets
60
+ # a Python wheel run on a machine without the CUDA toolkit.
61
+ set(NERAULGRAPHICS_CUDA_RUNTIME Shared CACHE STRING "How to link the CUDA runtime: Shared or Static")
62
+ set_property(CACHE NERAULGRAPHICS_CUDA_RUNTIME PROPERTY STRINGS Shared Static)
63
+
64
+ find_package(Threads REQUIRED)
65
+ if (NERAULGRAPHICS_WITH_CUDA)
66
+ find_package(CUDAToolkit REQUIRED)
67
+ endif()
68
+
69
+ # ---------------------------------------------------------------- library --
70
+
71
+ # The renderer on its own: no window, no SDL. Consumers link neraulgraphics::core.
72
+ add_library(neraulgraphics_core STATIC
73
+ src/renderer.cpp
74
+ src/cpu_backend.cpp
75
+ src/instances.cpp
76
+ src/bvh.cpp
77
+ src/mesh.cpp
78
+ src/mesh_build.cpp)
79
+ if (NERAULGRAPHICS_WITH_CUDA)
80
+ target_sources(neraulgraphics_core PRIVATE src/cuda_backend.cu)
81
+ else()
82
+ target_sources(neraulgraphics_core PRIVATE src/cuda_backend_stub.cpp)
83
+ endif()
84
+ add_library(neraulgraphics::core ALIAS neraulgraphics_core)
85
+
86
+ configure_file(cmake/version.h.in ${CMAKE_CURRENT_BINARY_DIR}/include/neraulgraphics/version.h
87
+ @ONLY)
88
+
89
+ # The public headers. BASE_DIRS makes these the include path, both here and once
90
+ # installed, so every consumer writes #include <neraulgraphics/renderer.h>.
91
+ # Everything in src/ (the BVH, the rendering core, the backends) stays private to the library.
92
+ target_sources(neraulgraphics_core PUBLIC
93
+ FILE_SET HEADERS
94
+ BASE_DIRS include ${CMAKE_CURRENT_BINARY_DIR}/include
95
+ FILES
96
+ include/neraulgraphics/mesh.h
97
+ include/neraulgraphics/renderer.h
98
+ include/neraulgraphics/scene.h
99
+ ${CMAKE_CURRENT_BINARY_DIR}/include/neraulgraphics/version.h)
100
+
101
+ target_include_directories(neraulgraphics_core PRIVATE src)
102
+
103
+ # PRIVATE: the public headers need nothing from CUDA or threads. A static library still
104
+ # carries them to whoever links it, as link-only dependencies.
105
+ target_link_libraries(neraulgraphics_core PRIVATE Threads::Threads)
106
+ if (NERAULGRAPHICS_WITH_CUDA)
107
+ if (NERAULGRAPHICS_CUDA_RUNTIME STREQUAL "Static")
108
+ target_link_libraries(neraulgraphics_core PRIVATE CUDA::cudart_static)
109
+ else()
110
+ target_link_libraries(neraulgraphics_core PRIVATE CUDA::cudart)
111
+ endif()
112
+ endif()
113
+ target_compile_features(neraulgraphics_core PUBLIC cxx_std_17)
114
+
115
+ # Position-independent so the library can be linked into a shared object, such as
116
+ # the Python extension module.
117
+ set_target_properties(neraulgraphics_core PROPERTIES
118
+ POSITION_INDEPENDENT_CODE ON
119
+ EXPORT_NAME core)
120
+
121
+ target_compile_options(neraulgraphics_core PRIVATE
122
+ $<$<AND:$<CONFIG:Release>,$<COMPILE_LANGUAGE:CUDA>>:-use_fast_math>
123
+ # Prints registers per thread and spill counts; a nonzero stack frame means
124
+ # something fell out to local memory.
125
+ $<$<COMPILE_LANGUAGE:CUDA>:-Xptxas=-v>)
126
+
127
+ # ----------------------------------------------------------------- viewer --
128
+
129
+ if (NERAULGRAPHICS_BUILD_VIEWER)
130
+ find_package(SDL2 REQUIRED)
131
+
132
+ # Links the library exactly the way an outside project would.
133
+ add_executable(neraulgraphics-viewer src/main.cpp)
134
+ target_link_libraries(neraulgraphics-viewer PRIVATE neraulgraphics::core SDL2::SDL2)
135
+
136
+ # The COMPILE_LANGUAGE guard is required: nvcc rejects -march=native and -ffast-math.
137
+ # -ffast-math lets the compiler assume NaN never occurs, which is why the
138
+ # renderer's input checks live in render_gpu.cu, not in anything built here.
139
+ target_compile_options(neraulgraphics-viewer PRIVATE
140
+ $<$<AND:$<CONFIG:Release>,$<COMPILE_LANGUAGE:CXX>>:-march=native -ffast-math>)
141
+ endif()
142
+
143
+ # ------------------------------------------------------------------ tests --
144
+
145
+ if (NERAULGRAPHICS_BUILD_TESTS)
146
+ enable_testing()
147
+ # Plain C++ built with -ffast-math, like the viewer: the checks must survive it.
148
+ add_executable(neraulgraphics-tests tests/test_renderer.cpp)
149
+ target_link_libraries(neraulgraphics-tests PRIVATE neraulgraphics::core)
150
+ # The BVH builder is library-private, but its tests want it directly.
151
+ target_include_directories(neraulgraphics-tests PRIVATE src)
152
+ target_compile_options(neraulgraphics-tests PRIVATE
153
+ $<$<AND:$<CONFIG:Release>,$<COMPILE_LANGUAGE:CXX>>:-ffast-math>)
154
+ add_test(NAME renderer COMMAND neraulgraphics-tests)
155
+ endif()
156
+
157
+ # ----------------------------------------------------------------- python --
158
+
159
+ if (NERAULGRAPHICS_BUILD_PYTHON)
160
+ find_package(Python 3.10 REQUIRED COMPONENTS Interpreter Development.Module
161
+ OPTIONAL_COMPONENTS Development.SABIModule)
162
+ execute_process(COMMAND "${Python_EXECUTABLE}" -m nanobind --cmake_dir
163
+ OUTPUT_STRIP_TRAILING_WHITESPACE OUTPUT_VARIABLE nanobind_ROOT)
164
+ find_package(nanobind CONFIG REQUIRED)
165
+
166
+ # STABLE_ABI: on Python 3.12+ one build serves every later version too.
167
+ nanobind_add_module(_core STABLE_ABI NB_STATIC python/bindings.cpp)
168
+ # __version__ comes from the generated version.h, like everything else.
169
+ target_link_libraries(_core PRIVATE neraulgraphics::core)
170
+ install(TARGETS _core LIBRARY DESTINATION neraulgraphics)
171
+
172
+ # Type stubs, so editors can autocomplete and type-check against the module.
173
+ nanobind_add_stub(_core_stub
174
+ MODULE _core
175
+ OUTPUT _core.pyi
176
+ PYTHON_PATH $<TARGET_FILE_DIR:_core>
177
+ DEPENDS _core)
178
+ install(FILES ${CMAKE_CURRENT_BINARY_DIR}/_core.pyi DESTINATION neraulgraphics)
179
+ endif()
180
+
181
+ # ---------------------------------------------------------------- install --
182
+
183
+ if (NERAULGRAPHICS_INSTALL)
184
+ include(GNUInstallDirs)
185
+ include(CMakePackageConfigHelpers)
186
+
187
+ set(NERAULGRAPHICS_CMAKE_DIR ${CMAKE_INSTALL_LIBDIR}/cmake/neraulgraphics)
188
+
189
+ install(TARGETS neraulgraphics_core
190
+ EXPORT neraulgraphicsTargets
191
+ FILE_SET HEADERS)
192
+
193
+ # Writes the imported target neraulgraphics::core that find_package hands back.
194
+ install(EXPORT neraulgraphicsTargets
195
+ NAMESPACE neraulgraphics::
196
+ DESTINATION ${NERAULGRAPHICS_CMAKE_DIR})
197
+
198
+ configure_package_config_file(cmake/neraulgraphicsConfig.cmake.in
199
+ ${CMAKE_CURRENT_BINARY_DIR}/neraulgraphicsConfig.cmake
200
+ INSTALL_DESTINATION ${NERAULGRAPHICS_CMAKE_DIR})
201
+
202
+ # Before 1.0 a minor version may change the API, so only the same minor matches.
203
+ write_basic_package_version_file(${CMAKE_CURRENT_BINARY_DIR}/neraulgraphicsConfigVersion.cmake
204
+ COMPATIBILITY SameMinorVersion)
205
+
206
+ install(FILES
207
+ ${CMAKE_CURRENT_BINARY_DIR}/neraulgraphicsConfig.cmake
208
+ ${CMAKE_CURRENT_BINARY_DIR}/neraulgraphicsConfigVersion.cmake
209
+ DESTINATION ${NERAULGRAPHICS_CMAKE_DIR})
210
+
211
+ if (NERAULGRAPHICS_BUILD_VIEWER)
212
+ install(TARGETS neraulgraphics-viewer RUNTIME)
213
+ endif()
214
+ endif()