From da0c6df031fe1b0e1cf34f1236edb0995eb15504 Mon Sep 17 00:00:00 2001 From: Frank Richter Date: Sat, 20 Mar 2021 13:56:05 +0100 Subject: [PATCH 01/11] Don't link 'server' against SDL --- src/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 8aff22993..3b986198d 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -489,10 +489,10 @@ TARGET_INCLUDE_DIRECTORIES(server PRIVATE "${ZLIB_INCLUDE_DIRS}") # Use dynamic zlib for steam runtime if (CONFIG_LINUX_STEAM_RUNTIME_SUPPORT) TARGET_LINK_LIBRARIES(client SDL2main SDL2-static z) - TARGET_LINK_LIBRARIES(server SDL2main SDL2-static z) + TARGET_LINK_LIBRARIES(server z) else() TARGET_LINK_LIBRARIES(client SDL2main SDL2-static zlibstatic) - TARGET_LINK_LIBRARIES(server SDL2main SDL2-static zlibstatic) + TARGET_LINK_LIBRARIES(server zlibstatic) endif() SET_TARGET_PROPERTIES(client From f0d31547ada4c04f113c12efd7859e2347342b33 Mon Sep 17 00:00:00 2001 From: Frank Richter Date: Sat, 20 Mar 2021 14:35:50 +0100 Subject: [PATCH 02/11] Also build an x86 version of the dedicated server --- src/CMakeLists.txt | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 3b986198d..c7b7761fc 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -497,7 +497,7 @@ endif() SET_TARGET_PROPERTIES(client PROPERTIES - OUTPUT_NAME "q2rtx" + OUTPUT_NAME "q2rtx${BINARY_POSTFIX}" RUNTIME_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}" RUNTIME_OUTPUT_DIRECTORY_DEBUG "${CMAKE_SOURCE_DIR}" RUNTIME_OUTPUT_DIRECTORY_RELEASE "${CMAKE_SOURCE_DIR}" @@ -508,7 +508,7 @@ SET_TARGET_PROPERTIES(client SET_TARGET_PROPERTIES(server PROPERTIES - OUTPUT_NAME "q2rtxded" + OUTPUT_NAME "q2rtxded${BINARY_POSTFIX}" RUNTIME_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}" RUNTIME_OUTPUT_DIRECTORY_DEBUG "${CMAKE_SOURCE_DIR}" RUNTIME_OUTPUT_DIRECTORY_RELEASE "${CMAKE_SOURCE_DIR}" @@ -543,6 +543,32 @@ IF(IS_64_BIT) ) ENDIF() +include(ProcessorCount) + +if(WIN32 AND IS_64_BIT) + set(X86_BUILD_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/build_x86") + file(MAKE_DIRECTORY "${X86_BUILD_DIRECTORY}") + set(OUTPUT_FILE_NAME "${CMAKE_CURRENT_BINARY_DIR}/config_x86_out.txt") + set(ERROR_FILE_NAME "${CMAKE_CURRENT_BINARY_DIR}/config_x86_err.txt") + execute_process(COMMAND "${CMAKE_COMMAND}" "${CMAKE_SOURCE_DIR}" "-G" "${CMAKE_GENERATOR}" "-A" "Win32" "-DBINARY_POSTFIX=_x86" + WORKING_DIRECTORY "${X86_BUILD_DIRECTORY}" + OUTPUT_FILE "${OUTPUT_FILE_NAME}" + ERROR_FILE "${ERROR_FILE_NAME}" + RESULT_VARIABLE CONFIG_RESULT) + if(NOT CONFIG_RESULT EQUAL 0) + message(WARNING "Configuring X86 dedicated server failed with exit code ${CONFIG_RESULT}") + message(STATUS " Please see ${OUTPUT_FILE_NAME} and ${ERROR_FILE_NAME} for output") + else() + ProcessorCount(N) + if(N GREATER 0) + set(PARALLEL_ARGS "-j" "${N}") + endif() + add_custom_target(server_x86 ALL + COMMAND "${CMAKE_COMMAND}" "--build" "${X86_BUILD_DIRECTORY}" "--target" "server" "--config" "$" ${PARALLEL_ARGS} + COMMENT "Build X86 dedicated server") + endif() +endif() + IF(CONFIG_LINUX_PACKAGING_SUPPORT) # Put the real game binary in /usr/share so we can have a wrapper in /usr/bin INSTALL(TARGETS client DESTINATION share/quake2rtx/bin COMPONENT shareware) From 6fbdc98ff39ead6f3f4db7ff1ea175073d99f4ba Mon Sep 17 00:00:00 2001 From: Frank Richter Date: Sat, 20 Mar 2021 15:25:50 +0100 Subject: [PATCH 03/11] Change configuration handling to always execute default.cfg, config.cfg again ...but also always execute baseq2/autoexec.cfg, if present. This hopefully strikes a balance between "user preference" and "properly support mods" - they may set things in default.cfg that are required to make things work. --- src/common/files.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/common/files.c b/src/common/files.c index 0670bbf2c..98a9b6892 100644 --- a/src/common/files.c +++ b/src/common/files.c @@ -3690,13 +3690,15 @@ static void fs_game_changed(cvar_t *self) // otherwise, restart the filesystem CL_RestartFilesystem(qfalse); - // FIXME: if baseq2/autoexec.cfg exists DO NOT exec default.cfg and config.cfg. - // this assumes user prefers to do configuration via autoexec.cfg and doesn't - // want settings and binds messed up whenever gamedir changes after startup. - if (!FS_FileExistsEx(COM_AUTOEXEC_CFG, FS_TYPE_REAL | FS_PATH_BASE)) { - Com_AddConfigFile(COM_DEFAULT_CFG, FS_PATH_GAME); - Com_AddConfigFile(COM_Q2RTX_CFG, 0); - Com_AddConfigFile(COM_CONFIG_CFG, FS_TYPE_REAL | FS_PATH_GAME); + Com_AddConfigFile(COM_DEFAULT_CFG, FS_PATH_GAME); + Com_AddConfigFile(COM_Q2RTX_CFG, 0); + Com_AddConfigFile(COM_CONFIG_CFG, FS_TYPE_REAL | FS_PATH_GAME); + + // If baseq2/autoexec.cfg exists exec it again after default.cfg and config.cfg. + // Assumes user prefers to do configuration via autoexec.cfg and hopefully + // settings and binds will be restored to their preference whenever gamedir changes after startup. + if(Q_stricmp(s, BASEGAME) && FS_FileExistsEx(COM_AUTOEXEC_CFG, FS_TYPE_REAL | FS_PATH_BASE)) { + Com_AddConfigFile(COM_AUTOEXEC_CFG, FS_TYPE_REAL | FS_PATH_BASE); } // exec autoexec.cfg (must be a real file within the game directory) From c646db24e2d5f3c9e09540cc7404232de57cec6a Mon Sep 17 00:00:00 2001 From: Frank Richter Date: Sat, 20 Mar 2021 18:39:47 +0100 Subject: [PATCH 04/11] files: Add FS_LoadFileFlags() macro --- inc/common/files.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/inc/common/files.h b/inc/common/files.h index 26a92027a..3a351ee51 100644 --- a/inc/common/files.h +++ b/inc/common/files.h @@ -87,6 +87,8 @@ typedef struct file_info_s { #define FS_Mallocz(size) Z_TagMallocz(size, TAG_FILESYSTEM) #define FS_CopyString(string) Z_TagCopyString(string, TAG_FILESYSTEM) #define FS_LoadFile(path, buf) FS_LoadFileEx(path, buf, 0, TAG_FILESYSTEM) +#define FS_LoadFileFlags(path, buf, flags) \ + FS_LoadFileEx(path, buf, (flags), TAG_FILESYSTEM) #define FS_FreeFile(buf) Z_Free(buf) // just regular malloc for now From b8011b1fb845a4f8f8c5c719846ea28a4aae2814 Mon Sep 17 00:00:00 2001 From: Frank Richter Date: Sat, 20 Mar 2021 18:40:00 +0100 Subject: [PATCH 05/11] When loading images, always give priority to files from game directory ...even if their format is 'worse' (ie PCX). This behaviour prevents image from baseq2 to override images from a game, even if it's customized there, but with a format of lower precedence. This ensures the correct 'aesthetic' is applied. --- src/refresh/images.c | 191 +++++++++++++++++++++++++------------------ 1 file changed, 111 insertions(+), 80 deletions(-) diff --git a/src/refresh/images.c b/src/refresh/images.c index 1e66a9fa4..ce3ef56db 100644 --- a/src/refresh/images.c +++ b/src/refresh/images.c @@ -785,14 +785,20 @@ static image_t *lookup_image(const char *name, return NULL; } -static int _try_image_format(imageformat_t fmt, image_t *image, byte **pic) +#define TRY_IMAGE_SRC_GAME 1 +#define TRY_IMAGE_SRC_BASE 0 + +static int _try_image_format(imageformat_t fmt, image_t *image, int try_src, byte **pic) { byte *data; ssize_t len; qerror_t ret; // load the file - len = FS_LoadFile(image->name, (void **)&data); + int fs_flags = 0; + if (try_src > 0) + fs_flags = try_src == TRY_IMAGE_SRC_GAME ? FS_PATH_GAME : FS_PATH_BASE; + len = FS_LoadFileFlags(image->name, (void **)&data, fs_flags); if (!data) { return len; } @@ -812,16 +818,16 @@ static int _try_image_format(imageformat_t fmt, image_t *image, byte **pic) return ret < 0 ? ret : fmt; } -static int try_image_format(imageformat_t fmt, image_t *image, byte **pic) +static int try_image_format(imageformat_t fmt, image_t *image, int try_src, byte **pic) { // replace the extension memcpy(image->name + image->baselen + 1, img_loaders[fmt].ext, 4); - return _try_image_format(fmt, image, pic); + return _try_image_format(fmt, image, try_src, pic); } // tries to load the image with a different extension -static int try_other_formats(imageformat_t orig, image_t *image, byte **pic) +static int try_other_formats(imageformat_t orig, image_t *image, int try_src, byte **pic) { imageformat_t fmt; qerror_t ret; @@ -834,7 +840,7 @@ static int try_other_formats(imageformat_t orig, image_t *image, byte **pic) continue; // don't retry twice } - ret = try_image_format(fmt, image, pic); + ret = try_image_format(fmt, image, try_src, pic); if (ret != Q_ERR_NOENT) { return ret; // found something } @@ -846,7 +852,7 @@ static int try_other_formats(imageformat_t orig, image_t *image, byte **pic) return Q_ERR_NOENT; // don't retry twice } - return try_image_format(fmt, image, pic); + return try_image_format(fmt, image, try_src, pic); } static void get_image_dimensions(imageformat_t fmt, image_t *image) @@ -957,11 +963,19 @@ load_img(const char *name, image_t *image) // load the pic from disk pic = NULL; - // first try with original extension - ret = _try_image_format(fmt, image, &pic); - if (ret == Q_ERR_NOENT) { - // retry with remaining extensions - ret = try_other_formats(fmt, image, &pic); + // Always prefer images from the game dir, even if format might be 'inferior' + for (int try_location = Q_stricmp(fs_game->string, BASEGAME) ? TRY_IMAGE_SRC_GAME : TRY_IMAGE_SRC_BASE; + try_location >= TRY_IMAGE_SRC_BASE; + try_location--) + { + // first try with original extension + ret = _try_image_format(fmt, image, try_location, &pic); + if (ret == Q_ERR_NOENT) { + // retry with remaining extensions + ret = try_other_formats(fmt, image, try_location, &pic); + } + if (ret >= 0) + break; } // if we are replacing 8-bit texture with a higher resolution 32-bit @@ -1029,80 +1043,97 @@ static qerror_t find_or_load_image(const char *name, size_t len, if (!vid_rtx->integer && (type != IT_PIC)) override_textures = 0; - for (int use_override = override_textures; use_override >= 0; use_override--) - { - // fill in some basic info - if (use_override) - { - const char* last_slash = strrchr(name, '/'); - if (!last_slash) - last_slash = name; - else - last_slash += 1; - - strcpy(image->name, "overrides/"); - strcat(image->name, last_slash); - image->baselen = strlen(image->name) - 4; - } - else - { - memcpy(image->name, name, len + 1); - image->baselen = len - 4; - } - image->type = type; - image->flags = flags; - image->registration_sequence = registration_sequence; - - // find out original extension - for (fmt = 0; fmt < IM_MAX; fmt++) { - if (!Q_stricmp(image->name + image->baselen + 1, img_loaders[fmt].ext)) { - break; - } - } + // Always prefer images from the game dir, even if format might be 'inferior' + for (int try_location = Q_stricmp(fs_game->string, BASEGAME) ? TRY_IMAGE_SRC_GAME : TRY_IMAGE_SRC_BASE; + try_location >= TRY_IMAGE_SRC_BASE; + try_location--) + { + for (int use_override = override_textures; use_override >= 0; use_override--) + { + // fill in some basic info + if (use_override) + { + const char *last_slash = strrchr(name, '/'); + if (!last_slash) + last_slash = name; + else + last_slash += 1; - // load the pic from disk - pic = NULL; + strcpy(image->name, "overrides/"); + strcat(image->name, last_slash); + image->baselen = strlen(image->name) - 4; + } + else + { + memcpy(image->name, name, len + 1); + image->baselen = len - 4; + } + image->type = type; + image->flags = flags; + image->registration_sequence = registration_sequence; + + // find out original extension + for (fmt = 0; fmt < IM_MAX; fmt++) + { + if (!Q_stricmp(image->name + image->baselen + 1, img_loaders[fmt].ext)) + { + break; + } + } - if (fmt == IM_MAX) { - // unknown extension, but give it a chance to load anyway - ret = try_other_formats(IM_MAX, image, &pic); - if (ret == Q_ERR_NOENT) { - // not found, change error to invalid path - ret = Q_ERR_INVALID_PATH; - } - } - else if (override_textures) { - // forcibly replace the extension - ret = try_other_formats(IM_MAX, image, &pic); - } - else { - // first try with original extension - ret = _try_image_format(fmt, image, &pic); - if (ret == Q_ERR_NOENT) { - // retry with remaining extensions - ret = try_other_formats(fmt, image, &pic); - } - } + // load the pic from disk + pic = NULL; + + if (fmt == IM_MAX) + { + // unknown extension, but give it a chance to load anyway + ret = try_other_formats(IM_MAX, image, try_location, &pic); + if (ret == Q_ERR_NOENT) + { + // not found, change error to invalid path + ret = Q_ERR_INVALID_PATH; + } + } + else if (override_textures) + { + // forcibly replace the extension + ret = try_other_formats(IM_MAX, image, try_location, &pic); + } + else + { + // first try with original extension + ret = _try_image_format(fmt, image, try_location, &pic); + if (ret == Q_ERR_NOENT) + { + // retry with remaining extensions + ret = try_other_formats(fmt, image, try_location, &pic); + } + } - // record last modified time (skips reload when invoking IMG_ReloadAll) - image->last_modified = 0; - FS_LastModified(image->name, &image->last_modified); + // record last modified time (skips reload when invoking IMG_ReloadAll) + image->last_modified = 0; + FS_LastModified(image->name, &image->last_modified); - if (use_override) - { - memcpy(image->name, name, len + 1); - image->baselen = len - 4; - } + if (use_override) + { + memcpy(image->name, name, len + 1); + image->baselen = len - 4; + } - // if we are replacing 8-bit texture with a higher resolution 32-bit - // texture, we need to recover original image dimensions - if (fmt <= IM_WAL && ret > IM_WAL) { - get_image_dimensions(fmt, image); - } + // if we are replacing 8-bit texture with a higher resolution 32-bit + // texture, we need to recover original image dimensions + if (fmt <= IM_WAL && ret > IM_WAL) + { + get_image_dimensions(fmt, image); + } - if(ret >= 0) - break; - } + if (ret >= 0) + break; + } + + if (ret >= 0) + break; + } if (ret < 0) { memset(image, 0, sizeof(*image)); From f23f2f7f4bb079f2e7cef8b778f794b91b722fd5 Mon Sep 17 00:00:00 2001 From: Frank Richter Date: Sat, 20 Mar 2021 20:30:25 +0100 Subject: [PATCH 06/11] Allow dedicated servers to generate patched PVS as well --- src/common/bsp.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/common/bsp.c b/src/common/bsp.c index a33b69c95..6b52798a8 100644 --- a/src/common/bsp.c +++ b/src/common/bsp.c @@ -1188,11 +1188,7 @@ qerror_t BSP_Load(const char *name, bsp_t **bsp_p) if (!BSP_LoadPatchedPVS(bsp)) { - if (dedicated->integer) - Com_WPrintf("WARNING: Pathced PVS file for %s unavailable. Some entities may disappear.\n" - "Load the map with the RTX renderer once to generate the patched PVS file.\n", bsp->name); - else - BSP_BuildPvsMatrix(bsp); + BSP_BuildPvsMatrix(bsp); } else { From 162047b70f7804d8cf657d93b05e79ca45a8bff6 Mon Sep 17 00:00:00 2001 From: Frank Richter Date: Tue, 13 Apr 2021 01:37:14 +0200 Subject: [PATCH 07/11] When loading models, always give priority to files from game directory This behaviour prevents md3s from baseq2 to override md2s from a game, allowing mods to, well, modify appearances. --- src/refresh/models.c | 33 ++++++++++++++++++++++++++------- 1 file changed, 26 insertions(+), 7 deletions(-) diff --git a/src/refresh/models.c b/src/refresh/models.c index 6eaccee6c..6ff7d429b 100644 --- a/src/refresh/models.c +++ b/src/refresh/models.c @@ -302,6 +302,9 @@ static qerror_t MOD_LoadSP2(model_t *model, const void *rawdata, size_t length) return Q_ERR_SUCCESS; } +#define TRY_MODEL_SRC_GAME 1 +#define TRY_MODEL_SRC_BASE 0 + qhandle_t R_RegisterModel(const char *name) { char normalized[MAX_QPATH]; @@ -344,15 +347,31 @@ qhandle_t R_RegisterModel(const char *name) goto done; } - char* extension = normalized + namelen - 4; - if (namelen > 4 && (strcmp(extension, ".md2") == 0) && vid_rtx->integer) - { - memcpy(extension, ".md3", 4); + // Always prefer models from the game dir, even if format might be 'inferior' + for (int try_location = Q_stricmp(fs_game->string, BASEGAME) ? TRY_MODEL_SRC_GAME : TRY_MODEL_SRC_BASE; + try_location >= TRY_MODEL_SRC_BASE; + try_location--) + { + int fs_flags = 0; + if (try_location > 0) + fs_flags = try_location == TRY_MODEL_SRC_GAME ? FS_PATH_GAME : FS_PATH_BASE; - filelen = FS_LoadFile(normalized, (void **)&rawdata); + char* extension = normalized + namelen - 4; + if (namelen > 4 && (strcmp(extension, ".md2") == 0) && vid_rtx->integer) + { + memcpy(extension, ".md3", 4); - memcpy(extension, ".md2", 4); - } + filelen = FS_LoadFileFlags(normalized, (void **)&rawdata, fs_flags); + + memcpy(extension, ".md2", 4); + } + if (!rawdata) + { + filelen = FS_LoadFileFlags(normalized, (void **)&rawdata, fs_flags); + } + if (rawdata) + break; + } if (!rawdata) { From b2e328901b8f5866be4f5abf960bfedd3c207dba Mon Sep 17 00:00:00 2001 From: Frank Richter Date: Tue, 13 Apr 2021 20:23:27 +0200 Subject: [PATCH 08/11] Share code to load additional material images (normalmap, emissive) --- src/refresh/vkpt/bsp_mesh.c | 36 +++++++----------------- src/refresh/vkpt/models.c | 56 ++++--------------------------------- src/refresh/vkpt/textures.c | 47 +++++++++++++++++++------------ src/refresh/vkpt/vkpt.h | 7 +++++ 4 files changed, 52 insertions(+), 94 deletions(-) diff --git a/src/refresh/vkpt/bsp_mesh.c b/src/refresh/vkpt/bsp_mesh.c index f325502fa..edc404535 100644 --- a/src/refresh/vkpt/bsp_mesh.c +++ b/src/refresh/vkpt/bsp_mesh.c @@ -1792,37 +1792,21 @@ bsp_mesh_register_textures(bsp_t *bsp) if (!mat) Com_EPrintf("error finding material '%s'\n", buffer); - image_t* image_diffuse = IMG_Find(buffer, IT_WALL, flags | IF_SRGB); - image_t* image_normals = NULL; - image_t* image_emissive = NULL; + vkpt_material_images_t images; + vkpt_load_material_images(&images, buffer, IT_WALL, flags); - if (image_diffuse != R_NOTEXTURE) + if (images.normals && !images.normals->processing_complete) { - // attempt loading the second texture - Q_concat(buffer, sizeof(buffer), "textures/", info->name, "_n.tga", NULL); - FS_NormalizePath(buffer, buffer); - image_normals = IMG_Find(buffer, IT_WALL, flags); - if (image_normals == R_NOTEXTURE) image_normals = NULL; - - if (image_normals && !image_normals->processing_complete) - { - vkpt_normalize_normal_map(image_normals); - } - - // attempt loading the emissive texture - Q_concat(buffer, sizeof(buffer), "textures/", info->name, "_light.tga", NULL); - FS_NormalizePath(buffer, buffer); - image_emissive = IMG_Find(buffer, IT_WALL, flags | IF_SRGB); - if (image_emissive == R_NOTEXTURE) image_emissive = NULL; - - if (image_emissive && !image_emissive->processing_complete && (mat->emissive_scale > 0.f) && ((mat->flags & MATERIAL_FLAG_LIGHT) != 0 || MAT_IsKind(mat->flags, MATERIAL_KIND_LAVA))) - { - vkpt_extract_emissive_texture_info(image_emissive); - } + vkpt_normalize_normal_map(images.normals); + } + + if (images.emissive && !images.emissive->processing_complete && (mat->emissive_scale > 0.f) && ((mat->flags & MATERIAL_FLAG_LIGHT) != 0 || MAT_IsKind(mat->flags, MATERIAL_KIND_LAVA))) + { + vkpt_extract_emissive_texture_info(images.emissive); } // finish registration - MAT_RegisterPBRMaterial(mat, image_diffuse, image_normals, image_emissive); + MAT_RegisterPBRMaterial(mat, images.diffuse, images.normals, images.emissive); info->material = mat; } diff --git a/src/refresh/vkpt/models.c b/src/refresh/vkpt/models.c index a5d716b03..aa06413bc 100644 --- a/src/refresh/vkpt/models.c +++ b/src/refresh/vkpt/models.c @@ -258,32 +258,10 @@ qerror_t MOD_LoadMD2_RTX(model_t *model, const void *rawdata, size_t length) if (!mat) Com_EPrintf("error finding material '%s'\n", skinname); - image_t* image_diffuse = IMG_Find(skinname, IT_SKIN, IF_SRGB); - image_t* image_normals = NULL; - image_t* image_emissive = NULL; + vkpt_material_images_t images; + vkpt_load_material_images(&images, skinname, IT_SKIN, IF_NONE); - if (image_diffuse != R_NOTEXTURE) - { - // attempt loading the normals texture - if (!Q_strlcpy(skinname, src_skin, strlen(src_skin) - 3)) - return Q_ERR_STRING_TRUNCATED; - - Q_concat(skinname, sizeof(skinname), skinname, "_n.tga", NULL); - FS_NormalizePath(skinname, skinname); - image_normals = IMG_Find(skinname, IT_SKIN, IF_NONE); - if (image_normals == R_NOTEXTURE) image_normals = NULL; - - // attempt loading the emissive texture - if (!Q_strlcpy(skinname, src_skin, strlen(src_skin) - 3)) - return Q_ERR_STRING_TRUNCATED; - - Q_concat(skinname, sizeof(skinname), skinname, "_light.tga", NULL); - FS_NormalizePath(skinname, skinname); - image_emissive = IMG_Find(skinname, IT_SKIN, IF_SRGB); - if (image_emissive == R_NOTEXTURE) image_emissive = NULL; - } - - MAT_RegisterPBRMaterial(mat, image_diffuse, image_normals, image_emissive); + MAT_RegisterPBRMaterial(mat, images.diffuse, images.normals, images.emissive); dst_mesh->materials[i] = mat; @@ -468,32 +446,10 @@ static qerror_t MOD_LoadMD3Mesh(model_t *model, maliasmesh_t *mesh, if (!mat) Com_EPrintf("error finding material '%s'\n", skinname); - image_t* image_diffuse = IMG_Find(skinname, IT_SKIN, IF_SRGB); - image_t* image_normals = NULL; - image_t* image_emissive = NULL; - - if (image_diffuse != R_NOTEXTURE) - { - // attempt loading the normals texture - if (!Q_strlcpy(skinname, src_skin->name, strlen(src_skin->name) - 3)) - return Q_ERR_STRING_TRUNCATED; - - Q_concat(skinname, sizeof(skinname), skinname, "_n.tga", NULL); - FS_NormalizePath(skinname, skinname); - image_normals = IMG_Find(skinname, IT_SKIN, IF_NONE); - if (image_normals == R_NOTEXTURE) image_normals = NULL; - - // attempt loading the emissive texture - if (!Q_strlcpy(skinname, src_skin->name, strlen(src_skin->name) - 3)) - return Q_ERR_STRING_TRUNCATED; - - Q_concat(skinname, sizeof(skinname), skinname, "_light.tga", NULL); - FS_NormalizePath(skinname, skinname); - image_emissive = IMG_Find(skinname, IT_SKIN, IF_SRGB); - if (image_emissive == R_NOTEXTURE) image_emissive = NULL; - } + vkpt_material_images_t images; + vkpt_load_material_images(&images, skinname, IT_SKIN, IF_NONE); - MAT_RegisterPBRMaterial(mat, image_diffuse, image_normals, image_emissive); + MAT_RegisterPBRMaterial(mat, images.diffuse, images.normals, images.emissive); mesh->materials[i] = mat; } diff --git a/src/refresh/vkpt/textures.c b/src/refresh/vkpt/textures.c index 87604ad42..ca8d41086 100644 --- a/src/refresh/vkpt/textures.c +++ b/src/refresh/vkpt/textures.c @@ -94,25 +94,9 @@ void vkpt_textures_prefetch() if (!line) continue; - image_t const * img1 = IMG_Find(line, IT_SKIN, IF_PERMANENT | IF_SRGB); - char other_name[MAX_QPATH]; - - // attempt loading a matching normal map - if (!Q_strlcpy(other_name, line, strlen(line) - 3)) - continue; - Q_concat(other_name, sizeof(other_name), other_name, "_n.tga", NULL); - FS_NormalizePath(other_name, other_name); - image_t const * img2 = IMG_Find(other_name, IT_SKIN, IF_PERMANENT); - /* if (img2 != R_NOTEXTURE) - Com_Printf("Prefetched '%s' (%d)\n", other_name, (int)(img2 - r_images)); */ - - // attempt loading a matching emissive map - if (!Q_strlcpy(other_name, line, strlen(line) - 3)) - continue; - Q_concat(other_name, sizeof(other_name), other_name, "_light.tga", NULL); - FS_NormalizePath(other_name, other_name); - image_t const * img3 = IMG_Find(other_name, IT_SKIN, IF_PERMANENT | IF_SRGB); + vkpt_material_images_t images; + vkpt_load_material_images(&images, line, IT_SKIN, IF_PERMANENT); } // Com_Printf("Loaded '%s'\n", filename); FS_FreeFile(buffer); @@ -630,6 +614,33 @@ vkpt_normalize_normal_map(image_t *image) image->processing_complete = qtrue; } +void vkpt_load_material_images(vkpt_material_images_t* images, const char *diffuse_path, imagetype_t type, imageflags_t flags) +{ + images->diffuse = IMG_Find(diffuse_path, type, flags | IF_SRGB); + images->normals = NULL; + images->emissive = NULL; + + if (images->diffuse != R_NOTEXTURE) + { + char other_name[MAX_QPATH]; + + // attempt loading a matching normal map + size_t diffuse_name_len = Q_strlcpy(other_name, diffuse_path, q_countof(other_name)); + other_name[diffuse_name_len - 4] = 0; + Q_strlcat(other_name, "_n.tga", q_countof(other_name)); + FS_NormalizePath(other_name, other_name); + images->normals = IMG_Find(other_name, type, flags); + if (images->normals == R_NOTEXTURE) images->normals = NULL; + + // attempt loading the emissive texture + other_name[diffuse_name_len - 4] = 0; + Q_strlcat(other_name, "_light.tga", q_countof(other_name)); + FS_NormalizePath(other_name, other_name); + images->emissive = IMG_Find(other_name, type, flags | IF_SRGB); + if (images->emissive == R_NOTEXTURE) images->emissive = NULL; + } +} + void IMG_Load_RTX(image_t *image, byte *pic) { diff --git a/src/refresh/vkpt/vkpt.h b/src/refresh/vkpt/vkpt.h index 57b1ef3af..e7aa5958b 100644 --- a/src/refresh/vkpt/vkpt.h +++ b/src/refresh/vkpt/vkpt.h @@ -528,6 +528,13 @@ void vkpt_extract_emissive_texture_info(image_t *image); void vkpt_textures_prefetch(); void vkpt_init_light_textures(); +typedef struct vkpt_material_images_s { + image_t *diffuse; + image_t *normals; + image_t *emissive; +} vkpt_material_images_t; +void vkpt_load_material_images(vkpt_material_images_t* images, const char *diffuse_path, imagetype_t type, imageflags_t flags); + VkCommandBuffer vkpt_begin_command_buffer(cmd_buf_group_t* group); void vkpt_free_command_buffers(cmd_buf_group_t* group); void vkpt_reset_command_buffers(cmd_buf_group_t* group); From 2415da21620e07aeada906780097512ac7689efd Mon Sep 17 00:00:00 2001 From: Frank Richter Date: Tue, 13 Apr 2021 20:35:27 +0200 Subject: [PATCH 09/11] Restrict additional images to the same source (base or game) as the diffuse image --- inc/refresh/refresh.h | 7 ++++++- src/refresh/images.c | 10 +++++++++- src/refresh/vkpt/textures.c | 5 +++-- 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/inc/refresh/refresh.h b/inc/refresh/refresh.h index 7dcb3c790..744de5ea0 100644 --- a/inc/refresh/refresh.h +++ b/inc/refresh/refresh.h @@ -202,7 +202,12 @@ typedef enum { IF_REPEAT = (1 << 6), IF_NEAREST = (1 << 7), IF_OPAQUE = (1 << 8), - IF_SRGB = (1 << 9) + IF_SRGB = (1 << 9), + + // Image source indicator/requirement flags + IF_SRC_BASE = (0x1 << 16), + IF_SRC_GAME = (0x2 << 16), + IF_SRC_MASK = (0x3 << 16), } imageflags_t; typedef enum { diff --git a/src/refresh/images.c b/src/refresh/images.c index ce3ef56db..a9034fd0c 100644 --- a/src/refresh/images.c +++ b/src/refresh/images.c @@ -968,6 +968,10 @@ load_img(const char *name, image_t *image) try_location >= TRY_IMAGE_SRC_BASE; try_location--) { + int location_flag = try_location == TRY_IMAGE_SRC_GAME ? IF_SRC_GAME : IF_SRC_MASK; + if(((image->flags & IF_SRC_MASK) != 0) && ((image->flags & IF_SRC_MASK) != location_flag)) + continue; + // first try with original extension ret = _try_image_format(fmt, image, try_location, &pic); if (ret == Q_ERR_NOENT) { @@ -1048,6 +1052,10 @@ static qerror_t find_or_load_image(const char *name, size_t len, try_location >= TRY_IMAGE_SRC_BASE; try_location--) { + int location_flag = try_location == TRY_IMAGE_SRC_GAME ? IF_SRC_GAME : IF_SRC_MASK; + if(((flags & IF_SRC_MASK) != 0) && ((flags & IF_SRC_MASK) != location_flag)) + continue; + for (int use_override = override_textures; use_override >= 0; use_override--) { // fill in some basic info @@ -1069,7 +1077,7 @@ static qerror_t find_or_load_image(const char *name, size_t len, image->baselen = len - 4; } image->type = type; - image->flags = flags; + image->flags = flags | location_flag; image->registration_sequence = registration_sequence; // find out original extension diff --git a/src/refresh/vkpt/textures.c b/src/refresh/vkpt/textures.c index ca8d41086..8269e28d4 100644 --- a/src/refresh/vkpt/textures.c +++ b/src/refresh/vkpt/textures.c @@ -622,6 +622,7 @@ void vkpt_load_material_images(vkpt_material_images_t* images, const char *diffu if (images->diffuse != R_NOTEXTURE) { + int src_flag = images->diffuse->flags & IF_SRC_MASK; char other_name[MAX_QPATH]; // attempt loading a matching normal map @@ -629,14 +630,14 @@ void vkpt_load_material_images(vkpt_material_images_t* images, const char *diffu other_name[diffuse_name_len - 4] = 0; Q_strlcat(other_name, "_n.tga", q_countof(other_name)); FS_NormalizePath(other_name, other_name); - images->normals = IMG_Find(other_name, type, flags); + images->normals = IMG_Find(other_name, type, flags | src_flag); if (images->normals == R_NOTEXTURE) images->normals = NULL; // attempt loading the emissive texture other_name[diffuse_name_len - 4] = 0; Q_strlcat(other_name, "_light.tga", q_countof(other_name)); FS_NormalizePath(other_name, other_name); - images->emissive = IMG_Find(other_name, type, flags | IF_SRGB); + images->emissive = IMG_Find(other_name, type, flags | src_flag | IF_SRGB); if (images->emissive == R_NOTEXTURE) images->emissive = NULL; } } From c6ffc678073c13ae2b9ec02401c2fdb3b8b9b35e Mon Sep 17 00:00:00 2001 From: Frank Richter Date: Wed, 14 Apr 2021 00:39:05 +0200 Subject: [PATCH 10/11] Greatly reduce the center offset amount in get_triangle_off_center() On some custom maps, offsetting one normal meant the 'center' actually ended inside some other geometry, which unfortunately means "not inside any BSP leaf". Scaling down the normal so it's only a little over the plane fixes these cases while still providing a point that's can be associated with a BSP leaf. --- src/refresh/vkpt/bsp_mesh.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/refresh/vkpt/bsp_mesh.c b/src/refresh/vkpt/bsp_mesh.c index edc404535..722d1d69e 100644 --- a/src/refresh/vkpt/bsp_mesh.c +++ b/src/refresh/vkpt/bsp_mesh.c @@ -300,9 +300,10 @@ get_triangle_off_center(const float* positions, float* center, float* anti_cente CrossProduct(e1, e2, normal); float length = VectorNormalize(normal); - // Offset the center by one normal to make sure that the point is + // Offset the center by a fraction of the normal to make sure that the point is // inside a BSP leaf and not on a boundary plane. + VectorScale(normal, 0.0001, normal); VectorAdd(center, normal, center); if (anti_center) From 4b9cf3b897544ace69640ebc5269342ec36fb384 Mon Sep 17 00:00:00 2001 From: Frank Richter Date: Wed, 14 Apr 2021 01:12:16 +0200 Subject: [PATCH 11/11] Use case-insensitive comparison when looking for player skin icon Fixes detection of skins ending with UPPERCASE icon file suffixes --- src/client/ui/playermodels.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/client/ui/playermodels.c b/src/client/ui/playermodels.c index 301c20f01..59f6a01f7 100644 --- a/src/client/ui/playermodels.c +++ b/src/client/ui/playermodels.c @@ -36,7 +36,7 @@ static qboolean IconOfSkinExists(char *skin, char **pcxfiles, int npcxfiles) Q_strlcat(scratch, "_i.pcx", sizeof(scratch)); for (i = 0; i < npcxfiles; i++) { - if (strcmp(pcxfiles[i], scratch) == 0) + if (stricmp(pcxfiles[i], scratch) == 0) return qtrue; }