# Tuxanci 2 - A first person shooter
# Copyright (C) 2025-2026 Connor Thomson
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
set(SHADER_SLANG_PROFILE "spirv_1_5")
set(SHADER_SLANG_TARGET "spirv")
find_program(SLANGC_EXECUTABLE slangc)
if(NOT SLANGC_EXECUTABLE)
message(FATAL_ERROR "slangc is not found. Download it from https://github.com/shader-slang/slang and make sure it is in your PATH.")
endif()
find_program(XXD_EXECUTABLE NAMES xxd)
if(NOT XXD_EXECUTABLE)
message(FATAL_ERROR "xxd is not found. Make sure it is in your PATH.")
endif()
set(SHADER_EMBED_SCRIPT "${CMAKE_CURRENT_LIST_DIR}/EmbedFile.cmake")
function(target_compile_shaders TARGET_NAME)
cmake_parse_arguments(PARSE_ARGV 1 ARG "" "PROFILE;ENTRY" "SHADERS")
if(NOT ARG_PROFILE)
set(ARG_PROFILE "${SHADER_SLANG_PROFILE}")
endif()
if(NOT ARG_ENTRY)
set(ARG_ENTRY "main")
endif()
set(SPIRV_OUTPUTS "")
foreach(SHADER ${ARG_SHADERS})
get_filename_component(SHADER_ABS "${SHADER}" ABSOLUTE)
get_filename_component(SHADER_NAME "${SHADER}" NAME)
set(_stage "")
if(SHADER_NAME MATCHES "\\.vert\\.slang$")
set(_stage "vertex")
elseif(SHADER_NAME MATCHES "\\.frag\\.slang$")
set(_stage "fragment")
elseif(SHADER_NAME MATCHES "\\.comp\\.slang$")
set(_stage "compute")
elseif(SHADER_NAME MATCHES "\\.geom\\.slang$")
set(_stage "geometry")
elseif(SHADER_NAME MATCHES "\\.tesc\\.slang$")
set(_stage "hull")
elseif(SHADER_NAME MATCHES "\\.tese\\.slang$")
set(_stage "domain")
elseif(SHADER_NAME MATCHES "\\.rgen\\.slang$")
set(_stage "raygeneration")
elseif(SHADER_NAME MATCHES "\\.rchit\\.slang$")
set(_stage "closesthit")
elseif(SHADER_NAME MATCHES "\\.rmiss\\.slang$")
set(_stage "miss")
else()
message(FATAL_ERROR "'${SHADER_NAME}' must end with ..slang")
endif()
set(SPIRV_OUT "${CMAKE_CURRENT_BINARY_DIR}/shaders/${SHADER_NAME}.spv")
set(SPIRV_EMBED_C_OUT "${CMAKE_CURRENT_BINARY_DIR}/shaders/${SHADER_NAME}.xxd.c")
string(REGEX REPLACE "[^A-Za-z0-9]" "_" SHADER_SYMBASE "${SHADER_NAME}.spv")
set(SHADER_SYMBASE "file_${SHADER_SYMBASE}")
add_custom_command(
OUTPUT ${SPIRV_OUT}
COMMAND ${CMAKE_COMMAND} -E make_directory "${CMAKE_CURRENT_BINARY_DIR}/shaders"
COMMAND "${SLANGC_EXECUTABLE}" "${SHADER_ABS}"
-target ${SHADER_SLANG_TARGET}
-profile ${ARG_PROFILE}
-entry ${ARG_ENTRY}
-stage ${_stage}
-o ${SPIRV_OUT}
MAIN_DEPENDENCY "${SHADER_ABS}"
COMMENT "Building SPIR-V ${SHADER_NAME}.spv"
VERBATIM
)
add_custom_command(
OUTPUT ${SPIRV_EMBED_C_OUT}
COMMAND ${CMAKE_COMMAND}
-D IN_FILE=${SPIRV_OUT}
-D OUT_C=${SPIRV_EMBED_C_OUT}
-D SYMBASE=${SHADER_SYMBASE}
-D XXD_EXE=${XXD_EXECUTABLE}
-P "${SHADER_EMBED_SCRIPT}"
DEPENDS ${SPIRV_OUT} "${SHADER_EMBED_SCRIPT}"
COMMENT "Building C object ${SHADER_NAME}.spv.o"
VERBATIM
)
list(APPEND SPIRV_OUTPUTS ${SPIRV_EMBED_C_OUT})
endforeach()
target_sources(${TARGET_NAME} PRIVATE ${SPIRV_OUTPUTS})
endfunction()
function(target_embed_files TARGET_NAME)
cmake_parse_arguments(PARSE_ARGV 1 ARG "" "" "FILES")
set(EMBEDDED_OUTPUTS "")
foreach(ASSET ${ARG_FILES})
get_filename_component(ASSET_ABS "${ASSET}" ABSOLUTE)
file(RELATIVE_PATH ASSET_RELATIVE "${CMAKE_SOURCE_DIR}" "${ASSET_ABS}")
string(REGEX REPLACE "[^A-Za-z0-9]" "_" ASSET_SYMBASE "${ASSET_RELATIVE}")
set(ASSET_SYMBASE "file_${ASSET_SYMBASE}")
get_filename_component(ASSET_NAME "${ASSET_RELATIVE}" NAME)
set(ASSET_EMBED_C_OUT "${CMAKE_CURRENT_BINARY_DIR}/data/${ASSET_NAME}.xxd.c")
add_custom_command(
OUTPUT ${ASSET_EMBED_C_OUT}
COMMAND ${CMAKE_COMMAND} -E make_directory "${CMAKE_CURRENT_BINARY_DIR}/data"
COMMAND ${CMAKE_COMMAND}
-D IN_FILE=${ASSET_ABS}
-D OUT_C=${ASSET_EMBED_C_OUT}
-D SYMBASE=${ASSET_SYMBASE}
-D XXD_EXE=${XXD_EXECUTABLE}
-P "${SHADER_EMBED_SCRIPT}"
DEPENDS "${ASSET_ABS}" "${SHADER_EMBED_SCRIPT}"
COMMENT "Building C object ${ASSET_RELATIVE}.o"
VERBATIM
)
list(APPEND EMBEDDED_OUTPUTS ${ASSET_EMBED_C_OUT})
endforeach()
target_sources(${TARGET_NAME} PRIVATE ${EMBEDDED_OUTPUTS})
endfunction()