From ec9e2c12b85fab37964a4897925c409c0773d6c8 Mon Sep 17 00:00:00 2001 From: Bill Currie Date: Mon, 15 Jan 2024 19:07:33 +0900 Subject: [PATCH] [vulkan] Implement skin support Finally, quakeworld gets its *ahem* fancy skins. I'm not happy with how skin loading is handled, but the whole model and skin support needs a redesign. Closes #74. --- include/QF/Vulkan/qf_model.h | 18 ++++- include/QF/model.h | 2 +- libs/models/alias/vulkan_model_alias.c | 40 +++++----- libs/models/vulkan_skin.c | 97 +++++++++++++++++++++++ libs/video/renderer/vid_render_vulkan.c | 9 +++ libs/video/renderer/vulkan/vulkan_alias.c | 13 ++- 6 files changed, 151 insertions(+), 28 deletions(-) diff --git a/include/QF/Vulkan/qf_model.h b/include/QF/Vulkan/qf_model.h index 3a42e131d..18a618b36 100644 --- a/include/QF/Vulkan/qf_model.h +++ b/include/QF/Vulkan/qf_model.h @@ -36,9 +36,25 @@ #include "QF/model.h" #include "QF/Vulkan/qf_vid.h" +typedef struct vulkan_ctx_s vulkan_ctx_t; + typedef struct modelctx_s { - struct vulkan_ctx_s *ctx; + vulkan_ctx_t *ctx; VkDeviceMemory texture_memory; } modelctx_t; +typedef struct skin_s skin_t; +typedef struct mod_alias_ctx_s mod_alias_ctx_t; +typedef struct maliasskindesc_s maliasskindesc_t; +typedef struct qfv_alias_skin_s qfv_alias_skin_t; + +qfv_alias_skin_t * +Vulkan_Mod_LoadSkin (mod_alias_ctx_t *alias_ctx, byte *skinpix, + int skinsize, int snum, int gnum, bool group, + maliasskindesc_t *skindesc, vulkan_ctx_t *ctx); +void Vulkan_Skin_Clear (qfv_alias_skin_t *skin, vulkan_ctx_t *ctx); + +void Vulkan_Skin_SetupSkin (skin_t *skin, struct vulkan_ctx_s *ctx); +void Vulkan_Skin_Destroy (skin_t *skin, struct vulkan_ctx_s *ctx); + #endif//__QF_Vulkan_qf_model_h diff --git a/include/QF/model.h b/include/QF/model.h index e6f796224..fc35f5d0e 100644 --- a/include/QF/model.h +++ b/include/QF/model.h @@ -316,7 +316,7 @@ typedef struct { char name[16]; } maliasframedesc_t; -typedef struct { +typedef struct maliasskindesc_s { aliasskintype_t type; int skin; int texnum; diff --git a/libs/models/alias/vulkan_model_alias.c b/libs/models/alias/vulkan_model_alias.c index 7eb9726bd..b93855397 100644 --- a/libs/models/alias/vulkan_model_alias.c +++ b/libs/models/alias/vulkan_model_alias.c @@ -46,6 +46,7 @@ #include "QF/modelgen.h" #include "QF/vid.h" #include "QF/Vulkan/qf_alias.h" +#include "QF/Vulkan/qf_model.h" #include "QF/Vulkan/qf_texture.h" #include "QF/Vulkan/barrier.h" #include "QF/Vulkan/buffer.h" @@ -64,17 +65,10 @@ static vec3_t vertex_normals[NUMVERTEXNORMALS] = { #include "anorms.h" }; -static void -skin_clear (int skin_offset, aliashdr_t *hdr, vulkan_ctx_t *ctx) +static qfv_alias_skin_t * +find_skin (int skin_offset, aliashdr_t *hdr) { - qfv_device_t *device = ctx->device; - qfv_devfuncs_t *dfunc = device->funcs; - qfv_alias_skin_t *skin = (qfv_alias_skin_t *) ((byte *) hdr + skin_offset); - - Vulkan_AliasRemoveSkin (ctx, skin); - dfunc->vkDestroyImageView (device->dev, skin->view, 0); - dfunc->vkDestroyImage (device->dev, skin->image, 0); - dfunc->vkFreeMemory (device->dev, skin->memory, 0); + return (qfv_alias_skin_t *) ((byte *) hdr + skin_offset); } static void @@ -101,35 +95,37 @@ vulkan_alias_clear (model_t *m, void *data) __auto_type group = (maliasskingroup_t *) ((byte *) hdr + skins[i].skin); for (int j = 0; j < group->numskins; j++) { - skin_clear (group->skindescs[j].skin, hdr, ctx); + auto skin = find_skin (group->skindescs[j].skin, hdr); + Vulkan_Skin_Clear (skin, ctx); } } else { - skin_clear (skins[i].skin, hdr, ctx); + auto skin = find_skin (skins[i].skin, hdr); + Vulkan_Skin_Clear (skin, ctx); } } } #define SKIN_LAYERS 3 -static void * +qfv_alias_skin_t * Vulkan_Mod_LoadSkin (mod_alias_ctx_t *alias_ctx, byte *skinpix, int skinsize, int snum, int gnum, bool group, maliasskindesc_t *skindesc, vulkan_ctx_t *ctx) { - qfvPushDebug (ctx, va (ctx->va_ctx, "alias.load_skin: %s", alias_ctx->mod->name)); + const char *mod_name = alias_ctx->mod->name; + qfvPushDebug (ctx, va (ctx->va_ctx, "alias.load_skin: %s", mod_name)); qfv_device_t *device = ctx->device; qfv_devfuncs_t *dfunc = device->funcs; aliashdr_t *header = alias_ctx->header; + int w = header->mdl.skinwidth; + int h = header->mdl.skinheight; qfv_alias_skin_t *skin; byte *tskin; - int w, h; skin = Hunk_Alloc (0, sizeof (qfv_alias_skin_t)); QuatSet (TOP_RANGE + 7, BOTTOM_RANGE + 7, 0, 0, skin->colors); skindesc->skin = (byte *) skin - (byte *) header; - //FIXME move all skins into arrays(?) - w = header->mdl.skinwidth; - h = header->mdl.skinheight; + tskin = malloc (2 * skinsize); memcpy (tskin, skinpix, skinsize); Mod_FloodFillSkin (tskin, w, h); @@ -144,13 +140,13 @@ Vulkan_Mod_LoadSkin (mod_alias_ctx_t *alias_ctx, byte *skinpix, int skinsize, | VK_IMAGE_USAGE_TRANSFER_SRC_BIT); QFV_duSetObjectName (device, VK_OBJECT_TYPE_IMAGE, skin->image, va (ctx->va_ctx, "image:%s:%d:%d", - alias_ctx->mod->name, snum, gnum)); + mod_name, snum, gnum)); skin->memory = QFV_AllocImageMemory (device, skin->image, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, 0, 0); QFV_duSetObjectName (device, VK_OBJECT_TYPE_DEVICE_MEMORY, skin->memory, va (ctx->va_ctx, "memory:%s:%d:%d", - alias_ctx->mod->name, snum, gnum)); + mod_name, snum, gnum)); QFV_BindImageMemory (device, skin->image, skin->memory, 0); skin->view = QFV_CreateImageView (device, skin->image, VK_IMAGE_VIEW_TYPE_2D_ARRAY, @@ -158,7 +154,7 @@ Vulkan_Mod_LoadSkin (mod_alias_ctx_t *alias_ctx, byte *skinpix, int skinsize, VK_IMAGE_ASPECT_COLOR_BIT); QFV_duSetObjectName (device, VK_OBJECT_TYPE_IMAGE_VIEW, skin->view, va (ctx->va_ctx, "iview:%s:%d:%d", - alias_ctx->mod->name, snum, gnum)); + mod_name, snum, gnum)); qfv_stagebuf_t *stage = QFV_CreateStagingBuffer (device, "alias stage", SKIN_LAYERS * skinsize * 4, @@ -220,7 +216,7 @@ Vulkan_Mod_LoadSkin (mod_alias_ctx_t *alias_ctx, byte *skinpix, int skinsize, Vulkan_AliasAddSkin (ctx, skin); qfvPopDebug (ctx); - return skinpix + skinsize; + return skin; } void diff --git a/libs/models/vulkan_skin.c b/libs/models/vulkan_skin.c index e69de29bb..820cc764e 100644 --- a/libs/models/vulkan_skin.c +++ b/libs/models/vulkan_skin.c @@ -0,0 +1,97 @@ +/* + vulkan_skin.c + + Vulkan Skin support + + Copyright (C) 2024 Bill Currie + + Author: Bill Currie + Date: 2024/1/15 + + 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 2 + 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, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef HAVE_STRING_H +# include "string.h" +#endif +#ifdef HAVE_STRINGS_H +# include "strings.h" +#endif + +#include + +#include "QF/image.h" +#include "QF/model.h" +#include "QF/skin.h" +#include "QF/sys.h" + +#include "QF/Vulkan/device.h" +#include "QF/Vulkan/qf_alias.h" +#include "QF/Vulkan/qf_model.h" + +#include "mod_internal.h" +#include "vid_vulkan.h" + +void +Vulkan_Skin_Clear (qfv_alias_skin_t *skin, vulkan_ctx_t *ctx) +{ + qfv_device_t *device = ctx->device; + qfv_devfuncs_t *dfunc = device->funcs; + + Vulkan_AliasRemoveSkin (ctx, skin); + dfunc->vkDestroyImageView (device->dev, skin->view, 0); + dfunc->vkDestroyImage (device->dev, skin->image, 0); + dfunc->vkFreeMemory (device->dev, skin->memory, 0); +} + +void +Vulkan_Skin_SetupSkin (skin_t *skin, struct vulkan_ctx_s *ctx) +{ + tex_t *tex = skin->tex; + // FIXME this is gross, but the vulkan skin (and even model) handling + // needs a complete overhaul. + aliashdr_t dummy_header = { + .mdl.skinwidth = tex->width, + .mdl.skinheight = tex->height, + }; + model_t dummy_model = { }; + mod_alias_ctx_t alias_ctx = { + .header = &dummy_header, + .mod = &dummy_model, + }; + maliasskindesc_t skindesc = {}; + int skinsize = tex->width * tex->height; + size_t hunk_mark = Hunk_LowMark (0); + auto vkskin = Vulkan_Mod_LoadSkin (&alias_ctx, tex->data, skinsize, + 0, 0, false, &skindesc, ctx); + skin->tex = malloc (sizeof (*vkskin)); + *(qfv_alias_skin_t *) skin->tex = *vkskin; + Hunk_FreeToLowMark (0, hunk_mark); +} + +void +Vulkan_Skin_Destroy (skin_t *skin, struct vulkan_ctx_s *ctx) +{ + auto alias_skin = (qfv_alias_skin_t *) skin->tex; + Vulkan_Skin_Clear (alias_skin, ctx); +} diff --git a/libs/video/renderer/vid_render_vulkan.c b/libs/video/renderer/vid_render_vulkan.c index a3a2f4fe2..97f46e9ef 100644 --- a/libs/video/renderer/vid_render_vulkan.c +++ b/libs/video/renderer/vid_render_vulkan.c @@ -49,6 +49,7 @@ #include "QF/Vulkan/qf_lighting.h" #include "QF/Vulkan/qf_lightmap.h" #include "QF/Vulkan/qf_matrices.h" +#include "QF/Vulkan/qf_model.h" #include "QF/Vulkan/qf_output.h" #include "QF/Vulkan/qf_palette.h" #include "QF/Vulkan/qf_particles.h" @@ -454,6 +455,13 @@ vulkan_Mod_SpriteLoadFrames (mod_sprite_ctx_t *sprite_ctx) static void vulkan_Skin_SetupSkin (struct skin_s *skin) { + Vulkan_Skin_SetupSkin (skin, vulkan_ctx); +} + +static void +vulkan_Skin_Destroy (struct skin_s *skin) +{ + Vulkan_Skin_Destroy (skin, vulkan_ctx); } static void @@ -515,6 +523,7 @@ static vid_model_funcs_t model_funcs = { .skin_set = Skin_Set, .skin_setupskin = vulkan_Skin_SetupSkin, + .skin_destroy = vulkan_Skin_Destroy, }; static void diff --git a/libs/video/renderer/vulkan/vulkan_alias.c b/libs/video/renderer/vulkan/vulkan_alias.c index ee886817d..39776e59c 100644 --- a/libs/video/renderer/vulkan/vulkan_alias.c +++ b/libs/video/renderer/vulkan/vulkan_alias.c @@ -50,6 +50,7 @@ #include "QF/Vulkan/instance.h" #include "QF/Vulkan/render.h" +#include "mod_internal.h" #include "r_internal.h" #include "vid_vulkan.h" @@ -177,7 +178,6 @@ alias_draw_ent (qfv_taskctx_t *taskctx, entity_t ent, bool pass, { auto model = renderer->model; aliashdr_t *hdr; - qfv_alias_skin_t *skin; uint16_t *matrix_base = taskctx->data; if (!(hdr = model->aliashdr)) { @@ -189,9 +189,14 @@ alias_draw_ent (qfv_taskctx_t *taskctx, entity_t ent, bool pass, transform_t transform = Entity_Transform (ent); - if (0/*XXX ent->skin && ent->skin->tex*/) { - //skin = ent->skin->tex; - } else { + qfv_alias_skin_t *skin = nullptr; + if (renderer->skin) { + skin_t *tskin = Skin_Get (renderer->skin); + if (tskin) { + skin = (qfv_alias_skin_t *) tskin->tex; + } + } + if (!skin) { maliasskindesc_t *skindesc; skindesc = R_AliasGetSkindesc (animation, renderer->skinnum, hdr); skin = (qfv_alias_skin_t *) ((byte *) hdr + skindesc->skin);