diff --git a/CMakeLists.txt b/CMakeLists.txt index 24b09edacf..62cfdf1ba8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -75,7 +75,7 @@ if (Daemon_OUT) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${Daemon_OUT}) endif() -include(DaemonBuildInfo) +include(DaemonSourceGenerator) include(DaemonPlatform) ################################################################################ @@ -927,39 +927,11 @@ if (BUILD_CLIENT) Tests ${CLIENTTESTLIST} ) - # generate glsl include files - set(GLSL_SOURCE_DIR ${ENGINE_DIR}/renderer/glsl_source) - set(EMBED_INCLUDE_DIR ${CMAKE_CURRENT_BINARY_DIR}/embed_data) - file(MAKE_DIRECTORY ${EMBED_INCLUDE_DIR}) + # Generate GLSL include files. + daemon_embed_files("EngineShaders" "${GLSL_EMBED_DIR}" "${GLSL_EMBED_LIST}" "TEXT" "client-objects") - set(SHADERS_CPP_TEXT "// This file is auto-generated by CMakeLists.txt.\n") - string(APPEND SHADERS_CPP_TEXT "#include \"common/Common.h\"\n\n") - set(SHADERMAP_TEXT "") - - foreach(res ${GLSLSOURCELIST}) - get_filename_component(filename_no_ext ${res} NAME_WE) - set(outpath ${EMBED_INCLUDE_DIR}/${filename_no_ext}.glsl.h) - - add_custom_command( - OUTPUT ${outpath} - COMMAND ${CMAKE_COMMAND} "-DINPUT_FILE=${res}" "-DOUTPUT_FILE=${outpath}" - "-DVARIABLE_NAME=${filename_no_ext}_glsl" -P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/EmbedText.cmake - MAIN_DEPENDENCY ${res} - ) - - set_property(TARGET client-objects APPEND PROPERTY SOURCES ${outpath}) - - string(APPEND SHADERS_CPP_TEXT "#include \"../embed_data/${filename_no_ext}.glsl.h\"\n") - string(APPEND SHADERMAP_TEXT "\t{ \"${filename_no_ext}.glsl\", ") - string(APPEND SHADERMAP_TEXT "std::string(reinterpret_cast( ${filename_no_ext}_glsl ), ") - string(APPEND SHADERMAP_TEXT "sizeof( ${filename_no_ext}_glsl )) },\n") - endforeach() - - string(APPEND SHADERS_CPP_TEXT "\nextern const std::unordered_map shadermap\n{\n") - string(APPEND SHADERS_CPP_TEXT "${SHADERMAP_TEXT}") - string(APPEND SHADERS_CPP_TEXT "};\n") - - daemon_write_generated("shaders.cpp" "${SHADERS_CPP_TEXT}") + # Generate media include files. + daemon_embed_files("EngineMedia" "${MEDIA_EMBED_DIR}" "${MEDIA_EMBED_LIST}" "BINARY" "client-objects") endif() if (BUILD_SERVER) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 4140a21f14..e4a8d64e48 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -61,7 +61,7 @@ jobs: displayName: 'Build' - bash: | set -e - build/test-ttyclient -pakpath pkg -set fs_basepak daemon -set vm.cgame.type 3 + build/test-ttyclient -pakpath pkg -set fs_basepak '*daemon' -set vm.cgame.type 3 displayName: 'Test' - job: Linux @@ -130,6 +130,6 @@ jobs: displayName: 'Build' - bash: | set -e - SDL_VIDEODRIVER=offscreen ALSOFT_DRIVERS=null build/test-client -pakpath pkg -set fs_basepak daemon -set vm.cgame.type 3 + SDL_VIDEODRIVER=offscreen ALSOFT_DRIVERS=null build/test-client -pakpath pkg -set fs_basepak '*daemon' -set vm.cgame.type 3 condition: "and(succeeded(), eq(variables.TOOLCHAIN_FILE, ''))" displayName: 'Test' diff --git a/cmake/DaemonBuildInfo.cmake b/cmake/DaemonBuildInfo.cmake deleted file mode 100644 index 95f08084e5..0000000000 --- a/cmake/DaemonBuildInfo.cmake +++ /dev/null @@ -1,44 +0,0 @@ -set(DAEMON_GENERATED_DIR "${CMAKE_CURRENT_BINARY_DIR}/GeneratedSource") - -set(DAEMON_BUILDINFO_DIR "DaemonBuildInfo") -set(DAEMON_BUILDINFO_HEADER "// Automatically generated, do not modify!\n") -set(DAEMON_BUILDINFO_CPP_EXT ".cpp") -set(DAEMON_BUILDINFO_H_EXT ".h") -set(BUILDINFOLIST) - -file(MAKE_DIRECTORY "${DAEMON_GENERATED_DIR}") -include_directories("${DAEMON_GENERATED_DIR}") - -file(MAKE_DIRECTORY "${DAEMON_GENERATED_DIR}/${DAEMON_BUILDINFO_DIR}") - -foreach(kind CPP H) - set(DAEMON_BUILDINFO_${kind} "${DAEMON_BUILDINFO_HEADER}") -endforeach() - -macro(daemon_add_buildinfo TYPE NAME VALUE) - set(DAEMON_BUILDINFO_CPP "${DAEMON_BUILDINFO_CPP}const ${TYPE} ${NAME}=${VALUE};\n") - set(DAEMON_BUILDINFO_H "${DAEMON_BUILDINFO_H}extern const ${TYPE} ${NAME};\n") -endmacro() - -macro(daemon_write_generated GENERATED_PATH GENERATED_CONTENT) - set(DAEMON_GENERATED_FILE ${DAEMON_GENERATED_DIR}/${GENERATED_PATH}) - - if (EXISTS "${DAEMON_GENERATED_FILE}") - file(READ "${DAEMON_GENERATED_FILE}" GENERATED_CONTENT_READ) - endif() - - if (NOT "${GENERATED_CONTENT}" STREQUAL "${GENERATED_CONTENT_READ}") - message(STATUS "Generating ${GENERATED_PATH}") - file(WRITE "${DAEMON_GENERATED_FILE}" "${GENERATED_CONTENT}") - endif() -endmacro() - -macro(daemon_write_buildinfo NAME) - foreach(kind CPP H) - set(DAEMON_BUILDINFO_${kind}_NAME "${NAME}${DAEMON_BUILDINFO_${kind}_EXT}") - set(DAEMON_BUILDINFO_${kind}_PATH "${DAEMON_BUILDINFO_DIR}/${DAEMON_BUILDINFO_${kind}_NAME}") - - daemon_write_generated("${DAEMON_BUILDINFO_${kind}_PATH}" "${DAEMON_BUILDINFO_${kind}}") - list(APPEND BUILDINFOLIST "${DAEMON_GENERATED_FILE}") - endforeach() -endmacro() diff --git a/cmake/DaemonFileEmbedder.cmake b/cmake/DaemonFileEmbedder.cmake new file mode 100644 index 0000000000..5410cc906c --- /dev/null +++ b/cmake/DaemonFileEmbedder.cmake @@ -0,0 +1,44 @@ +# Converts a text file into a C-language char array definition. +# For use in CMake script mode (cmake -P). +# Required definitions on command line: +# INPUT_FILE, OUTPUT_FILE, FILE_FORMAT, VARIABLE_NAME + +# Inspired by: +# https://stackoverflow.com/questions/11813271/embed-resources-eg-shader-code-images-into-executable-library-with-cmake/27206982#27206982 + +file(READ ${INPUT_FILE} contents HEX) + +# Translate the file content. +if ("${FILE_FORMAT}" STREQUAL "TEXT") + # Strip \r for consistency. + string(REGEX REPLACE "(0d)?(..)" "0x\\2," contents "${contents}") +elseif("${FILE_FORMAT}" STREQUAL "BINARY") + string(REGEX REPLACE "(..)" "0x\\1," contents "${contents}") +else() + message(FATAL_ERROR "Unknown file format: ${FILE_FORMAT}") +endif() + +# Add null terminator. +set(contents "${contents}0x00,") + +# Split long lines. +string(REGEX REPLACE + "(0x..,0x..,0x..,0x..,0x..,0x..,0x..,0x..,0x..,0x..,0x..,0x..,0x..,0x..,0x..,)" "\\1\n" + contents "${contents}" +) + +# A bit more of beautification. +string(REGEX REPLACE ",$" ",\n" contents "${contents}") +set(DATA_VARIABLE_NAME "data_${VARIABLE_NAME}") + +file(WRITE ${OUTPUT_FILE} + "constexpr unsigned char ${DATA_VARIABLE_NAME}[] =\n" + "{\n" + "${contents}" + "};\n" + "const embeddedFileMapEntry_t ${VARIABLE_NAME} =\n" + "{\n" + "reinterpret_cast( ${DATA_VARIABLE_NAME} ),\n" + "sizeof( ${DATA_VARIABLE_NAME} ) - 1,\n" + "};\n" +) diff --git a/cmake/DaemonGame.cmake b/cmake/DaemonGame.cmake index 331980efaf..9cf9ee95cc 100644 --- a/cmake/DaemonGame.cmake +++ b/cmake/DaemonGame.cmake @@ -39,7 +39,7 @@ option(BUILD_GAME_NATIVE_DLL "Build the shared library files, mostly useful for option(BUILD_GAME_NATIVE_EXE "Build native executable, which might be used for better performances by server owners" OFF) include(ExternalProject) -include(DaemonBuildInfo) +include(DaemonSourceGenerator) include(DaemonPlatform) # Do not report unused native compiler if native vms are not built. diff --git a/cmake/DaemonSourceGenerator.cmake b/cmake/DaemonSourceGenerator.cmake new file mode 100644 index 0000000000..c2183651a8 --- /dev/null +++ b/cmake/DaemonSourceGenerator.cmake @@ -0,0 +1,175 @@ +# Daemon BSD Source Code +# Copyright (c) 2025, Daemon Developers +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of the nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +set(DAEMON_SOURCE_GENERATOR "${CMAKE_CURRENT_LIST_FILE}") +get_filename_component(current_list_dir "${CMAKE_CURRENT_LIST_FILE}" DIRECTORY) +set(DAEMON_FILE_EMBEDDER "${current_list_dir}/DaemonFileEmbedder.cmake") + +set(DAEMON_GENERATED_SUBDIR "GeneratedSource") +set(DAEMON_GENERATED_DIR "${CMAKE_CURRENT_BINARY_DIR}/${DAEMON_GENERATED_SUBDIR}") + +set(DAEMON_BUILDINFO_SUBDIR "DaemonBuildInfo") +set(DAEMON_EMBEDDED_SUBDIR "DaemonEmbeddedFiles") + +set(DAEMON_BUILDINFO_DIR "${DAEMON_GENERATED_DIR}/${DAEMON_BUILDINFO_SUBDIR}") +set(DAEMON_EMBEDDED_DIR "${DAEMON_GENERATED_DIR}/${DAEMON_EMBEDDED_SUBDIR}") + +file(MAKE_DIRECTORY "${DAEMON_GENERATED_DIR}") +include_directories("${DAEMON_GENERATED_DIR}") + +file(MAKE_DIRECTORY "${DAEMON_BUILDINFO_DIR}") +file(MAKE_DIRECTORY "${DAEMON_EMBEDDED_DIR}") + +set(DAEMON_GENERATED_HEADER "// Automatically generated, do not modify!\n") +set(DAEMON_GENERATED_CPP_EXT ".cpp") +set(DAEMON_GENERATED_H_EXT ".h") + +set(BUILDINFOLIST) + +foreach(kind CPP H) + set(DAEMON_BUILDINFO_${kind}_TEXT "${DAEMON_GENERATED_HEADER}") +endforeach() + +macro(daemon_add_buildinfo type name value) + string(APPEND DAEMON_BUILDINFO_CPP_TEXT "const ${type} ${name}=${value};\n") + string(APPEND DAEMON_BUILDINFO_H_TEXT "extern const ${type} ${name};\n") +endmacro() + +macro(daemon_write_buildinfo name) + foreach(kind CPP H) + set(buildinfo_file_path "${DAEMON_BUILDINFO_DIR}/${name}${DAEMON_GENERATED_${kind}_EXT}") + + file(GENERATE OUTPUT "${buildinfo_file_path}" CONTENT "${DAEMON_BUILDINFO_${kind}_TEXT}") + list(APPEND BUILDINFOLIST "${buildinfo_file_path}") + endforeach() +endmacro() + +macro(daemon_embed_files basename dir list format targetname) + set(embed_subdir "${DAEMON_EMBEDDED_SUBDIR}/${basename}") + set(embed_dir "${DAEMON_GENERATED_DIR}/${embed_subdir}") + + file(MAKE_DIRECTORY "${embed_dir}") + + foreach(kind CPP H) + set(embed_${kind}_basename "${basename}${DAEMON_GENERATED_${kind}_EXT}") + set(embed_${kind}_src_file "${DAEMON_EMBEDDED_DIR}/${embed_${kind}_basename}") + set(embed_${kind}_file "${DAEMON_EMBEDDED_SUBDIR}/${embed_${kind}_basename}") + set(embed_${kind}_text "${DAEMON_GENERATED_HEADER}") + set_property(SOURCE "${embed_${kind}_src_file}" APPEND PROPERTY OBJECT_DEPENDS "${DAEMON_SOURCE_GENERATOR}") + set_property(TARGET "${targetname}" APPEND PROPERTY SOURCES "${embed_${kind}_src_file}") + endforeach() + + if (NOT DAEMON_EMBEDDED_FILES_HEADER) + set(DAEMON_EMBEDDED_FILES_HEADER "${DAEMON_EMBEDDED_SUBDIR}/DaemonEmbeddedFiles.h") + + string(APPEND embed_header_text + "#ifndef DAEMON_EMBEDDED_FILES_H_\n" + "#define DAEMON_EMBEDDED_FILES_H_\n" + "#include \n" + "#include \n" + "\n" + "struct embeddedFileMapEntry_t\n" + "{\n" + " const char* data;\n" + " size_t size;\n" + "};\n" + "\n" + "using embeddedFileMap_t = std::unordered_map;\n" + "#endif // DAEMON_EMBEDDED_FILES_H_\n" + ) + + set(embed_header_file "${DAEMON_GENERATED_DIR}/${DAEMON_EMBEDDED_FILES_HEADER}") + file(GENERATE OUTPUT "${embed_header_file}" CONTENT "${embed_header_text}") + add_definitions(-DDAEMON_EMBEDDED_FILES_HEADER="${DAEMON_EMBEDDED_FILES_HEADER}") + endif() + + string(APPEND embed_CPP_text + "#include \"${embed_H_file}\"\n" + "\n" + "namespace ${basename} {\n" + ) + + string(APPEND embed_H_text + "#include \"${DAEMON_EMBEDDED_FILES_HEADER}\"\n" + "\n" + "namespace ${basename} {\n" + ) + + set(embed_map_text "") + + foreach(filename ${list}) + string(REGEX REPLACE "[^A-Za-z0-9]" "_" filename_symbol "${filename}") + + set(inpath "${dir}/${filename}") + set(outpath "${embed_dir}/${filename_symbol}${DAEMON_GENERATED_H_EXT}") + + add_custom_command( + OUTPUT "${outpath}" + COMMAND ${CMAKE_COMMAND} + "-DINPUT_FILE=${inpath}" + "-DOUTPUT_FILE=${outpath}" + "-DFILE_FORMAT=${format}" + "-DVARIABLE_NAME=${filename_symbol}" + -P "${DAEMON_FILE_EMBEDDER}" + MAIN_DEPENDENCY ${inpath} + DEPENDS + "${DAEMON_FILE_EMBEDDER}" + "${DAEMON_SOURCE_GENERATOR}" + ) + + set_property(TARGET "${targetname}" APPEND PROPERTY SOURCES "${outpath}") + + string(APPEND embed_CPP_text + "#include \"${basename}/${filename_symbol}.h\"\n" + ) + + string(APPEND embed_H_text + "extern const embeddedFileMapEntry_t ${filename_symbol};\n" + ) + + string(APPEND embed_map_text + "\t{ \"${filename}\", ${filename_symbol} },\n" + ) + endforeach() + + string(APPEND embed_CPP_text + "\n" + "const embeddedFileMap_t FileMap\n{\n" + "${embed_map_text}" + "};\n" + "}" + ) + + string(APPEND embed_H_text + "extern const embeddedFileMap_t FileMap;\n" + "};\n" + ) + + foreach(kind CPP H) + set(embed_file "${DAEMON_GENERATED_DIR}/${embed_${kind}_file}") + file(GENERATE OUTPUT "${embed_file}" CONTENT "${embed_${kind}_text}") + endforeach() +endmacro() diff --git a/cmake/EmbedText.cmake b/cmake/EmbedText.cmake deleted file mode 100644 index 43b9ffab4b..0000000000 --- a/cmake/EmbedText.cmake +++ /dev/null @@ -1,8 +0,0 @@ -# Converts a text file into a C-language char array definition. -# For use in CMake script mode (cmake -P). -# Required definitions on command line: INPUT_FILE, OUTPUT_FILE, VARIABLE_NAME - -# Inspired by https://stackoverflow.com/questions/11813271/embed-resources-eg-shader-code-images-into-executable-library-with-cmake/27206982#27206982 -file(READ ${INPUT_FILE} contents HEX) -string(REGEX REPLACE "(0d)?(..)" "0x\\2," contents ${contents}) # Strip \r for consistency -file(WRITE ${OUTPUT_FILE} "const unsigned char ${VARIABLE_NAME}[] = {${contents}};\n") diff --git a/pkg/daemon_src.dpkdir/gfx/2d/bigchars.png b/pkg/daemon_src.dpkdir/gfx/2d/bigchars.png deleted file mode 100644 index 3f75c39661..0000000000 Binary files a/pkg/daemon_src.dpkdir/gfx/2d/bigchars.png and /dev/null differ diff --git a/pkg/daemon_src.dpkdir/scripts/engine.shader b/pkg/daemon_src.dpkdir/scripts/engine.shader deleted file mode 100644 index 045c8961e2..0000000000 --- a/pkg/daemon_src.dpkdir/scripts/engine.shader +++ /dev/null @@ -1,21 +0,0 @@ -white -{ - cull none - { - map $whiteimage - blendfunc GL_SRC_ALPHA GL_ONE_MINUS_SRC_ALPHA - rgbgen vertex - } -} - -// console font fallback -gfx/2d/bigchars -{ - nopicmip - nomipmaps - { - map gfx/2d/bigchars - blendFunc GL_SRC_ALPHA GL_ONE_MINUS_SRC_ALPHA - rgbgen vertex - } -} diff --git a/src.cmake b/src.cmake index 20305a0ebb..62d7d519b3 100644 --- a/src.cmake +++ b/src.cmake @@ -86,68 +86,74 @@ else() include (${ENGINE_DIR}/renderer/src.cmake) endif() -set(GLSLSOURCELIST +set(MEDIA_EMBED_DIR ${ENGINE_DIR}/media) +set(MEDIA_EMBED_LIST + fonts/unifont.ttf + scripts/engine.shader +) + +set(GLSL_EMBED_DIR "${ENGINE_DIR}/renderer/glsl_source") +set(GLSL_EMBED_LIST # Common shader libraries - ${ENGINE_DIR}/renderer/glsl_source/common.glsl - ${ENGINE_DIR}/renderer/glsl_source/common_cp.glsl - ${ENGINE_DIR}/renderer/glsl_source/fogEquation_fp.glsl - ${ENGINE_DIR}/renderer/glsl_source/shaderProfiler_vp.glsl - ${ENGINE_DIR}/renderer/glsl_source/shaderProfiler_fp.glsl - + common.glsl + common_cp.glsl + fogEquation_fp.glsl + shaderProfiler_vp.glsl + shaderProfiler_fp.glsl # Material system shaders - ${ENGINE_DIR}/renderer/glsl_source/material_cp.glsl - ${ENGINE_DIR}/renderer/glsl_source/material_vp.glsl - ${ENGINE_DIR}/renderer/glsl_source/material_fp.glsl - ${ENGINE_DIR}/renderer/glsl_source/clearSurfaces_cp.glsl - ${ENGINE_DIR}/renderer/glsl_source/cull_cp.glsl - ${ENGINE_DIR}/renderer/glsl_source/depthReduction_cp.glsl - ${ENGINE_DIR}/renderer/glsl_source/processSurfaces_cp.glsl + material_cp.glsl + material_vp.glsl + material_fp.glsl + clearSurfaces_cp.glsl + cull_cp.glsl + depthReduction_cp.glsl + processSurfaces_cp.glsl # Screen-space shaders - ${ENGINE_DIR}/renderer/glsl_source/screenSpace_vp.glsl - ${ENGINE_DIR}/renderer/glsl_source/blur_fp.glsl - ${ENGINE_DIR}/renderer/glsl_source/cameraEffects_fp.glsl - ${ENGINE_DIR}/renderer/glsl_source/contrast_fp.glsl - ${ENGINE_DIR}/renderer/glsl_source/fogGlobal_fp.glsl - ${ENGINE_DIR}/renderer/glsl_source/fxaa_fp.glsl - ${ENGINE_DIR}/renderer/glsl_source/fxaa3_11_fp.glsl - ${ENGINE_DIR}/renderer/glsl_source/motionblur_fp.glsl - ${ENGINE_DIR}/renderer/glsl_source/ssao_fp.glsl + screenSpace_vp.glsl + blur_fp.glsl + cameraEffects_fp.glsl + contrast_fp.glsl + fogGlobal_fp.glsl + fxaa_fp.glsl + fxaa3_11_fp.glsl + motionblur_fp.glsl + ssao_fp.glsl # Lighting shaders - ${ENGINE_DIR}/renderer/glsl_source/depthtile1_vp.glsl - ${ENGINE_DIR}/renderer/glsl_source/depthtile1_fp.glsl - ${ENGINE_DIR}/renderer/glsl_source/depthtile2_fp.glsl - ${ENGINE_DIR}/renderer/glsl_source/lighttile_vp.glsl - ${ENGINE_DIR}/renderer/glsl_source/lighttile_fp.glsl - ${ENGINE_DIR}/renderer/glsl_source/computeLight_fp.glsl - ${ENGINE_DIR}/renderer/glsl_source/reliefMapping_fp.glsl + depthtile1_vp.glsl + depthtile1_fp.glsl + depthtile2_fp.glsl + lighttile_vp.glsl + lighttile_fp.glsl + computeLight_fp.glsl + reliefMapping_fp.glsl # Common vertex shader libraries - ${ENGINE_DIR}/renderer/glsl_source/deformVertexes_vp.glsl - ${ENGINE_DIR}/renderer/glsl_source/vertexAnimation_vp.glsl - ${ENGINE_DIR}/renderer/glsl_source/vertexSimple_vp.glsl - ${ENGINE_DIR}/renderer/glsl_source/vertexSkinning_vp.glsl + deformVertexes_vp.glsl + vertexAnimation_vp.glsl + vertexSimple_vp.glsl + vertexSkinning_vp.glsl # Regular shaders - ${ENGINE_DIR}/renderer/glsl_source/fogQuake3_vp.glsl - ${ENGINE_DIR}/renderer/glsl_source/fogQuake3_fp.glsl - ${ENGINE_DIR}/renderer/glsl_source/generic_vp.glsl - ${ENGINE_DIR}/renderer/glsl_source/generic_fp.glsl - ${ENGINE_DIR}/renderer/glsl_source/heatHaze_vp.glsl - ${ENGINE_DIR}/renderer/glsl_source/heatHaze_fp.glsl - ${ENGINE_DIR}/renderer/glsl_source/lightMapping_vp.glsl - ${ENGINE_DIR}/renderer/glsl_source/lightMapping_fp.glsl - ${ENGINE_DIR}/renderer/glsl_source/liquid_vp.glsl - ${ENGINE_DIR}/renderer/glsl_source/liquid_fp.glsl - ${ENGINE_DIR}/renderer/glsl_source/portal_vp.glsl - ${ENGINE_DIR}/renderer/glsl_source/portal_fp.glsl - ${ENGINE_DIR}/renderer/glsl_source/reflection_CB_vp.glsl - ${ENGINE_DIR}/renderer/glsl_source/reflection_CB_fp.glsl - ${ENGINE_DIR}/renderer/glsl_source/screen_vp.glsl - ${ENGINE_DIR}/renderer/glsl_source/screen_fp.glsl - ${ENGINE_DIR}/renderer/glsl_source/skybox_vp.glsl - ${ENGINE_DIR}/renderer/glsl_source/skybox_fp.glsl + fogQuake3_vp.glsl + fogQuake3_fp.glsl + generic_vp.glsl + generic_fp.glsl + heatHaze_vp.glsl + heatHaze_fp.glsl + lightMapping_vp.glsl + lightMapping_fp.glsl + liquid_vp.glsl + liquid_fp.glsl + portal_vp.glsl + portal_fp.glsl + reflection_CB_vp.glsl + reflection_CB_fp.glsl + screen_vp.glsl + screen_fp.glsl + skybox_vp.glsl + skybox_fp.glsl ) set(SERVERLIST @@ -271,6 +277,8 @@ set(CLIENTBASELIST ) set(CLIENTLIST + ${DAEMON_EMBEDDED_DIR}/EngineMedia.cpp + ${DAEMON_EMBEDDED_DIR}/EngineMedia.h ${ENGINE_DIR}/audio/ALObjects.cpp ${ENGINE_DIR}/audio/ALObjects.h ${ENGINE_DIR}/audio/Audio.cpp diff --git a/src/common/FileSystem.cpp b/src/common/FileSystem.cpp index 03cd3ef266..a5b660d0d4 100644 --- a/src/common/FileSystem.cpp +++ b/src/common/FileSystem.cpp @@ -129,6 +129,35 @@ static Cvar::Cvar fs_legacypaks("fs_legacypaks", "also load pk3s, ignoring static Cvar::Cvar fs_maxSymlinkDepth("fs_maxSymlinkDepth", "max depth of symlinks in zip paks (0 means disabled)", Cvar::NONE, 1); static Cvar::Cvar fs_pakprefixes("fs_pakprefixes", "prefixes to look for paks to load", 0, ""); +builtinPakMap_t builtinPakMap = {}; + +std::vector builtinPaks = {}; + +void ClearBuiltinPaks() +{ + builtinPakMap.clear(); + + builtinPaks.clear(); + builtinPaks.shrink_to_fit(); +} + +void AddBuiltinPak(const std::string& name, const std::string& version, const embeddedFileMap_t& map) +{ + PakInfo *pak = new PakInfo(); + pak->name = Str::Format( "*%s", name ); + pak->version = version; + pak->checksum = 0; + pak->type = pakType_t::PAK_ZIP; + pak->path = Str::Format( "*%s_%s", name, version ); + + builtinPaks.push_back(*pak); + + Log::Debug("Adding builtin pak with %d files: %s", map.size(), pak->name); + builtinPakMap[pak->name] = map; + + PakPath::LoadPak(*pak); +} + bool UseLegacyPaks() { return fs_legacypaks.Get(); @@ -1257,8 +1286,13 @@ static void InternalLoadPak( bool hasDeps = false; offset_t depsOffset = 0; ZipArchive zipFile; + bool isLegacy = pak.version.empty(); + bool isBuiltin = pak.type == pakType_t::PAK_ZIP && pak.name[ 0 ] == '*'; + bool isDir = pak.type == pakType_t::PAK_DIR; + bool isZip = pak.type == pakType_t::PAK_ZIP && !isBuiltin; + // Check if this pak has already been loaded to avoid recursive dependencies for (auto& x: loadedPaks) { // If the prefix is a superset of our current prefix, then it already @@ -1267,13 +1301,15 @@ static void InternalLoadPak( return; } - if (pak.type == pakType_t::PAK_ZIP) { + if (isBuiltin) { + fsLogs.WithoutSuppression().Notice("Loading builtin pak '%s'...", pak.path.c_str()); + } else if (isZip) { if (!isLegacy) { fsLogs.WithoutSuppression().Notice("Loading pak '%s'...", pak.path.c_str()); } else { fsLogs.WithoutSuppression().Notice("Loading legacy pak '%s'...", pak.path.c_str()); } - } else if (pak.type == pakType_t::PAK_DIR) { + } else if (isDir) { if (!isLegacy) { fsLogs.WithoutSuppression().Notice("Loading pakdir '%s'...", pak.path.c_str()); } else { @@ -1292,7 +1328,14 @@ static void InternalLoadPak( loadedPak.path = pak.path; // Update the list of files, but don't overwrite existing files, so the sort order is preserved - if (pak.type == pakType_t::PAK_DIR) { + if (isBuiltin) { + loadedPak.fd = -1; + for (auto& it : builtinPakMap[pak.name]) + { + Log::Debug("Adding file from builtin pak %s: %s", pak.name, it.first); + fileMap.emplace(it.first, std::pair(loadedPaks.size() - 1, 0)); + } + } else if (isDir) { loadedPak.fd = -1; auto dirRange = RawPath::ListFilesRecursive(pak.path, err); if (err) @@ -1316,7 +1359,7 @@ static void InternalLoadPak( if (err) return; } - } else if (pak.type == pakType_t::PAK_ZIP) { + } else if (isZip) { // Open file loadedPak.fd = my_open(pak.path, openMode_t::MODE_READ); if (loadedPak.fd == -1) { @@ -1380,7 +1423,7 @@ static void InternalLoadPak( // Directories (aka a dpkdir) don't need timestamp. // Fixes Windows bug where calling _wstat64i with trailing slash causes "file not found" error. // For future stat calls on directories, trim the trailing slash (if exists) - if (pak.type == pakType_t::PAK_ZIP) { + if (isZip) { loadedPak.timestamp = FS::RawPath::FileTimestamp(pak.path, err); if (err) return; @@ -1388,8 +1431,8 @@ static void InternalLoadPak( loadedPak.pathPrefix = pathPrefix; - // Legacy paks don't have version neither checksum - if (!isLegacy) { + // Legacy and builtin paks don't have version neither checksum + if (!isLegacy && !isBuiltin) { // If an explicit checksum was requested, verify that the pak we loaded is the one we are expecting if (expectedChecksum && realChecksum != *expectedChecksum) { SetErrorCodeFilesystem(err, filesystem_error::wrong_pak_checksum, pak.path); @@ -1406,14 +1449,16 @@ static void InternalLoadPak( if (!isLegacy) { if (hasDeleted) { std::string deletedData; - if (pak.type == pakType_t::PAK_DIR) { + if (isBuiltin) { + // Not implemented. + } else if (isDir) { File depsFile = RawPath::OpenRead(Path::Build(pak.path, PAK_DELETED_FILE), err); if (err) return; deletedData = depsFile.ReadAll(err); if (err) return; - } else if (pak.type == pakType_t::PAK_ZIP) { + } else if (isZip) { zipFile.OpenFile(deletedOffset, err); if (err) return; @@ -1434,14 +1479,16 @@ static void InternalLoadPak( // Load dependencies (non-legacy paks (pk3) only) if (loadDeps && hasDeps) { std::string depsData; - if (pak.type == pakType_t::PAK_DIR) { + if (isBuiltin) { + // Not implemented. + } else if (isDir) { File depsFile = RawPath::OpenRead(Path::Build(pak.path, PAK_DEPS_FILE), err); if (err) return; depsData = depsFile.ReadAll(err); if (err) return; - } else if (pak.type == pakType_t::PAK_ZIP) { + } else if (isZip) { zipFile.OpenFile(depsOffset, err); if (err) return; @@ -1491,6 +1538,7 @@ void ClearPaks() close(x.fd); } loadedPaks.clear(); + FS::ClearBuiltinPaks(); FS::RefreshPaks(); } #else // BUILD_VM @@ -1549,7 +1597,18 @@ std::string ReadFile(Str::StringRef path, std::error_code& err) } const LoadedPakInfo& pak = loadedPaks[it->second.first]; - if (pak.type == pakType_t::PAK_DIR) { + + bool isBuiltin = pak.type == pakType_t::PAK_ZIP && pak.name[0] == '*'; + bool isDir = pak.type == pakType_t::PAK_DIR; + bool isZip = pak.type == pakType_t::PAK_ZIP && !isBuiltin; + + if (isBuiltin) { + const embeddedFileMapEntry_t& entry = builtinPakMap[pak.name][path]; + std::string out; + out.resize(entry.size); + memcpy(&out[0], entry.data, entry.size); + return out; + } else if (isDir) { // Open file File file = RawPath::OpenRead(Path::Build(pak.path, it->first), err); if (err) @@ -1565,7 +1624,7 @@ std::string ReadFile(Str::StringRef path, std::error_code& err) out.resize(length); file.Read(&out[0], length, err); return out; - } else if (pak.type == pakType_t::PAK_ZIP) { + } else if (isZip) { // Open zip ZipArchive zipFile = ZipArchive::Open(pak.fd, err); if (err) @@ -1604,12 +1663,19 @@ void CopyFile(Str::StringRef path, const File& dest, std::error_code& err) } const LoadedPakInfo& pak = loadedPaks[it->second.first]; - if (pak.type == pakType_t::PAK_DIR) { + + bool isBuiltin = pak.type == pakType_t::PAK_ZIP && pak.name[0] == '*'; + bool isDir = pak.type == pakType_t::PAK_DIR; + bool isZip = pak.type == pakType_t::PAK_ZIP && !isBuiltin; + + if (isBuiltin) { + // Not implemented + } else if (isDir) { File file = RawPath::OpenRead(Path::Build(pak.path, it->first), err); if (err) return; file.CopyTo(dest, err); - } else if (pak.type == pakType_t::PAK_ZIP) { + } else if (isZip) { // Open zip ZipArchive zipFile = ZipArchive::Open(pak.fd, err); if (err) @@ -1672,7 +1738,15 @@ std::chrono::system_clock::time_point FileTimestamp(Str::StringRef path, std::er } const LoadedPakInfo& pak = loadedPaks[it->second.first]; - if (pak.type == pakType_t::PAK_DIR) { + + bool isBuiltin = pak.type == pakType_t::PAK_ZIP && pak.name[0] == '*'; + bool isDir = pak.type == pakType_t::PAK_DIR; + bool isZip = pak.type == pakType_t::PAK_ZIP && !isBuiltin; + + if (isBuiltin) { + // Not implemented. + return {}; + } else if (isDir) { #ifdef BUILD_VM Util::optional result; VM::SendMsg(it->second.first, it->first, result); @@ -1686,7 +1760,7 @@ std::chrono::system_clock::time_point FileTimestamp(Str::StringRef path, std::er #else return RawPath::FileTimestamp(Path::Build(pak.path, it->first), err); #endif - } else if (pak.type == pakType_t::PAK_ZIP) { + } else if (isZip) { return pak.timestamp; } @@ -2636,6 +2710,10 @@ void RefreshPaks() if (a.checksum != b.checksum) return a.checksum > b.checksum; + // Prefer builtin packages to zip packages. + if (b.type == pakType_t::PAK_ZIP && a.type == pakType_t::PAK_ZIP && b.name[0] == '*') + return true; + // Prefer zip packages to directory packages if (b.type == pakType_t::PAK_ZIP && a.type != pakType_t::PAK_ZIP) return true; @@ -2660,6 +2738,16 @@ static const PakInfo* FindPakNoPrefix(Str::StringRef name) const PakInfo* FindPak(Str::StringRef name) { +#if defined(BUILD_ENGINE) + for ( auto& builtinPak : builtinPaks) + { + if ( builtinPak.name == name ) + { + return &builtinPak; + } + } +#endif + Cmd::Args pakprefixes(Cvar::GetValue("fs_pakprefixes")); for (const std::string &pakprefix: pakprefixes) { @@ -2715,7 +2803,12 @@ const PakInfo* FindPak(Str::StringRef name, Str::StringRef version, uint32_t che return checksum > pakInfo.checksum; }); - if (iter == availablePaks.begin() || (iter - 1)->name != name || (iter - 1)->version != version || !(iter - 1)->checksum || *(iter - 1)->checksum != checksum) { + if (iter == availablePaks.begin() + || (iter - 1)->name != name + || (iter - 1)->version != version + || !(iter - 1)->checksum + || *(iter - 1)->checksum != checksum) { + // Try again, but this time look for the pak without a checksum. We will verify the checksum later. iter = std::upper_bound(availablePaks.begin(), availablePaks.end(), name, [version](Str::StringRef name1, const PakInfo& pakInfo) -> bool { int result = name1.compare(pakInfo.name); @@ -2728,7 +2821,23 @@ const PakInfo* FindPak(Str::StringRef name, Str::StringRef version, uint32_t che }); // Only allow zip packages because directories don't have a checksum - if (iter == availablePaks.begin() || (iter - 1)->type == pakType_t::PAK_DIR || (iter - 1)->name != name || (iter - 1)->version != version || (iter - 1)->checksum) + if (iter == availablePaks.begin()) + return nullptr; + + PakInfo& pak = (iter - 1)[0]; + + bool isBuiltin = pak.type == pakType_t::PAK_ZIP && pak.name[ 0 ] == '*'; + bool isDir = pak.type == pakType_t::PAK_DIR; + + if (isBuiltin) + return nullptr; + if (isDir) + return nullptr; + if (pak.name != name) + return nullptr; + if (pak.version != version) + return nullptr; + if (pak.checksum) return nullptr; } @@ -2944,12 +3053,19 @@ void HandleFileSystemSyscall(int minor, Util::Reader& reader, IPC::Channel& chan case VM::FS_PAKPATH_TIMESTAMP: IPC::HandleMsg(channel, std::move(reader), [](uint32_t pakIndex, std::string path, Util::optional& out) { auto& loadedPaks = FS::PakPath::GetLoadedPaks(); + if (loadedPaks.size() <= pakIndex) return; - if (loadedPaks[pakIndex].type == pakType_t::PAK_ZIP) + + const LoadedPakInfo& pak = loadedPaks[pakIndex]; + bool isDir = pak.type == pakType_t::PAK_DIR; + + if (!isDir) return; + if (!Path::IsValid(path, false)) return; + std::error_code err; std::chrono::system_clock::time_point t = RawPath::FileTimestamp(Path::Build(loadedPaks[pakIndex].path, path), err); if (!err) diff --git a/src/common/FileSystem.h b/src/common/FileSystem.h index 725afa66d8..a3a709be6d 100644 --- a/src/common/FileSystem.h +++ b/src/common/FileSystem.h @@ -37,6 +37,10 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "IPC/Channel.h" #endif +#if defined(BUILD_ENGINE) +#include DAEMON_EMBEDDED_FILES_HEADER +#endif + namespace FS { bool UseLegacyPaks(); @@ -265,7 +269,7 @@ template class DirectoryIterator { // Type of pak enum class pakType_t { PAK_ZIP, // Zip archive - PAK_DIR // Directory + PAK_DIR, // Directory }; // Information about a package @@ -308,6 +312,12 @@ struct LoadedPakInfo: public PakInfo { std::string pathPrefix; }; +#if defined(BUILD_ENGINE) +using builtinPakMap_t = std::unordered_map; + +void AddBuiltinPak(const std::string& name, const std::string& version, const embeddedFileMap_t& map); +#endif + // Operations which work on files that are in packages. Packages should be used // for read-only assets which can be distributed by auto-download. namespace PakPath { diff --git a/src/engine/client/cl_main.cpp b/src/engine/client/cl_main.cpp index 037dec1f87..95a04f7a8d 100644 --- a/src/engine/client/cl_main.cpp +++ b/src/engine/client/cl_main.cpp @@ -2093,8 +2093,6 @@ bool CL_InitRenderer() return false; } - fileHandle_t f; - // this sets up the renderer and calls R_Init if ( !re.BeginRegistration( &cls.windowConfig ) ) { @@ -2105,39 +2103,49 @@ bool CL_InitRenderer() cl_consoleFontSize = Cvar_Get( "cl_consoleFontSize", "16", CVAR_LATCH ); cl_consoleFontScaling = Cvar_Get( "cl_consoleFontScaling", "1", CVAR_LATCH ); - // load character sets - cls.charSetShader = re.RegisterShader( "gfx/2d/bigchars", RSF_2D ); - cls.useLegacyConsoleFont = cls.useLegacyConsoleFace = true; +#if defined(BUILD_GRAPHICAL_CLIENT) + fileHandle_t f; // Register console font specified by cl_consoleFont, if any // filehandle is unused but forces FS_FOpenFileRead() to heed purecheck because it does not when filehandle is nullptr if ( cl_consoleFont->string[0] ) { - if ( FS_FOpenFileRead( cl_consoleFont->string, &f ) >= 0 ) + if ( FS_FOpenFileRead( cl_consoleFont->string, &f ) < 0 ) { - if ( cl_consoleFontScaling->value == 0 ) - { - cls.consoleFont = re.RegisterFont( cl_consoleFont->string, cl_consoleFontSize->integer ); - } - else - { - // This gets 12px on 1920×1080 screen, which is libRocket default for 1em - int fontScale = std::min(cls.windowConfig.vidWidth, cls.windowConfig.vidHeight) / 90; + Log::Warn("Font file '%s' not found", cl_consoleFont->string); - // fontScale / 12px gets 1px on 1920×1080 screen - cls.consoleFont = re.RegisterFont( cl_consoleFont->string, cl_consoleFontSize->integer * fontScale / 12 ); - } + Cvar::ClearFlags("cl_consoleFont", CVAR_LATCH); + Cvar_Set( "cl_consoleFont", "fonts/unifont.ttf" ); + Cvar::AddFlags("cl_consoleFont", CVAR_LATCH); + } - if ( cls.consoleFont != nullptr ) - cls.useLegacyConsoleFont = false; + if ( cl_consoleFontScaling->value == 0 ) + { + cls.consoleFont = re.RegisterFont( cl_consoleFont->string, cl_consoleFontSize->integer ); } else { - Log::Warn("Font file '%s' not found", cl_consoleFont->string); + // This gets 12px on 1920×1080 screen, which is libRocket default for 1em + int fontScale = std::min(cls.windowConfig.vidWidth, cls.windowConfig.vidHeight) / 90; + + // fontScale / 12px gets 1px on 1920×1080 screen + cls.consoleFont = re.RegisterFont( cl_consoleFont->string, cl_consoleFontSize->integer * fontScale / 12 ); + } + + if ( cls.consoleFont == nullptr ) + { + // Can this happen? + cls.consoleFont = re.RegisterFont( cl_consoleFont->string, cl_consoleFontSize->integer ); + + if ( cls.consoleFont == nullptr ) + { + Sys::Error( "Failed to load font %s", cl_consoleFont->string ); + } } FS_FCloseFile( f ); } +#endif cls.whiteShader = re.RegisterShader( "white", RSF_NOMIP ); diff --git a/src/engine/client/cl_scrn.cpp b/src/engine/client/cl_scrn.cpp index b969bbd14b..ad51083863 100644 --- a/src/engine/client/cl_scrn.cpp +++ b/src/engine/client/cl_scrn.cpp @@ -107,12 +107,6 @@ static glyphInfo_t *Glyph( int ch ) void SCR_DrawConsoleFontUnichar( float x, float y, int ch ) { - if ( cls.useLegacyConsoleFont ) - { - SCR_DrawSmallUnichar( ( int ) x, ( int ) y, ch ); - return; - } - if ( ch != ' ' ) { glyphInfo_t *glyph = Glyph( ch ); @@ -126,53 +120,6 @@ void SCR_DrawConsoleFontUnichar( float x, float y, int ch ) } } -/* -** SCR_DrawSmallUnichar -** small chars are drawn at native screen resolution -*/ -void SCR_DrawSmallUnichar( int x, int y, int ch ) -{ - int row, col; - float frow, fcol; - float size; - - if ( ch < 0x100 || cls.useLegacyConsoleFont ) - { - if ( ch == ' ' ) { - return; - } - - if ( y < -SMALLCHAR_HEIGHT ) { - return; - } - - if ( ch >= 0x100 ) { ch = 0; } - - row = ch>>4; - col = ch&15; - - frow = row*0.0625; - fcol = col*0.0625; - size = 0.0625; - - // adjust for baseline - re.DrawStretchPic( x, y - (int)( SMALLCHAR_HEIGHT / ( CONSOLE_FONT_VPADDING + 1 ) ), - SMALLCHAR_WIDTH, SMALLCHAR_HEIGHT, - fcol, frow, - fcol + size, frow + size, - cls.charSetShader ); - } else { - glyphInfo_t *glyph = Glyph( ch ); - - re.DrawStretchPic( x, y, SMALLCHAR_WIDTH, glyph->imageHeight, - glyph->s, - glyph->t, - glyph->s2, - glyph->t2, - glyph->glyph ); - } -} - /* ================== SCR_DrawSmallString[Color] @@ -335,9 +282,7 @@ void SCR_UpdateScreen() float SCR_ConsoleFontUnicharWidth( int ch ) { - return cls.useLegacyConsoleFont - ? SMALLCHAR_WIDTH - : Glyph( ch )->xSkip + cl_consoleFontKerning->value; + return Glyph( ch )->xSkip + cl_consoleFontKerning->value; } float SCR_ConsoleFontCharWidth( const char *s ) @@ -347,44 +292,18 @@ float SCR_ConsoleFontCharWidth( const char *s ) float SCR_ConsoleFontCharHeight() { - return cls.useLegacyConsoleFont - ? SMALLCHAR_HEIGHT - : cls.consoleFont->glyphBlock[0][(unsigned)'I'].imageHeight + CONSOLE_FONT_VPADDING * cl_consoleFontSize->value; + return cls.consoleFont->glyphBlock[0][(unsigned)'I'].imageHeight + CONSOLE_FONT_VPADDING * cl_consoleFontSize->value; } float SCR_ConsoleFontCharVPadding() { - return cls.useLegacyConsoleFont - ? 0 - : std::max( 0, -cls.consoleFont->glyphBlock[0][(unsigned)'g'].bottom >> 6); + return std::max( 0, -cls.consoleFont->glyphBlock[0][(unsigned)'g'].bottom >> 6); } float SCR_ConsoleFontStringWidth( const char* s, int len ) { float width = 0; - if( cls.useLegacyConsoleFont ) - { - if( cls.useLegacyConsoleFace ) - { - return len * SMALLCHAR_WIDTH; - } - else - { - int l = 0; - const char *str = s; - - while( *str && len > 0 ) - { - l++; - str += Q_UTF8_Width( str ); - len--; - } - - return l * SMALLCHAR_WIDTH; - } - } - while( *s && len > 0 ) { width += SCR_ConsoleFontCharWidth( s ); diff --git a/src/engine/client/client.h b/src/engine/client/client.h index 3c64e4e7cc..7dcc195b32 100644 --- a/src/engine/client/client.h +++ b/src/engine/client/client.h @@ -317,8 +317,6 @@ struct clientStatic_t WindowConfig windowConfig; qhandle_t charSetShader; qhandle_t whiteShader; - bool useLegacyConsoleFont; - bool useLegacyConsoleFace; fontInfo_t *consoleFont; // www downloading @@ -650,7 +648,6 @@ void SCR_AdjustFrom640( float *x, float *y, float *w, float *h ); void SCR_FillRect( float x, float y, float width, float height, const Color::Color& color ); void SCR_DrawSmallStringExt( int x, int y, const char *string, const Color::Color& setColor, bool forceColor, bool noColorEscape ); -void SCR_DrawSmallUnichar( int x, int y, int ch ); void SCR_DrawConsoleFontUnichar( float x, float y, int ch ); float SCR_ConsoleFontCharWidth( const char *s ); float SCR_ConsoleFontUnicharWidth( int ch ); diff --git a/src/engine/media/fonts/unifont.ttf b/src/engine/media/fonts/unifont.ttf new file mode 100644 index 0000000000..764ee1f7e0 Binary files /dev/null and b/src/engine/media/fonts/unifont.ttf differ diff --git a/src/engine/media/scripts/engine.shader b/src/engine/media/scripts/engine.shader new file mode 100644 index 0000000000..d156edd2f3 --- /dev/null +++ b/src/engine/media/scripts/engine.shader @@ -0,0 +1,9 @@ +white +{ + cull none + { + map $white + blend blend + rgbgen vertex + } +} diff --git a/src/engine/qcommon/files.cpp b/src/engine/qcommon/files.cpp index e2b97a861e..6ecf918469 100644 --- a/src/engine/qcommon/files.cpp +++ b/src/engine/qcommon/files.cpp @@ -27,6 +27,10 @@ along with Daemon Source Code. If not, see . #include "qcommon.h" #include "common/Defs.h" +#if defined(BUILD_GRAPHICAL_CLIENT) || defined(BUILD_TTY_CLIENT) +#include "DaemonEmbeddedFiles/EngineMedia.h" +#endif + extern Log::Logger fsLogs; // There must be some limit for the APIs in this file since they use 'int' for lengths which can be overflowed by large files. @@ -626,6 +630,10 @@ const char* FS_LoadedPaks() static char info[BIG_INFO_STRING]; info[0] = '\0'; for (const FS::LoadedPakInfo& x: FS::PakPath::GetLoadedPaks()) { + bool isBuiltin = x.type == FS::pakType_t::PAK_ZIP && x.name[0] == '*'; + + if (isBuiltin) + continue; if (!x.pathPrefix.empty()) continue; if (info[0]) @@ -653,8 +661,20 @@ bool FS_LoadPak(const Str::StringRef name) } } +/* Empty base pak available on all engines. +It makes possible to run the engine without a game, +this way: +./daemon -set fs_basepak '*daemon' */ +embeddedFileMap_t engineBasePak = {}; + void FS_LoadBasePak() { + FS::AddBuiltinPak("daemon", ENGINE_VERSION, engineBasePak); + +#if defined(BUILD_GRAPHICAL_CLIENT) || defined(BUILD_TTY_CLIENT) + FS::AddBuiltinPak("daemon-client", ENGINE_VERSION, EngineMedia::FileMap); +#endif + Cmd::Args extrapaks(fs_extrapaks.Get()); for (auto& x: extrapaks) { if (!FS_LoadPak(x)) { diff --git a/src/engine/renderer/gl_shader.cpp b/src/engine/renderer/gl_shader.cpp index cc6bc46432..f58cfd5cf2 100644 --- a/src/engine/renderer/gl_shader.cpp +++ b/src/engine/renderer/gl_shader.cpp @@ -24,6 +24,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #include #include "gl_shader.h" #include "Material.h" +#include "DaemonEmbeddedFiles/EngineShaders.h" // We currently write GLBinaryHeader to a file and memcpy all over it. // Make sure it's a pod, so we don't put a std::string in it or something @@ -41,7 +42,6 @@ static Cvar::Cvar r_logUnmarkedGLSLBuilds( "r_logUnmarkedGLSLBuilds", "Log building information for GLSL shaders that are built after the map is loaded", Cvar::NONE, true ); -extern const std::unordered_map shadermap; // shaderKind's value will be determined later based on command line setting or absence of. ShaderKind shaderKind = ShaderKind::Unknown; @@ -92,9 +92,9 @@ namespace // Implementation details const char* GetInternalShader(Str::StringRef filename) { - auto it = shadermap.find(filename); - if (it != shadermap.end()) - return it->second.c_str(); + auto it = EngineShaders::FileMap.find(filename); + if (it != EngineShaders::FileMap.end()) + return it->second.data; return nullptr; } diff --git a/src/engine/renderer/src.cmake b/src/engine/renderer/src.cmake index ff85e9872a..98ef800b31 100644 --- a/src/engine/renderer/src.cmake +++ b/src/engine/renderer/src.cmake @@ -1,6 +1,5 @@ set(RENDERERLIST - ${DAEMON_GENERATED_DIR}/shaders.cpp ${ENGINE_DIR}/renderer/BufferBind.h ${ENGINE_DIR}/renderer/DetectGLVendors.cpp ${ENGINE_DIR}/renderer/DetectGLVendors.h