This commit is contained in:
Gaël committed 2026-10-19 20:59:15 +02:00
commit 87bff164f1
416 files changed
+279512

No files matched your search

+205
View File
@@ -0,0 +1,205 @@
cmake_minimum_required(VERSION 3.20)
project(noita_gpu LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
if(MSVC) # Release keeps its optimisations and writes a PDB (the crash handler of noita_gpu prints a symbolised stack)
add_compile_options($<$<CONFIG:Release>:/Zi>)
add_link_options($<$<CONFIG:Release>:/DEBUG:FULL> $<$<CONFIG:Release>:/OPT:REF> $<$<CONFIG:Release>:/OPT:ICF>)
endif()
add_library(ngcore STATIC
src/core/wak.cpp
src/core/xml.cpp
src/core/materials.cpp)
target_include_directories(ngcore PUBLIC src)
if(MSVC)
target_compile_options(ngcore PRIVATE /W4 /permissive-)
endif()
# ---- GPU app (needs the Vulkan SDK: set VULKAN_SDK or -DVULKAN_SDK=...) ----
if(NOT VULKAN_SDK AND DEFINED ENV{VULKAN_SDK})
set(VULKAN_SDK $ENV{VULKAN_SDK})
endif()
set(ENV{VULKAN_SDK} "${VULKAN_SDK}")
find_package(Vulkan REQUIRED)
find_program(GLSLC glslc HINTS ${VULKAN_SDK}/Bin REQUIRED)
# Runtime Lua -> GLSL -> SPIR-V. Embed engine declarations, not generated Lua.
file(READ ${CMAKE_SOURCE_DIR}/shaders/common.glsl NG_LUA_COMMON)
string(REGEX MATCH "layout\\(push_constant\\)[^}]*} pc;" NG_LUA_PC "${NG_LUA_COMMON}")
file(READ ${CMAKE_SOURCE_DIR}/shaders/fx_common.glsl NG_LUA_FX)
string(REGEX MATCH "struct Ent [^{]*\\{[^}]*};" NG_LUA_ENT "${NG_LUA_FX}")
file(READ ${CMAKE_SOURCE_DIR}/shaders/fx_state.glsl NG_LUA_STATE)
string(REGEX MATCH "struct FProj [^{]*\\{[^}]*};" NG_LUA_PROJ "${NG_LUA_STATE}")
file(READ ${CMAKE_SOURCE_DIR}/shaders/lua_native_runtime.glsl NG_LUA_RUNTIME)
file(READ ${CMAKE_SOURCE_DIR}/shaders/lua_native_terrain.glsl NG_LUA_TERRAIN)
file(READ ${CMAKE_SOURCE_DIR}/shaders/proj_lua_dispatch.glsl NG_LUA_DISPATCH)
set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS
shaders/common.glsl shaders/fx_common.glsl shaders/fx_state.glsl
shaders/lua_native_runtime.glsl shaders/lua_native_terrain.glsl shaders/proj_lua_dispatch.glsl)
if(NOT NG_LUA_PC OR NOT NG_LUA_ENT OR NOT NG_LUA_PROJ)
message(FATAL_ERROR "Cannot extract the engine ABI for runtime Lua shaders")
endif()
configure_file(src/lua/gpu_lua_sources.h.in generated/gpu_lua_sources.h @ONLY)
find_path(SHADERC_INCLUDE_DIR shaderc/shaderc.h HINTS ${VULKAN_SDK}/Include REQUIRED)
find_file(SHADERC_RUNTIME shaderc_shared.dll HINTS ${VULKAN_SDK}/Bin REQUIRED)
add_library(nggpulua STATIC src/lua/gpu_lua.cpp src/lua/gpu_lua_proto.c)
target_include_directories(nggpulua PUBLIC src PRIVATE ${CMAKE_BINARY_DIR}/generated ${SHADERC_INCLUDE_DIR})
target_link_libraries(nggpulua PUBLIC luajit_static)
# ---- Blosc2 (world save format: byte-shuffled, LZ4/Zstd-compressed tiles); lz4/zstd sources are local checkouts ----
set(BUILD_SHARED OFF CACHE BOOL "" FORCE)
set(BUILD_STATIC ON CACHE BOOL "" FORCE)
set(BUILD_TESTS OFF CACHE BOOL "" FORCE)
set(BUILD_FUZZERS OFF CACHE BOOL "" FORCE)
set(BUILD_BENCHMARKS OFF CACHE BOOL "" FORCE)
set(BUILD_EXAMPLES OFF CACHE BOOL "" FORCE)
set(BUILD_PLUGINS OFF CACHE BOOL "" FORCE)
set(DEACTIVATE_ZLIB ON CACHE BOOL "" FORCE)
set(BLOSC_ENABLE_ZFP OFF CACHE STRING "" FORCE)
set(BLOSC_LZ4_SOURCE_DIR ${CMAKE_SOURCE_DIR}/third_party/lz4 CACHE PATH "" FORCE)
set(BLOSC_ZSTD_SOURCE_DIR ${CMAKE_SOURCE_DIR}/third_party/zstd CACHE PATH "" FORCE)
set(BLOSC_MERGE_BUNDLED_STATIC OFF CACHE BOOL "" FORCE) # (the in-place merge of lz4 / zstd into libblosc2 ran on every build: blosc/CMakeLists.txt)
add_subdirectory(third_party/c-blosc2 EXCLUDE_FROM_ALL)
add_library(ngblosc INTERFACE)
target_link_libraries(ngblosc INTERFACE blosc2_static)
target_include_directories(ngblosc INTERFACE ${CMAKE_SOURCE_DIR}/third_party/c-blosc2/include)
add_executable(noita_gpu src/app/main.cpp src/audio/fmod_audio.cpp)
target_link_libraries(noita_gpu PRIVATE ngspawn ngblosc nggpulua Vulkan::Vulkan)
# the short git hash of the sources at configure time (the GIF metadata's "build", docs/GIF_METADATA.md)
execute_process(COMMAND git rev-parse --short HEAD WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} OUTPUT_VARIABLE NG_GIT_HASH OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_QUIET)
if(NOT NG_GIT_HASH)
set(NG_GIT_HASH "dev")
endif()
target_compile_definitions(noita_gpu PRIVATE NG_BUILD="${NG_GIT_HASH}")
if(MSVC)
target_compile_options(noita_gpu PRIVATE /W4 /permissive- /bigobj)
endif()
set(SHADERS intent apply paint render terrain sprites entities projectiles edits react burn rigid_solve rigid_commit rigid_frame rigid_events proj_spawn proj_step fx_particles fx_anims ents_prev fx_hand displace rigid_raster explode_rays rigid_burn vines sky_cols glow fog particle_req light_cells clip_half)
# shaders are rebuilt when their source or any included .glsl changes, then copied next to the executable
file(GLOB SHADER_INCLUDES ${CMAKE_SOURCE_DIR}/shaders/*.glsl ${CMAKE_SOURCE_DIR}/shaders/generated/*.glsl)
set(SPV_FILES "")
# Offline reference artifacts only; the game uses native C++ startup translation.
find_package(Python3 COMPONENTS Interpreter QUIET)
if(Python3_Interpreter_FOUND)
add_custom_target(lua_gpu_generate
COMMAND ${Python3_EXECUTABLE} -m tools.lua_gpu.engine
--data-root ${CMAKE_SOURCE_DIR}/..
--out-dir ${CMAKE_SOURCE_DIR}/shaders/generated
--header ${CMAKE_SOURCE_DIR}/src/lua/gpu_program_generated.h
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
COMMENT "Generating offline Lua GPU reference artifacts"
VERBATIM)
endif()
foreach(s ${SHADERS})
add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/spv/${s}.spv
COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_BINARY_DIR}/spv
COMMAND ${GLSLC} --target-env=vulkan1.1 -O -I ${CMAKE_SOURCE_DIR}/shaders
${CMAKE_SOURCE_DIR}/shaders/${s}.comp -o ${CMAKE_BINARY_DIR}/spv/${s}.spv
DEPENDS ${CMAKE_SOURCE_DIR}/shaders/${s}.comp ${SHADER_INCLUDES}
COMMENT "glslc ${s}.comp")
list(APPEND SPV_FILES ${CMAKE_BINARY_DIR}/spv/${s}.spv)
add_custom_command(TARGET noita_gpu POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_BINARY_DIR}/spv/${s}.spv $<TARGET_FILE_DIR:noita_gpu>/${s}.spv)
endforeach()
add_custom_target(shaders ALL DEPENDS ${SPV_FILES})
# building only the shaders (e.g. the `shaders` project in Visual Studio) must also refresh the demo folder and the exe folder
foreach(s ${SHADERS})
add_custom_command(TARGET shaders POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_BINARY_DIR}/spv/${s}.spv ${CMAKE_SOURCE_DIR}/demo/${s}.spv
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_BINARY_DIR}/spv/${s}.spv ${CMAKE_BINARY_DIR}/$<CONFIG>/${s}.spv)
endforeach()
# `cmake --install` / the INSTALL project: the demo folder gets the exe and the shaders
install(TARGETS noita_gpu RUNTIME DESTINATION ${CMAKE_SOURCE_DIR}/demo)
install(FILES ${SPV_FILES} DESTINATION ${CMAKE_SOURCE_DIR}/demo)
# keep the demo folder (run_*.bat) in step with the build
add_custom_command(TARGET noita_gpu POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different $<TARGET_FILE:noita_gpu> ${CMAKE_SOURCE_DIR}/demo/noita_gpu.exe)
foreach(s ${SHADERS})
add_custom_command(TARGET noita_gpu POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_BINARY_DIR}/spv/${s}.spv ${CMAKE_SOURCE_DIR}/demo/${s}.spv)
endforeach()
add_dependencies(noita_gpu shaders)
# noita_spelltest.exe: the same sources and engine, built to run only the spell test room (NG_SPELLTEST_ONLY in main.cpp); it uses the same shaders
add_executable(noita_spelltest src/app/main.cpp src/audio/fmod_audio.cpp)
target_link_libraries(noita_spelltest PRIVATE ngspawn ngblosc nggpulua Vulkan::Vulkan)
target_compile_definitions(noita_spelltest PRIVATE NG_BUILD="${NG_GIT_HASH}" NG_SPELLTEST_ONLY=1)
if(MSVC)
target_compile_options(noita_spelltest PRIVATE /W4 /permissive- /bigobj)
endif()
add_dependencies(noita_spelltest shaders)
foreach(app noita_gpu noita_spelltest)
add_custom_command(TARGET ${app} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${SHADERC_RUNTIME} $<TARGET_FILE_DIR:${app}>/shaderc_shared.dll
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${SHADERC_RUNTIME} ${CMAKE_SOURCE_DIR}/demo/shaderc_shared.dll)
endforeach()
install(FILES ${SHADERC_RUNTIME} DESTINATION ${CMAKE_SOURCE_DIR}/demo)
add_custom_command(TARGET noita_spelltest POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different $<TARGET_FILE:noita_spelltest> ${CMAKE_SOURCE_DIR}/demo/noita_spelltest.exe)
# pack_spelltest (not part of ALL): build/pack/noita_spelltest/ and build/pack/noita_spelltest_<hash>.zip, to run with the Steam Noita (packaging/spelltest/README.txt):
# the exe, the shaders, audio_host.exe (the 32-bit sound helper, built here in build_audio32) and the C++ runtime DLLs; never data.wak or FMOD
# cmake --build build --config Release --target pack_spelltest
set(CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS_SKIP TRUE)
set(CMAKE_INSTALL_UCRT_LIBRARIES FALSE)
include(InstallRequiredSystemLibraries) # (only to find the redistributable DLLs: CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS)
string(REPLACE ";" "|" NG_PACK_SHADERS "${SHADERS}")
string(REPLACE ";" "|" NG_PACK_CRT "${CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS}")
set(NG_AUDIO32_DIR ${CMAKE_SOURCE_DIR}/build_audio32)
add_custom_target(pack_spelltest
COMMAND ${CMAKE_COMMAND} -S ${CMAKE_SOURCE_DIR}/tools/audio_host -B ${NG_AUDIO32_DIR} -G "${CMAKE_GENERATOR}" -A Win32
COMMAND ${CMAKE_COMMAND} --build ${NG_AUDIO32_DIR} --config Release
COMMAND ${CMAKE_COMMAND} -DEXE=$<TARGET_FILE:noita_spelltest> -DSPV_DIR=${CMAKE_BINARY_DIR}/spv "-DSHADERS=${NG_PACK_SHADERS}"
-DAUDIO=${NG_AUDIO32_DIR}/Release/audio_host.exe -DREADME=${CMAKE_SOURCE_DIR}/packaging/spelltest/README.txt "-DCRT=${NG_PACK_CRT}"
-DSHADERC=${SHADERC_RUNTIME}
-DOUT=${CMAKE_BINARY_DIR}/pack -DVERSION=${NG_GIT_HASH} -P ${CMAKE_SOURCE_DIR}/packaging/spelltest/pack.cmake
COMMENT "packing the spell room"
VERBATIM)
add_dependencies(pack_spelltest noita_spelltest shaders)
enable_testing()
# ---- world generation (vendored noita_random, MIT) ----
add_library(ngworld STATIC src/world/wang_gen.cpp src/world/biomes.cpp src/world/petri.cpp src/world/noise.cpp src/world/biome_materials.cpp src/world/cave_bitmap.cpp src/world/proc_biome.cpp src/world/terrain.cpp src/world/reactions.cpp)
target_link_libraries(ngworld PUBLIC ngcore)
target_include_directories(ngworld PUBLIC src PRIVATE third_party/noita_random)
if(MSVC)
target_compile_options(ngworld PRIVATE /W0 /bigobj /permissive)
endif()
add_library(ngspawn STATIC src/ai/pathfind.cpp src/world/chunk_spawn.cpp src/world/area_setup.cpp src/world/area_store.cpp src/world/biome_modifiers.cpp src/entity/entity.cpp src/entity/runtime.cpp src/entity/runtime_natives.cpp src/entity/electricity.cpp src/entity/status.cpp src/gun/gun.cpp src/gun/projectiles.cpp src/gun/effects.cpp src/gun/gpu_fx.cpp src/physics/rigid.cpp src/physics/gpu_bodies.cpp)
target_link_libraries(ngspawn PUBLIC ngworld nglua)
# ---- Lua host (LuaJIT built from third_party/luajit with MSVC) ----
set(LUAJIT_DIR ${CMAKE_SOURCE_DIR}/third_party/luajit)
if(NOT EXISTS ${LUAJIT_DIR}/src/lua51.lib)
message(STATUS "Building LuaJIT (static, x64)...")
execute_process(COMMAND cmd /c ${LUAJIT_DIR}/build_luajit.bat OUTPUT_QUIET ERROR_QUIET RESULT_VARIABLE LJ_RC)
if(NOT LJ_RC EQUAL 0)
message(FATAL_ERROR "LuaJIT build failed; run third_party/luajit/build_luajit.bat to see why")
endif()
endif()
add_library(luajit_static STATIC IMPORTED)
set_target_properties(luajit_static PROPERTIES IMPORTED_LOCATION ${LUAJIT_DIR}/src/lua51.lib
INTERFACE_INCLUDE_DIRECTORIES ${LUAJIT_DIR}/src)
add_library(nglua STATIC src/lua/lua_host.cpp src/lua/entity_natives.cpp src/lua/host_natives.cpp src/lua/mod_loader.cpp src/entity/herds.cpp)
target_link_libraries(nglua PUBLIC ngcore luajit_static)
if(MSVC)
target_compile_options(nglua PRIVATE /W4 /permissive-)
endif()
# CPU rigid-body engine: the recovered Box2D of the original, used by the game through src/phys.
set(NG_CPU_PHYS_SOURCES src/phys/entity_physics.cpp src/phys/rigid_dynamics.cpp src/phys/rigid_collision.cpp src/phys/convex_distance.cpp src/phys/rigid_world_toi.cpp src/phys/dynamic_tree.cpp
src/phys/contact_solver.cpp src/phys/rigid_contact_world.cpp src/phys/rigid_world_step.cpp src/phys/joint_solver.cpp src/phys/shape_queries.cpp src/phys/recovered_units.cpp src/phys/world.cpp src/phys/rigid_fracture.cpp src/phys/terrain_outline.cpp src/phys/terrain_world.cpp src/phys/poly_decompose.cpp src/phys/phys_system.cpp)
file(GLOB NG_TESS_SOURCES third_party/libtess2/Source/*.c) # libtess2 (SGI free software licence B), the tessellator of the original
add_library(ngphys STATIC ${NG_CPU_PHYS_SOURCES} ${NG_TESS_SOURCES})
target_link_libraries(ngspawn PUBLIC ngphys)
target_include_directories(ngphys PUBLIC cpu src third_party/libtess2/Include)
target_include_directories(ngphys PRIVATE third_party/libtess2/Source)