diff --git a/Quake/common.c b/Quake/common.c index eb62e033a..ade748b6a 100644 --- a/Quake/common.c +++ b/Quake/common.c @@ -52,9 +52,9 @@ static void COM_Path_f (void); #define PAK0_COUNT_V091 308 /* id1/pak0.pak - v0.91/0.92, not supported */ #define PAK0_CRC_V091 28804 /* id1/pak0.pak - v0.91/0.92, not supported */ -char com_token[1024]; -int com_argc; -char **com_argv; +THREAD_LOCAL char com_token[1024]; +int com_argc; +char **com_argv; #define CMDLINE_LENGTH 256 /* johnfitz -- mirrored in cmd.c */ char com_cmdline[CMDLINE_LENGTH]; diff --git a/Quake/common.h b/Quake/common.h index 64cf0ed3b..ce20021ec 100644 --- a/Quake/common.h +++ b/Quake/common.h @@ -228,8 +228,8 @@ int q_vsnprintf (char *str, size_t size, const char *format, va_list args) FUNC_ //============================================================================ -extern char com_token[1024]; -extern qboolean com_eof; +extern THREAD_LOCAL char com_token[1024]; +extern qboolean com_eof; const char *COM_Parse (const char *data); diff --git a/Quake/common.make b/Quake/common.make index 8eb9054b0..e13129154 100644 --- a/Quake/common.make +++ b/Quake/common.make @@ -123,6 +123,7 @@ SHADER_OBJS = \ alias_frag.o \ alias_alphatest_frag.o \ alias_vert.o \ + md5_vert.o \ basic_alphatest_frag.o \ screen_effects_8bit_comp.o \ screen_effects_8bit_scale_comp.o \ diff --git a/Quake/gl_mesh.c b/Quake/gl_mesh.c index 9111ddb0b..7cefced3c 100644 --- a/Quake/gl_mesh.c +++ b/Quake/gl_mesh.c @@ -34,21 +34,21 @@ ALIAS MODEL DISPLAY LIST GENERATION */ // Heap -#define INDEX_HEAP_SIZE_MB 2 -#define VERTEX_HEAP_SIZE_MB 16 +#define MESH_HEAP_SIZE_MB 32 +#define MESH_HEAP_NAME "Mesh heap" -static glheap_t **vertex_buffer_heaps; -static glheap_t **index_buffer_heaps; -static int num_vertex_buffer_heaps; -static int num_index_buffer_heaps; +static glheap_t **mesh_buffer_heaps; +static int num_mesh_buffer_heaps; +static int mesh_heap_memory_type_index; typedef struct { - VkBuffer buffer; - glheap_t *heap; - glheapnode_t *heap_node; - glheap_t ***heaps; - int *num_heaps; + VkBuffer buffer; + VkDescriptorSet descriptor_set; + glheap_t *heap; + glheapnode_t *heap_node; + glheap_t ***heaps; + int *num_heaps; } buffer_garbage_t; static int current_garbage_index; @@ -60,7 +60,7 @@ static buffer_garbage_t buffer_garbage[MAX_MODELS * 2][2]; AddBufferGarbage ================ */ -static void AddBufferGarbage (VkBuffer buffer, glheap_t *heap, glheapnode_t *heap_node, glheap_t ***heaps, int *num_heaps) +static void AddBufferGarbage (VkBuffer buffer, VkDescriptorSet descriptor_set, glheap_t *heap, glheapnode_t *heap_node, glheap_t ***heaps, int *num_heaps) { int garbage_index; buffer_garbage_t *garbage; @@ -68,11 +68,37 @@ static void AddBufferGarbage (VkBuffer buffer, glheap_t *heap, glheapnode_t *hea garbage_index = num_garbage_buffers[current_garbage_index]++; garbage = &buffer_garbage[garbage_index][current_garbage_index]; garbage->buffer = buffer; + garbage->descriptor_set = descriptor_set; garbage->heap = heap; garbage->heap_node = heap_node; garbage->heaps = heaps; garbage->num_heaps = num_heaps; } +/* +================ +R_InitMeshHeapMemoryIndex +================ +*/ +void R_InitMeshHeapMemoryIndex () +{ + // Allocate index buffer & upload to GPU + ZEROED_STRUCT (VkBufferCreateInfo, buffer_create_info); + buffer_create_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; + buffer_create_info.size = 16; + buffer_create_info.usage = + VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT; + VkBuffer dummy_buffer; + VkResult err = vkCreateBuffer (vulkan_globals.device, &buffer_create_info, NULL, &dummy_buffer); + if (err != VK_SUCCESS) + Sys_Error ("vkCreateBuffer failed"); + + VkMemoryRequirements memory_requirements; + vkGetBufferMemoryRequirements (vulkan_globals.device, dummy_buffer, &memory_requirements); + + mesh_heap_memory_type_index = GL_MemoryTypeFromProperties (memory_requirements.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, 0); + + vkDestroyBuffer (vulkan_globals.device, dummy_buffer, NULL); +} /* ================ @@ -96,15 +122,9 @@ void R_CollectMeshBufferGarbage () num_garbage_buffers[current_garbage_index] = 0; } -static void GLMesh_LoadVertexBuffer (qmodel_t *m, const aliashdr_t *hdr, int numindexes, unsigned short *indexes, trivertx_t *vertexes, aliasmesh_t *meshdesc); - /* ================ GL_MakeAliasModelDisplayLists - -Saves data needed to build the VBO for this model on the hunk. Afterwards this -is copied to Mod_Extradata. - Original code by MH from RMQEngine ================ */ @@ -126,7 +146,7 @@ void GL_MakeAliasModelDisplayLists (qmodel_t *m, aliashdr_t *paliashdr) verts[i * paliashdr->numverts + j] = poseverts[i][j]; // there can never be more than this number of verts - const int maxverts_vbo = pheader->numtris * 3; + const int maxverts_vbo = paliashdr->numtris * 3; TEMP_ALLOC_ZEROED (aliasmesh_t, desc, maxverts_vbo); // there will always be this number of indexes TEMP_ALLOC_ZEROED (unsigned short, indexes, maxverts_vbo); @@ -134,7 +154,7 @@ void GL_MakeAliasModelDisplayLists (qmodel_t *m, aliashdr_t *paliashdr) hash_map_t *vertex_to_index_map = HashMap_Create (aliasmesh_t, unsigned short, &AliasMeshHash); HashMap_Reserve (vertex_to_index_map, maxverts_vbo); - for (int i = 0; i < pheader->numtris; i++) + for (int i = 0; i < paliashdr->numtris; i++) { for (int j = 0; j < 3; j++) { @@ -147,7 +167,7 @@ void GL_MakeAliasModelDisplayLists (qmodel_t *m, aliashdr_t *paliashdr) // check for back side and adjust texcoord s if (!triangles[i].facesfront && stverts[vertindex].onseam) - s += pheader->skinwidth / 2; + s += paliashdr->skinwidth / 2; const aliasmesh_t mesh = { .st = {s, t}, @@ -176,7 +196,8 @@ void GL_MakeAliasModelDisplayLists (qmodel_t *m, aliashdr_t *paliashdr) HashMap_Destroy (vertex_to_index_map); // upload immediately - GLMesh_LoadVertexBuffer (m, pheader, paliashdr->numindexes, indexes, verts, desc); + paliashdr->poseverttype = PV_QUAKE1; + GLMesh_UploadBuffers (m, paliashdr, indexes, (byte *)verts, desc, NULL); TEMP_FREE (indexes); TEMP_FREE (desc); @@ -188,28 +209,28 @@ extern float r_avertexnormals[NUMVERTEXNORMALS][3]; /* ================ -GLMesh_DeleteVertexBuffer +GLMesh_DeleteMeshBuffers ================ */ -static void GLMesh_DeleteVertexBuffer (qmodel_t *m) +static void GLMesh_DeleteMeshBuffers (qmodel_t *m) { if (m->vertex_buffer == VK_NULL_HANDLE) return; if (in_update_screen) { - AddBufferGarbage (m->vertex_buffer, m->vertex_heap, m->vertex_heap_node, &vertex_buffer_heaps, &num_vertex_buffer_heaps); - AddBufferGarbage (m->index_buffer, m->index_heap, m->index_heap_node, &index_buffer_heaps, &num_index_buffer_heaps); + AddBufferGarbage (m->vertex_buffer, VK_NULL_HANDLE, m->vertex_heap, m->vertex_heap_node, &mesh_buffer_heaps, &num_mesh_buffer_heaps); + AddBufferGarbage (m->index_buffer, VK_NULL_HANDLE, m->index_heap, m->index_heap_node, &mesh_buffer_heaps, &num_mesh_buffer_heaps); } else { GL_WaitForDeviceIdle (); vkDestroyBuffer (vulkan_globals.device, m->vertex_buffer, NULL); - GL_FreeFromHeaps (num_vertex_buffer_heaps, vertex_buffer_heaps, m->vertex_heap, m->vertex_heap_node, &num_vulkan_mesh_allocations); + GL_FreeFromHeaps (num_mesh_buffer_heaps, mesh_buffer_heaps, m->vertex_heap, m->vertex_heap_node, &num_vulkan_mesh_allocations); vkDestroyBuffer (vulkan_globals.device, m->index_buffer, NULL); - GL_FreeFromHeaps (num_index_buffer_heaps, index_buffer_heaps, m->index_heap, m->index_heap_node, &num_vulkan_mesh_allocations); + GL_FreeFromHeaps (num_mesh_buffer_heaps, mesh_buffer_heaps, m->index_heap, m->index_heap_node, &num_vulkan_mesh_allocations); } m->vertex_buffer = VK_NULL_HANDLE; @@ -222,36 +243,46 @@ static void GLMesh_DeleteVertexBuffer (qmodel_t *m) /* ================ -GLMesh_LoadVertexBuffer - -Upload the given alias model's mesh to a VBO - -Original code by MH from RMQEngine +GLMesh_UploadBuffers ================ */ -static void GLMesh_LoadVertexBuffer (qmodel_t *m, const aliashdr_t *hdr, int numindexes, unsigned short *indexes, trivertx_t *vertexes, aliasmesh_t *desc) +void GLMesh_UploadBuffers (qmodel_t *m, aliashdr_t *mainhdr, unsigned short *indexes, byte *vertexes, aliasmesh_t *desc, jointpose_t *joints) { - int totalvbosize = 0; - int remaining_size; - int copy_offset; - byte *vbodata; - int f; + size_t numindexes = 0; + size_t numverts = 0; VkResult err; + if (!mainhdr) + return; - GLMesh_DeleteVertexBuffer (m); + GLMesh_DeleteMeshBuffers (m); - // count the sizes we need + // count how much space we're going to need. + size_t totalvbosize = 0; + for (const aliashdr_t *hdr = mainhdr; hdr != NULL; hdr = hdr->nextsurface) + { + switch (hdr->poseverttype) + { + case PV_QUAKE1: + totalvbosize += + (hdr->numposes * hdr->numverts_vbo * sizeof (meshxyz_t)); // ericw -- what RMQEngine called nummeshframes is called numposes in QuakeSpasm + break; + case PV_MD5: + totalvbosize += (hdr->numposes * hdr->numverts_vbo * sizeof (md5vert_t)); + break; + } - // ericw -- RMQEngine stored these vbo*ofs values in aliashdr_t, but we must not - // mutate Mod_Extradata since it might be reloaded from disk, so I moved them to qmodel_t - // (test case: roman1.bsp from arwop, 64mb heap) - m->vboindexofs = 0; + numverts += hdr->numverts_vbo; + numindexes += hdr->numindexes; + } - m->vboxyzofs = 0; - totalvbosize += (hdr->numposes * hdr->numverts_vbo * sizeof (meshxyz_t)); // ericw -- what RMQEngine called nummeshframes is called numposes in QuakeSpasm + const size_t totaljointssize = mainhdr->numframes * mainhdr->numjoints * sizeof (jointpose_t); - m->vbostofs = totalvbosize; - totalvbosize += (hdr->numverts_vbo * sizeof (meshst_t)); + assert ((mainhdr->nextsurface == NULL) || (mainhdr->poseverttype != PV_QUAKE1)); + if (mainhdr->poseverttype == PV_QUAKE1) + { + m->vbostofs = totalvbosize; + totalvbosize += (numverts * sizeof (meshst_t)); + } if (isDedicated) return; @@ -260,10 +291,8 @@ static void GLMesh_LoadVertexBuffer (qmodel_t *m, const aliashdr_t *hdr, int num if (!totalvbosize) return; - // grab the pointers to data in the extradata - { - const int totalindexsize = numindexes * sizeof (unsigned short); + const size_t totalindexsize = numindexes * sizeof (unsigned short); // Allocate index buffer & upload to GPU ZEROED_STRUCT (VkBufferCreateInfo, buffer_create_info); @@ -279,81 +308,67 @@ static void GLMesh_LoadVertexBuffer (qmodel_t *m, const aliashdr_t *hdr, int num VkMemoryRequirements memory_requirements; vkGetBufferMemoryRequirements (vulkan_globals.device, m->index_buffer, &memory_requirements); - uint32_t memory_type_index = GL_MemoryTypeFromProperties (memory_requirements.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, 0); - VkDeviceSize heap_size = INDEX_HEAP_SIZE_MB * (VkDeviceSize)1024 * (VkDeviceSize)1024; + VkDeviceSize heap_size = MESH_HEAP_SIZE_MB * (VkDeviceSize)1024 * (VkDeviceSize)1024; VkDeviceSize aligned_offset = GL_AllocateFromHeaps ( - &num_index_buffer_heaps, &index_buffer_heaps, heap_size, memory_type_index, VULKAN_MEMORY_TYPE_DEVICE, memory_requirements.size, - memory_requirements.alignment, &m->index_heap, &m->index_heap_node, &num_vulkan_mesh_allocations, "Index Buffers"); + &num_mesh_buffer_heaps, &mesh_buffer_heaps, heap_size, mesh_heap_memory_type_index, VULKAN_MEMORY_TYPE_DEVICE, memory_requirements.size, + memory_requirements.alignment, &m->index_heap, &m->index_heap_node, &num_vulkan_mesh_allocations, MESH_HEAP_NAME); err = vkBindBufferMemory (vulkan_globals.device, m->index_buffer, m->index_heap->memory.handle, aligned_offset); if (err != VK_SUCCESS) Sys_Error ("vkBindBufferMemory failed"); - remaining_size = totalindexsize; - copy_offset = 0; - - while (remaining_size > 0) - { - const int size_to_copy = q_min (remaining_size, vulkan_globals.staging_buffer_size); - VkBuffer staging_buffer; - VkCommandBuffer command_buffer; - int staging_offset; - unsigned char *staging_memory = R_StagingAllocate (size_to_copy, 1, &command_buffer, &staging_buffer, &staging_offset); - - VkBufferCopy region; - region.srcOffset = staging_offset; - region.dstOffset = copy_offset; - region.size = size_to_copy; - vkCmdCopyBuffer (command_buffer, staging_buffer, m->index_buffer, 1, ®ion); - - R_StagingBeginCopy (); - memcpy (staging_memory, (byte *)indexes + copy_offset, size_to_copy); - R_StagingEndCopy (); - - copy_offset += size_to_copy; - remaining_size -= size_to_copy; - } + R_StagingUploadBuffer (m->index_buffer, totalindexsize, (byte *)indexes); } // create the vertex buffer (empty) - vbodata = (byte *)Mem_Alloc (totalvbosize); - memset (vbodata, 0, totalvbosize); + TEMP_ALLOC (byte, vbodata, totalvbosize); - // fill in the vertices at the start of the buffer - for (f = 0; f < hdr->numposes; f++) // ericw -- what RMQEngine called nummeshframes is called numposes in QuakeSpasm + // fill in the vertices of the buffer + size_t vertofs = 0; + for (const aliashdr_t *hdr = mainhdr; hdr != NULL; hdr = hdr->nextsurface) { - int v; - meshxyz_t *xyz = (meshxyz_t *)(vbodata + (f * hdr->numverts_vbo * sizeof (meshxyz_t))); - const trivertx_t *tv = vertexes + (hdr->numverts * f); - - for (v = 0; v < hdr->numverts_vbo; v++) + switch (hdr->poseverttype) { - trivertx_t trivert = tv[desc[v].vertindex]; - - xyz[v].xyz[0] = trivert.v[0]; - xyz[v].xyz[1] = trivert.v[1]; - xyz[v].xyz[2] = trivert.v[2]; - xyz[v].xyz[3] = 1; // need w 1 for 4 byte vertex compression - - // map the normal coordinates in [-1..1] to [-127..127] and store in an unsigned char. - // this introduces some error (less than 0.004), but the normals were very coarse - // to begin with - xyz[v].normal[0] = 127 * r_avertexnormals[trivert.lightnormalindex][0]; - xyz[v].normal[1] = 127 * r_avertexnormals[trivert.lightnormalindex][1]; - xyz[v].normal[2] = 127 * r_avertexnormals[trivert.lightnormalindex][2]; - xyz[v].normal[3] = 0; // unused; for 4-byte alignment + case PV_QUAKE1: + for (int f = 0; f < hdr->numposes; f++) // ericw -- what RMQEngine called nummeshframes is called numposes in QuakeSpasm + { + meshxyz_t *xyz = (meshxyz_t *)vbodata + vertofs; + const trivertx_t *tv = (trivertx_t *)vertexes + (hdr->numverts * f); + vertofs += hdr->numverts_vbo; + + for (int v = 0; v < hdr->numverts_vbo; v++) + { + trivertx_t trivert = tv[desc[v].vertindex]; + + xyz[v].xyz[0] = trivert.v[0]; + xyz[v].xyz[1] = trivert.v[1]; + xyz[v].xyz[2] = trivert.v[2]; + xyz[v].xyz[3] = 1; // need w 1 for 4 byte vertex compression + + // map the normal coordinates in [-1..1] to [-127..127] and store in an unsigned char. + // this introduces some error (less than 0.004), but the normals were very coarse + // to begin with + xyz[v].normal[0] = 127 * r_avertexnormals[trivert.lightnormalindex][0]; + xyz[v].normal[1] = 127 * r_avertexnormals[trivert.lightnormalindex][1]; + xyz[v].normal[2] = 127 * r_avertexnormals[trivert.lightnormalindex][2]; + xyz[v].normal[3] = 0; // unused; for 4-byte alignment + } + } + break; + case PV_MD5: + assert (hdr->numposes == 1); + memcpy (vbodata, vertexes, totalvbosize); } } // fill in the ST coords at the end of the buffer + if (mainhdr->poseverttype == PV_QUAKE1) { - meshst_t *st; - - st = (meshst_t *)(vbodata + m->vbostofs); - for (f = 0; f < hdr->numverts_vbo; f++) + meshst_t *st = (meshst_t *)(vbodata + m->vbostofs); + for (int f = 0; f < mainhdr->numverts_vbo; f++) { - st[f].st[0] = ((float)desc[f].st[0] + 0.5f) / (float)hdr->skinwidth; - st[f].st[1] = ((float)desc[f].st[1] + 0.5f) / (float)hdr->skinheight; + st[f].st[0] = ((float)desc[f].st[0] + 0.5f) / (float)mainhdr->skinwidth; + st[f].st[1] = ((float)desc[f].st[1] + 0.5f) / (float)mainhdr->skinheight; } } @@ -372,52 +387,73 @@ static void GLMesh_LoadVertexBuffer (qmodel_t *m, const aliashdr_t *hdr, int num VkMemoryRequirements memory_requirements; vkGetBufferMemoryRequirements (vulkan_globals.device, m->vertex_buffer, &memory_requirements); - uint32_t memory_type_index = GL_MemoryTypeFromProperties (memory_requirements.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, 0); - VkDeviceSize heap_size = VERTEX_HEAP_SIZE_MB * (VkDeviceSize)1024 * (VkDeviceSize)1024; + VkDeviceSize heap_size = MESH_HEAP_SIZE_MB * (VkDeviceSize)1024 * (VkDeviceSize)1024; VkDeviceSize aligned_offset = GL_AllocateFromHeaps ( - &num_vertex_buffer_heaps, &vertex_buffer_heaps, heap_size, memory_type_index, VULKAN_MEMORY_TYPE_DEVICE, memory_requirements.size, - memory_requirements.alignment, &m->vertex_heap, &m->vertex_heap_node, &num_vulkan_mesh_allocations, "Vertex Buffers"); + &num_mesh_buffer_heaps, &mesh_buffer_heaps, heap_size, mesh_heap_memory_type_index, VULKAN_MEMORY_TYPE_DEVICE, memory_requirements.size, + memory_requirements.alignment, &m->vertex_heap, &m->vertex_heap_node, &num_vulkan_mesh_allocations, MESH_HEAP_NAME); err = vkBindBufferMemory (vulkan_globals.device, m->vertex_buffer, m->vertex_heap->memory.handle, aligned_offset); if (err != VK_SUCCESS) Sys_Error ("vkBindBufferMemory failed"); - remaining_size = totalvbosize; - copy_offset = 0; + R_StagingUploadBuffer (m->vertex_buffer, totalvbosize, vbodata); + } - while (remaining_size > 0) - { - const int size_to_copy = q_min (remaining_size, vulkan_globals.staging_buffer_size); - VkBuffer staging_buffer; - VkCommandBuffer command_buffer; - int staging_offset; - unsigned char *staging_memory = R_StagingAllocate (size_to_copy, 1, &command_buffer, &staging_buffer, &staging_offset); - - VkBufferCopy region; - region.srcOffset = staging_offset; - region.dstOffset = copy_offset; - region.size = size_to_copy; - vkCmdCopyBuffer (command_buffer, staging_buffer, m->vertex_buffer, 1, ®ion); - - R_StagingBeginCopy (); - memcpy (staging_memory, (byte *)vbodata + copy_offset, size_to_copy); - R_StagingEndCopy (); - - copy_offset += size_to_copy; - remaining_size -= size_to_copy; - } + // Allocate joints buffer & upload to GPU + if (joints) + { + ZEROED_STRUCT (VkBufferCreateInfo, buffer_create_info); + buffer_create_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; + buffer_create_info.size = totaljointssize; + buffer_create_info.usage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT; + err = vkCreateBuffer (vulkan_globals.device, &buffer_create_info, NULL, &m->joints_buffer); + if (err != VK_SUCCESS) + Sys_Error ("vkCreateBuffer failed"); + + GL_SetObjectName ((uint64_t)m->joints_buffer, VK_OBJECT_TYPE_BUFFER, m->name); + + VkMemoryRequirements memory_requirements; + vkGetBufferMemoryRequirements (vulkan_globals.device, m->joints_buffer, &memory_requirements); + + VkDeviceSize heap_size = MESH_HEAP_SIZE_MB * (VkDeviceSize)1024 * (VkDeviceSize)1024; + VkDeviceSize aligned_offset = GL_AllocateFromHeaps ( + &num_mesh_buffer_heaps, &mesh_buffer_heaps, heap_size, mesh_heap_memory_type_index, VULKAN_MEMORY_TYPE_DEVICE, memory_requirements.size, + memory_requirements.alignment, &m->joints_heap, &m->joints_heap_node, &num_vulkan_mesh_allocations, MESH_HEAP_NAME); + err = vkBindBufferMemory (vulkan_globals.device, m->joints_buffer, m->joints_heap->memory.handle, aligned_offset); + if (err != VK_SUCCESS) + Sys_Error ("vkBindBufferMemory failed"); + + R_StagingUploadBuffer (m->joints_buffer, totaljointssize, (byte *)joints); + + m->joints_set = R_AllocateDescriptorSet (&vulkan_globals.joints_buffer_set_layout); + + ZEROED_STRUCT (VkDescriptorBufferInfo, buffer_info); + buffer_info.buffer = m->joints_buffer; + buffer_info.offset = 0; + buffer_info.range = VK_WHOLE_SIZE; + + ZEROED_STRUCT (VkWriteDescriptorSet, joints_set_write); + joints_set_write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + joints_set_write.dstSet = m->joints_set; + joints_set_write.dstBinding = 0; + joints_set_write.dstArrayElement = 0; + joints_set_write.descriptorCount = 1; + joints_set_write.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; + joints_set_write.pBufferInfo = &buffer_info; + + vkUpdateDescriptorSets (vulkan_globals.device, 1, &joints_set_write, 0, NULL); } - Mem_Free (vbodata); + TEMP_FREE (vbodata); } /* ================ -GLMesh_DeleteVertexBuffers +GLMesh_DeleteAllMeshBuffers Delete VBOs for all loaded alias models ================ */ -void GLMesh_DeleteVertexBuffers (void) +void GLMesh_DeleteAllMeshBuffers (void) { int j; qmodel_t *m; @@ -429,6 +465,6 @@ void GLMesh_DeleteVertexBuffers (void) if (m->type != mod_alias) continue; - GLMesh_DeleteVertexBuffer (m); + GLMesh_DeleteMeshBuffers (m); } } diff --git a/Quake/gl_model.c b/Quake/gl_model.c index d00a40508..b125fd8b1 100644 --- a/Quake/gl_model.c +++ b/Quake/gl_model.c @@ -29,10 +29,12 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. static void Mod_LoadSpriteModel (qmodel_t *mod, void *buffer); static void Mod_LoadBrushModel (qmodel_t *mod, const char *loadname, void *buffer); static void Mod_LoadAliasModel (qmodel_t *mod, void *buffer); +static void Mod_LoadMD5MeshModel (qmodel_t *mod, const void *buffer); static qmodel_t *Mod_LoadModel (qmodel_t *mod, qboolean crash); cvar_t external_ents = {"external_ents", "1", CVAR_ARCHIVE}; cvar_t external_vis = {"external_vis", "1", CVAR_ARCHIVE}; +cvar_t r_md5models = {"r_md5models", "1", CVAR_ARCHIVE}; static byte *mod_novis; static int mod_novis_capacity; @@ -94,6 +96,7 @@ void Mod_Init (void) { Cvar_RegisterVariable (&external_vis); Cvar_RegisterVariable (&external_ents); + Cvar_RegisterVariable (&r_md5models); // johnfitz -- create notexture miptex r_notexture_mip = (texture_t *)Mem_Alloc (sizeof (texture_t)); @@ -365,7 +368,7 @@ void Mod_ResetAll (void) qmodel_t *mod; // ericw -- free alias model VBOs - GLMesh_DeleteVertexBuffers (); + GLMesh_DeleteAllMeshBuffers (); GL_DeleteBModelAccelerationStructures (); for (i = 0, mod = mod_known; i < mod_numknown; i++, mod++) @@ -434,20 +437,30 @@ Loads a model into the cache */ static qmodel_t *Mod_LoadModel (qmodel_t *mod, qboolean crash) { - byte *buf; + byte *buf = NULL; int mod_type; if (!mod->needload) - { return mod; - } InvalidateTraceLineCache (); // // load the file // - buf = COM_LoadFile (mod->name, &mod->path_id); + if (r_md5models.value) + { + char newname[MAX_QPATH]; + q_strlcpy (newname, mod->name, sizeof (newname)); + char *extension = (char *)COM_FileGetExtension (newname); + if (strcmp (extension, "mdl") == 0) + { + q_strlcpy (extension, "md5mesh", sizeof (newname) - (extension - newname)); + buf = COM_LoadFile (newname, &mod->path_id); + } + } + if (!buf) + buf = COM_LoadFile (mod->name, &mod->path_id); if (!buf) { if (crash) @@ -479,6 +492,11 @@ static qmodel_t *Mod_LoadModel (qmodel_t *mod, qboolean crash) Mod_LoadSpriteModel (mod, buf); break; + // Spike -- md5 support + case (('M' << 0) + ('D' << 8) + ('5' << 16) + ('V' << 24)): + Mod_LoadMD5MeshModel (mod, buf); + break; + default: Mod_LoadBrushModel (mod, loadname, buf); break; @@ -576,18 +594,19 @@ static void Mod_LoadTextureTask (int i, qmodel_t **ppmod) // external textures -- first look in "textures/mapname/" then look in "textures/" COM_StripExtension (mod->name + 5, mapname, sizeof (mapname)); q_snprintf (filename, sizeof (filename), "textures/%s/#%s", mapname, tx->name + 1); // this also replaces the '*' with a '#' - data = Image_LoadImage (filename, &fwidth, &fheight); + enum srcformat fmt = SRC_RGBA; + data = Image_LoadImage (filename, &fwidth, &fheight, &fmt); if (!data) { q_snprintf (filename, sizeof (filename), "textures/#%s", tx->name + 1); - data = Image_LoadImage (filename, &fwidth, &fheight); + data = Image_LoadImage (filename, &fwidth, &fheight, &fmt); } // now load whatever we found if (data) // load external image { q_strlcpy (texturename, filename, sizeof (texturename)); - tx->gltexture = TexMgr_LoadImage (mod, texturename, fwidth, fheight, SRC_RGBA, data, filename, 0, TEXPREF_NONE); + tx->gltexture = TexMgr_LoadImage (mod, texturename, fwidth, fheight, fmt, data, filename, 0, TEXPREF_NONE); } else // use the texture from the bsp file { @@ -614,11 +633,12 @@ static void Mod_LoadTextureTask (int i, qmodel_t **ppmod) // external textures -- first look in "textures/mapname/" then look in "textures/" COM_StripExtension (mod->name + 5, mapname, sizeof (mapname)); q_snprintf (filename, sizeof (filename), "textures/%s/%s", mapname, tx->name); - data = Image_LoadImage (filename, &fwidth, &fheight); + enum srcformat fmt = SRC_RGBA; + data = Image_LoadImage (filename, &fwidth, &fheight, &fmt); if (!data) { q_snprintf (filename, sizeof (filename), "textures/%s", tx->name); - data = Image_LoadImage (filename, &fwidth, &fheight); + data = Image_LoadImage (filename, &fwidth, &fheight, &fmt); } // now load whatever we found @@ -626,20 +646,20 @@ static void Mod_LoadTextureTask (int i, qmodel_t **ppmod) { char filename2[MAX_OSPATH]; - tx->gltexture = TexMgr_LoadImage (mod, filename, fwidth, fheight, SRC_RGBA, data, filename, 0, TEXPREF_MIPMAP | extraflags); + tx->gltexture = TexMgr_LoadImage (mod, filename, fwidth, fheight, fmt, data, filename, 0, TEXPREF_MIPMAP | extraflags); Mem_Free (data); // now try to load glow/luma image from the same place q_snprintf (filename2, sizeof (filename2), "%s_glow", filename); - data = Image_LoadImage (filename2, &fwidth, &fheight); + data = Image_LoadImage (filename2, &fwidth, &fheight, &fmt); if (!data) { q_snprintf (filename2, sizeof (filename2), "%s_luma", filename); - data = Image_LoadImage (filename2, &fwidth, &fheight); + data = Image_LoadImage (filename2, &fwidth, &fheight, &fmt); } if (data) - tx->fullbright = TexMgr_LoadImage (mod, filename2, fwidth, fheight, SRC_RGBA, data, filename2, 0, TEXPREF_MIPMAP | extraflags); + tx->fullbright = TexMgr_LoadImage (mod, filename2, fwidth, fheight, fmt, data, filename2, 0, TEXPREF_MIPMAP | extraflags); } else // use the texture from the bsp file { @@ -2539,8 +2559,6 @@ ALIAS MODELS ============================================================================== */ -aliashdr_t *pheader; - stvert_t stverts[MAXALIASVERTS]; mtriangle_t triangles[MAXALIASTRIS]; @@ -2549,19 +2567,17 @@ mtriangle_t triangles[MAXALIASTRIS]; trivertx_t *poseverts[MAXALIASFRAMES]; int posenum; -byte **player_8bit_texels_tbl; -byte *player_8bit_texels; - /* ================= Mod_LoadAliasFrame ================= */ -void *Mod_LoadAliasFrame (void *pin, maliasframedesc_t *frame) +void *Mod_LoadAliasFrame (void *pin, aliashdr_t *pheader, const int index) { - trivertx_t *pinframe; - int i; - daliasframe_t *pdaliasframe; + maliasframedesc_t *frame = &pheader->frames[index]; + trivertx_t *pinframe; + int i; + daliasframe_t *pdaliasframe; if (posenum >= MAXALIASFRAMES) Sys_Error ("posenum >= MAXALIASFRAMES"); @@ -2595,12 +2611,13 @@ void *Mod_LoadAliasFrame (void *pin, maliasframedesc_t *frame) Mod_LoadAliasGroup ================= */ -void *Mod_LoadAliasGroup (void *pin, maliasframedesc_t *frame) +void *Mod_LoadAliasGroup (void *pin, aliashdr_t *pheader, const int index) { - daliasgroup_t *pingroup; - int i, numframes; - daliasinterval_t *pin_intervals; - void *ptemp; + maliasframedesc_t *frame = &pheader->frames[index]; + daliasgroup_t *pingroup; + int i, numframes; + daliasinterval_t *pin_intervals; + void *ptemp; pingroup = (daliasgroup_t *)pin; @@ -2727,9 +2744,10 @@ Mod_LoadSkinTask */ typedef struct load_skin_task_args_s { - qmodel_t *mod; - byte *mod_base; - byte **ppskintypes; + aliashdr_t *pheader; + qmodel_t *mod; + byte *mod_base; + byte **ppskintypes; } load_skin_task_args_t; static void Mod_LoadSkinTask (int i, load_skin_task_args_t *args) @@ -2745,6 +2763,7 @@ static void Mod_LoadSkinTask (int i, load_skin_task_args_t *args) unsigned int texflags = TEXPREF_PAD; qmodel_t *mod = args->mod; byte *mod_base = args->mod_base; + aliashdr_t *pheader = args->pheader; size = pheader->skinwidth * pheader->skinheight; @@ -2835,7 +2854,7 @@ static void Mod_LoadSkinTask (int i, load_skin_task_args_t *args) Mod_LoadAllSkins =============== */ -void *Mod_LoadAllSkins (qmodel_t *mod, byte *mod_base, int numskins, byte *pskintype) +void *Mod_LoadAllSkins (aliashdr_t *pheader, qmodel_t *mod, byte *mod_base, int numskins, byte *pskintype) { if (numskins < 1 || numskins > MAX_SKINS) Sys_Error ("Mod_LoadAliasModel: Invalid # of skins: %d", numskins); @@ -2861,6 +2880,7 @@ void *Mod_LoadAllSkins (qmodel_t *mod, byte *mod_base, int numskins, byte *pskin } load_skin_task_args_t args = { + .pheader = pheader, .mod = mod, .mod_base = mod_base, .ppskintypes = ppskintypes, @@ -2889,7 +2909,7 @@ void *Mod_LoadAllSkins (qmodel_t *mod, byte *mod_base, int numskins, byte *pskin Mod_CalcAliasBounds -- johnfitz -- calculate bounds of alias model for nonrotated, yawrotated, and fullrotated cases ================= */ -static void Mod_CalcAliasBounds (qmodel_t *mod, aliashdr_t *a) +static void Mod_CalcAliasBounds (qmodel_t *mod, aliashdr_t *a, int numvertexes, byte *vertexes) { int i, j, k; float dist, yawradius, radius; @@ -2903,12 +2923,36 @@ static void Mod_CalcAliasBounds (qmodel_t *mod, aliashdr_t *a) radius = yawradius = 0; } - // process verts - for (i = 0; i < a->numposes; i++) - for (j = 0; j < a->numverts; j++) + switch (a->poseverttype) + { + case PV_QUAKE1: + // process verts + for (i = 0; i < a->numposes; i++) + for (j = 0; j < a->numverts; j++) + { + for (k = 0; k < 3; k++) + v[k] = poseverts[i][j].v[k] * a->scale[k] + a->scale_origin[k]; + + for (k = 0; k < 3; k++) + { + mod->mins[k] = q_min (mod->mins[k], v[k]); + mod->maxs[k] = q_max (mod->maxs[k], v[k]); + } + dist = v[0] * v[0] + v[1] * v[1]; + if (yawradius < dist) + yawradius = dist; + dist += v[2] * v[2]; + if (radius < dist) + radius = dist; + } + break; + case PV_MD5: + // process verts + md5vert_t *pv = (md5vert_t *)vertexes; + for (j = 0; j < numvertexes; j++) { for (k = 0; k < 3; k++) - v[k] = poseverts[i][j].v[k] * pheader->scale[k] + pheader->scale_origin[k]; + v[k] = pv[j].xyz[k]; for (k = 0; k < 3; k++) { @@ -2922,14 +2966,16 @@ static void Mod_CalcAliasBounds (qmodel_t *mod, aliashdr_t *a) if (radius < dist) radius = dist; } + break; + } // rbounds will be used when entity has nonzero pitch or roll - radius = sqrt (radius); + radius = sqrtf (radius); mod->rmins[0] = mod->rmins[1] = mod->rmins[2] = -radius; mod->rmaxs[0] = mod->rmaxs[1] = mod->rmaxs[2] = radius; // ybounds will be used when entity has nonzero yaw - yawradius = sqrt (yawradius); + yawradius = sqrtf (yawradius); mod->ymins[0] = mod->ymins[1] = -yawradius; mod->ymaxs[0] = mod->ymaxs[1] = yawradius; mod->ymins[2] = mod->mins[2]; @@ -3021,8 +3067,9 @@ static void Mod_LoadAliasModel (qmodel_t *mod, void *buffer) // allocate space for a working header, plus all the data except the frames, // skin and group info // - size = sizeof (aliashdr_t) + (ReadLongUnaligned (mod_base + offsetof (mdl_t, numframes)) - 1) * sizeof (pheader->frames[0]); - pheader = (aliashdr_t *)Mem_Alloc (size); + size = sizeof (aliashdr_t) + (ReadLongUnaligned (mod_base + offsetof (mdl_t, numframes)) - 1) * sizeof (maliasframedesc_t); + aliashdr_t *pheader = (aliashdr_t *)Mem_Alloc (size); + pheader->poseverttype = PV_QUAKE1; mod->flags = ReadLongUnaligned (mod_base + offsetof (mdl_t, flags)); @@ -3073,7 +3120,7 @@ static void Mod_LoadAliasModel (qmodel_t *mod, void *buffer) // load the skins // pskintype = mod_base + sizeof (mdl_t); - pskintype = Mod_LoadAllSkins (mod, mod_base, pheader->numskins, pskintype); + pskintype = Mod_LoadAllSkins (pheader, mod, mod_base, pheader->numskins, pskintype); // // load base s and t vertices @@ -3115,9 +3162,9 @@ static void Mod_LoadAliasModel (qmodel_t *mod, void *buffer) aliasframetype_t frametype; frametype = (aliasframetype_t)ReadLongUnaligned (pframetype + offsetof (daliasframetype_t, type)); if (frametype == ALIAS_SINGLE) - pframetype = Mod_LoadAliasFrame (pframetype + sizeof (daliasframetype_t), &pheader->frames[i]); + pframetype = Mod_LoadAliasFrame (pframetype + sizeof (daliasframetype_t), pheader, i); else - pframetype = Mod_LoadAliasGroup (pframetype + sizeof (daliasframetype_t), &pheader->frames[i]); + pframetype = Mod_LoadAliasGroup (pframetype + sizeof (daliasframetype_t), pheader, i); } pheader->numposes = posenum; @@ -3126,7 +3173,7 @@ static void Mod_LoadAliasModel (qmodel_t *mod, void *buffer) Mod_SetExtraFlags (mod); // johnfitz - Mod_CalcAliasBounds (mod, pheader); // johnfitz + Mod_CalcAliasBounds (mod, pheader, 0, NULL); // johnfitz // // build the draw lists @@ -3310,6 +3357,862 @@ static void Mod_LoadSpriteModel (qmodel_t *mod, void *buffer) mod->type = mod_sprite; } +/* +================================================================= +MD5 Models, for compat with the rerelease and NOT doom3. +================================================================= +md5mesh: +MD5Version 10 +commandline "" +numJoints N +numMeshes N +joints { + "name" ParentIdx ( Pos_X Y Z ) ( Quat_X Y Z ) +} +mesh { + shader "name" //file-relative path, with _%02d_%02d postfixed for skin/framegroup support. unlike doom3. + numverts N + vert # ( S T ) FirstWeight count + numtris N + tri # A B C + numweights N + weight # JointIdx Scale ( X Y Z ) +} + +md5anim: +MD5Version 10 +commandline "" +numFrames N +numJoints N +frameRate FPS +numAnimatedComponents N //joints*6ish +hierachy { + "name" ParentIdx Flags DataStart +} +bounds { + ( X Y Z ) ( X Y Z ) +} +baseframe { + ( pos_X Y Z ) ( quad_X Y Z ) +} +frame # { + RAW ... +} + +We'll unpack the animation to separate framegroups (one-pose per, for consistency with most q1 models). +*/ + +/* +================ +MD5_ParseCheck +================ +*/ +static qboolean MD5_ParseCheck (const char *s, const void **buffer) +{ + if (strcmp (com_token, s)) + return false; + *buffer = COM_Parse (*buffer); + return true; +} + +/* +================ +MD5_ParseUInt +================ +*/ +static size_t MD5_ParseUInt (const void **buffer) +{ + size_t i = SDL_strtoull (com_token, NULL, 0); + *buffer = COM_Parse (*buffer); + return i; +} + +/* +================ +MD5_ParseSInt +================ +*/ +static long MD5_ParseSInt (const void **buffer) +{ + long i = SDL_strtol (com_token, NULL, 0); + *buffer = COM_Parse (*buffer); + return i; +} + +/* +================ +MD5_ParseFloat +================ +*/ +static double MD5_ParseFloat (const void **buffer) +{ + double i = SDL_strtod (com_token, NULL); + *buffer = COM_Parse (*buffer); + return i; +} + +#define MD5EXPECT(s) \ + do \ + { \ + if (strcmp (com_token, s)) \ + Sys_Error ("Mod_LoadMD5MeshModel(%s): Expected \"%s\"", fname, s); \ + buffer = COM_Parse (buffer); \ + } while (0) +#define MD5UINT() MD5_ParseUInt (&buffer) +#define MD5SINT() MD5_ParseSInt (&buffer) +#define MD5FLOAT() MD5_ParseFloat (&buffer) +#define MD5CHECK(s) MD5_ParseCheck (s, &buffer) + +/* +================ +md5vertinfo_s +================ +*/ +typedef struct md5vertinfo_s +{ + size_t firstweight; + unsigned int count; +} md5vertinfo_t; + +/* +================ +md5weightinfo_s +================ +*/ +typedef struct md5weightinfo_s +{ + size_t joint_index; + vec4_t pos; +} md5weightinfo_t; + +/* +================ +jointinfo_s +================ +*/ +typedef struct jointinfo_s +{ + ssize_t parent; //-1 for a root joint + char name[32]; + jointpose_t inverse; +} jointinfo_t; + +/* +================ +Matrix3x4_RM_Transform4 +================ +*/ +static void Matrix3x4_RM_Transform4 (const float *matrix, const float *vector, float *product) +{ + product[0] = matrix[0] * vector[0] + matrix[1] * vector[1] + matrix[2] * vector[2] + matrix[3] * vector[3]; + product[1] = matrix[4] * vector[0] + matrix[5] * vector[1] + matrix[6] * vector[2] + matrix[7] * vector[3]; + product[2] = matrix[8] * vector[0] + matrix[9] * vector[1] + matrix[10] * vector[2] + matrix[11] * vector[3]; +} + +/* +================ +GenMatrixPosQuat4Scale +================ +*/ +static void GenMatrixPosQuat4Scale (const vec3_t pos, const vec4_t quat, const vec3_t scale, float result[12]) +{ + const float x2 = quat[0] + quat[0]; + const float y2 = quat[1] + quat[1]; + const float z2 = quat[2] + quat[2]; + + const float xx = quat[0] * x2; + const float xy = quat[0] * y2; + const float xz = quat[0] * z2; + const float yy = quat[1] * y2; + const float yz = quat[1] * z2; + const float zz = quat[2] * z2; + const float xw = quat[3] * x2; + const float yw = quat[3] * y2; + const float zw = quat[3] * z2; + + result[0 * 4 + 0] = scale[0] * (1.0f - (yy + zz)); + result[1 * 4 + 0] = scale[0] * (xy + zw); + result[2 * 4 + 0] = scale[0] * (xz - yw); + + result[0 * 4 + 1] = scale[1] * (xy - zw); + result[1 * 4 + 1] = scale[1] * (1.0f - (xx + zz)); + result[2 * 4 + 1] = scale[1] * (yz + xw); + + result[0 * 4 + 2] = scale[2] * (xz + yw); + result[1 * 4 + 2] = scale[2] * (yz - xw); + result[2 * 4 + 2] = scale[2] * (1.0f - (xx + yy)); + + result[0 * 4 + 3] = pos[0]; + result[1 * 4 + 3] = pos[1]; + result[2 * 4 + 3] = pos[2]; +} + +/* +================ +Matrix3x4_Invert_Simple +================ +*/ +static void Matrix3x4_Invert_Simple (const float *in1, float *out) +{ + // we only support uniform scaling, so assume the first row is enough + // (note the lack of sqrt here, because we're trying to undo the scaling, + // this means multiplying by the inverse scale twice - squaring it, which + // makes the sqrt a waste of time) + const double scale = 1.0 / ((double)in1[0] * (double)in1[0] + (double)in1[1] * (double)in1[1] + (double)in1[2] * (double)in1[2]); + + // invert the rotation by transposing and multiplying by the squared + // reciprocal of the input matrix scale as described above + double temp[12]; + temp[0] = in1[0] * scale; + temp[1] = in1[4] * scale; + temp[2] = in1[8] * scale; + temp[4] = in1[1] * scale; + temp[5] = in1[5] * scale; + temp[6] = in1[9] * scale; + temp[8] = in1[2] * scale; + temp[9] = in1[6] * scale; + temp[10] = in1[10] * scale; + + // invert the translate + temp[3] = -(in1[3] * temp[0] + in1[7] * temp[1] + in1[11] * temp[2]); + temp[7] = -(in1[3] * temp[4] + in1[7] * temp[5] + in1[11] * temp[6]); + temp[11] = -(in1[3] * temp[8] + in1[7] * temp[9] + in1[11] * temp[10]); + + for (int i = 0; i < 12; ++i) + out[i] = (float)temp[i]; +} + +/* +================ +MD5_BakeInfluences +================ +*/ +static void MD5_BakeInfluences ( + const char *fname, jointpose_t *outposes, md5vert_t *vert, struct md5vertinfo_s *vinfo, struct md5weightinfo_s *weight, size_t numverts, size_t numweights) +{ + struct md5weightinfo_s *w; + vec3_t pos; + float scale; + unsigned int maxinfluences = 0; + float scaleimprecision = 1; + + for (size_t v = 0; v < numverts; v++, vert++, vinfo++) + { + float weights[4] = {0.0f, 0.0f, 0.0f, 0.0f}; + + // st were already loaded + // norm will need to be calculated after we have xyz info + for (int i = 0; i < 3; ++i) + vert->xyz[i] = 0; + for (int i = 0; i < 4; ++i) + { + vert->joint_weights[i] = 0; + vert->joint_indices[i] = 0; + } + + if (vinfo->firstweight + vinfo->count > numweights) + Sys_Error ("%s: weight index out of bounds", fname); + if (maxinfluences < vinfo->count) + maxinfluences = vinfo->count; + w = weight + vinfo->firstweight; + for (unsigned int i = 0; i < vinfo->count; i++, w++) + { + Matrix3x4_RM_Transform4 (outposes[w->joint_index].mat, w->pos, pos); + VectorAdd (vert->xyz, pos, vert->xyz); + + if (i < NUM_JOINT_INFLUENCES) + { + weights[i] = w->pos[3]; + vert->joint_indices[i] = w->joint_index; + } + else + { + // obnoxious code to find the lowest of the current possible joint indexes. + float lowval = weights[0]; + size_t lowidx = 0; + for (size_t k = 1; k < NUM_JOINT_INFLUENCES; ++k) + { + if (weights[k] < lowval) + { + lowval = weights[k]; + lowidx = k; + } + } + if (weights[lowidx] < w->pos[3]) + { // found a lower/unset weight, replace it. + weights[lowidx] = w->pos[3]; + vert->joint_indices[lowidx] = w->joint_index; + } + } + } + + // normalize in case we dropped some weights. + scale = weights[0] + weights[1] + weights[2] + weights[3]; + if (scale > 0) + { + if (scaleimprecision < scale) + scaleimprecision = scale; + scale = 1 / scale; + for (int k = 0; k < NUM_JOINT_INFLUENCES; ++k) + weights[k] *= scale; + } + else // something bad... + { + weights[0] = 1; + weights[1] = weights[2] = weights[3] = 0; + } + + for (int j = 0; j < 4; ++j) + vert->joint_weights[j] = (byte)(CLAMP (0.0f, weights[j], 1.0f) * 255.0f); + } + if (maxinfluences > NUM_JOINT_INFLUENCES) + Con_DWarning ("%s uses up to %u influences per vertex (weakest: %g)\n", fname, maxinfluences, scaleimprecision); +} + +/* +================ +MD5_ComputeNormals +================ +*/ +static void MD5_ComputeNormals (md5vert_t *vert, size_t numverts, unsigned short *indexes, size_t numindexes) +{ + size_t v, t; + md5vert_t *v0, *v1, *v2; + vec3_t d1, d2, norm; + for (v = 0; v < numverts; v++) + vert[v].norm[0] = vert[v].norm[1] = vert[v].norm[2] = 0; + for (t = 0; t < numindexes; t += 3) + { + v0 = &vert[indexes[t + 0]]; + v1 = &vert[indexes[t + 1]]; + v2 = &vert[indexes[t + 2]]; + + VectorSubtract (v1->xyz, v0->xyz, d1); + VectorSubtract (v2->xyz, v0->xyz, d2); + CrossProduct (d1, d2, norm); + VectorNormalize (norm); + + // FIXME: this should be weighted by each vertex angle. + VectorAdd (v0->norm, norm, v0->norm); + VectorAdd (v1->norm, norm, v1->norm); + VectorAdd (v2->norm, norm, v2->norm); + } + + // and make sure it actually makes sense. + for (v = 0; v < numverts; v++) + VectorNormalize (vert[v].norm); +} + +/* +================ +MD5_HackyModelFlags +================ +*/ +static unsigned int MD5_HackyModelFlags (const char *name) +{ + unsigned int ret = 0; + char oldmodel[MAX_QPATH]; + mdl_t *f; + COM_StripExtension (name, oldmodel, sizeof (oldmodel)); + COM_AddExtension (oldmodel, ".mdl", sizeof (oldmodel)); + + f = (mdl_t *)COM_LoadFile (oldmodel, NULL); + if (f) + { + if (com_filesize >= sizeof (*f) && LittleLong (f->ident) == IDPOLYHEADER && LittleLong (f->version) == ALIAS_VERSION) + ret = f->flags; + Mem_Free (f); + } + return ret; +} + +/* +================ +md5animctx_s +================ +*/ +typedef struct md5animctx_s +{ + void *animfile; + const void *buffer; + char fname[MAX_QPATH]; + size_t numposes; + size_t numjoints; + jointpose_t *posedata; +} md5animctx_t; + +/* +================ +MD5Anim_Begin +This is split into two because aliashdr_t has silly trailing framegroup info. +================ +*/ +static void MD5Anim_Begin (md5animctx_t *ctx, const char *fname) +{ + // Load an md5anim into it, if we can. + COM_StripExtension (fname, ctx->fname, sizeof (ctx->fname)); + COM_AddExtension (ctx->fname, ".md5anim", sizeof (ctx->fname)); + fname = ctx->fname; + ctx->animfile = COM_LoadFile (fname, NULL); + ctx->numposes = 0; + + if (ctx->animfile) + { + const void *buffer = COM_Parse (ctx->animfile); + MD5EXPECT ("MD5Version"); + MD5EXPECT ("10"); + if (MD5CHECK ("commandline")) + buffer = COM_Parse (buffer); + MD5EXPECT ("numFrames"); + ctx->numposes = MD5UINT (); + MD5EXPECT ("numJoints"); + ctx->numjoints = MD5UINT (); + MD5EXPECT ("frameRate"); // irrelevant here + + if (ctx->numposes <= 0) + Sys_Error ("%s has no poses", fname); + + ctx->buffer = buffer; + } +} + +/* +================ +MD5Anim_Load +================ +*/ +static void MD5Anim_Load (md5animctx_t *ctx, jointinfo_t *joints, size_t numjoints) +{ + const char *fname = ctx->fname; + struct + { + unsigned int flags, offset; + } *ab; + size_t rawcount; + float *raw, *r; + jointpose_t *outposes; + const void *buffer = COM_Parse (ctx->buffer); + size_t j; + + if (!buffer) + { + Mem_Free (ctx->animfile); + return; + } + + MD5EXPECT ("numAnimatedComponents"); + rawcount = MD5UINT (); + + if (ctx->numjoints != numjoints) + Sys_Error ("%s has incorrect joint count", fname); + + raw = Mem_Alloc (sizeof (*raw) * (rawcount + 6)); + ab = Mem_Alloc (sizeof (*ab) * ctx->numjoints); + + ctx->posedata = outposes = Mem_Alloc (sizeof (*outposes) * ctx->numjoints * ctx->numposes); + + MD5EXPECT ("hierarchy"); + MD5EXPECT ("{"); + for (j = 0; j < ctx->numjoints; j++) + { + // validate stuff + if (strcmp (joints[j].name, com_token)) + Sys_Error ("%s: joint was renamed", fname); + buffer = COM_Parse (buffer); + if (joints[j].parent != MD5SINT ()) + Sys_Error ("%s: joint has wrong parent", fname); + // new info + ab[j].flags = MD5UINT (); + if (ab[j].flags & ~63) + Sys_Error ("%s: joint has unsupported flags", fname); + ab[j].offset = MD5UINT (); + if (ab[j].offset > rawcount + 6) + Sys_Error ("%s: joint has bad offset", fname); + } + MD5EXPECT ("}"); + MD5EXPECT ("bounds"); + MD5EXPECT ("{"); + while (MD5CHECK ("(")) + { + (void)MD5FLOAT (); + (void)MD5FLOAT (); + (void)MD5FLOAT (); + MD5EXPECT (")"); + + MD5EXPECT ("("); + (void)MD5FLOAT (); + (void)MD5FLOAT (); + (void)MD5FLOAT (); + MD5EXPECT (")"); + } + MD5EXPECT ("}"); + + MD5EXPECT ("baseframe"); + MD5EXPECT ("{"); + while (MD5CHECK ("(")) + { + (void)MD5FLOAT (); + (void)MD5FLOAT (); + (void)MD5FLOAT (); + MD5EXPECT (")"); + + MD5EXPECT ("("); + (void)MD5FLOAT (); + (void)MD5FLOAT (); + (void)MD5FLOAT (); + MD5EXPECT (")"); + } + MD5EXPECT ("}"); + + while (MD5CHECK ("frame")) + { + size_t idx = MD5UINT (); + if (idx >= ctx->numposes) + Sys_Error ("%s: invalid pose index", fname); + MD5EXPECT ("{"); + for (j = 0; j < rawcount; j++) + raw[j] = MD5FLOAT (); + MD5EXPECT ("}"); + + // okay, we have our raw info, unpack the actual joint info. + for (j = 0; j < ctx->numjoints; j++) + { + vec3_t pos = {0, 0, 0}; + static vec3_t scale = {1, 1, 1}; + vec4_t quat = {0, 0, 0}; + r = raw + ab[j].offset; + if (ab[j].flags & 1) + pos[0] = *r++; + if (ab[j].flags & 2) + pos[1] = *r++; + if (ab[j].flags & 4) + pos[2] = *r++; + + if (ab[j].flags & 8) + quat[0] = *r++; + if (ab[j].flags & 16) + quat[1] = *r++; + if (ab[j].flags & 32) + quat[2] = *r++; + + quat[3] = 1 - DotProduct (quat, quat); + if (quat[3] < 0) + quat[3] = 0; // we have no imagination. + quat[3] = -sqrtf (quat[3]); + + GenMatrixPosQuat4Scale (pos, quat, scale, outposes[idx * ctx->numjoints + j].mat); + } + } + Mem_Free (raw); + Mem_Free (ab); + Mem_Free (ctx->animfile); +} + +/* +================ +Mod_LoadMD5MeshModel +================ +*/ +static void Mod_LoadMD5MeshModel (qmodel_t *mod, const void *buffer) +{ + const char *fname = mod->name; + unsigned short *poutindexes = NULL; + md5vert_t *poutvertexes = NULL; + aliashdr_t *outhdr, *surf; + size_t hdrsize; + size_t numjoints, j; + size_t nummeshes, m; + char texname[MAX_QPATH]; + md5vertinfo_t *vinfo; + md5weightinfo_t *weight; + size_t numweights; + + md5animctx_t anim = {NULL}; + + buffer = COM_Parse (buffer); + + MD5EXPECT ("MD5Version"); + MD5EXPECT ("10"); + if (MD5CHECK ("commandline")) + buffer = COM_Parse (buffer); + MD5EXPECT ("numJoints"); + numjoints = MD5UINT (); + MD5EXPECT ("numMeshes"); + nummeshes = MD5UINT (); + + if (numjoints <= 0) + Sys_Error ("%s has no joints", mod->name); + if (nummeshes <= 0) + Sys_Error ("%s has no meshes", mod->name); + + if (strcmp (com_token, "joints")) + Sys_Error ("Mod_LoadMD5MeshModel(%s): Expected \"%s\"", fname, "joints"); + MD5Anim_Begin (&anim, fname); + buffer = COM_Parse (buffer); + + hdrsize = sizeof (*outhdr) - sizeof (outhdr->frames); + hdrsize += sizeof (outhdr->frames) * anim.numposes; + outhdr = Mem_Alloc (hdrsize * numjoints); + TEMP_ALLOC_ZEROED (jointinfo_t, joint_infos, numjoints); + TEMP_ALLOC_ZEROED (jointpose_t, joint_poses, numjoints); + + MD5EXPECT ("{"); + for (j = 0; j < numjoints; j++) + { + vec3_t pos; + static vec3_t scale = {1, 1, 1}; + vec4_t quat; + q_strlcpy (joint_infos[j].name, com_token, sizeof (joint_infos[j].name)); + buffer = COM_Parse (buffer); + joint_infos[j].parent = MD5SINT (); + if (joint_infos[j].parent < -1 && joint_infos[j].parent >= (ssize_t)numjoints) + Sys_Error ("joint index out of bounds"); + MD5EXPECT ("("); + pos[0] = MD5FLOAT (); + pos[1] = MD5FLOAT (); + pos[2] = MD5FLOAT (); + MD5EXPECT (")"); + MD5EXPECT ("("); + quat[0] = MD5FLOAT (); + quat[1] = MD5FLOAT (); + quat[2] = MD5FLOAT (); + quat[3] = 1 - DotProduct (quat, quat); + if (quat[3] < 0) + quat[3] = 0; // we have no imagination. + quat[3] = -sqrtf (quat[3]); + MD5EXPECT (")"); + + GenMatrixPosQuat4Scale (pos, quat, scale, joint_poses[j].mat); + Matrix3x4_Invert_Simple (joint_poses[j].mat, joint_infos[j].inverse.mat); // absolute, so we can just invert now. + } + + if (strcmp (com_token, "}")) + Sys_Error ("Mod_LoadMD5MeshModel(%s): Expected \"%s\"", fname, "}"); + MD5Anim_Load (&anim, joint_infos, numjoints); + buffer = COM_Parse (buffer); + + int index_offset = 0; + int vertex_offset = 0; + for (m = 0; m < nummeshes; m++) + { + MD5EXPECT ("mesh"); + MD5EXPECT ("{"); + + surf = (aliashdr_t *)((byte *)outhdr + m * hdrsize); + if (m + 1 < nummeshes) + surf->nextsurface = (aliashdr_t *)((byte *)outhdr + (m + 1) * hdrsize); + else + surf->nextsurface = NULL; + + surf->poseverttype = PV_MD5; + for (j = 0; j < 3; j++) + { + surf->scale_origin[j] = 0; + surf->scale[j] = 1.0; + } + + surf->numjoints = numjoints; + + if (anim.numposes) + { + for (j = 0; j < anim.numposes; j++) + { + surf->frames[j].firstpose = j; + surf->frames[j].numposes = 1; + surf->frames[j].interval = 0.1; + } + surf->numframes = j; + } + + MD5EXPECT ("shader"); + // MD5 violation: the skin is a single material. adding prefixes/postfixes here is the wrong thing to do. + // but we do so anyway, because rerelease compat. + for (surf->numskins = 0; surf->numskins < MAX_SKINS; surf->numskins++) + { + unsigned int fwidth, fheight, f; + void *data; + for (f = 0; f < countof (surf->gltextures[0]); f++) + { + q_snprintf (texname, sizeof (texname), "progs/%s_%02u_%02u", com_token, surf->numskins, f); + + enum srcformat fmt = SRC_RGBA; + data = Image_LoadImage (texname, (int *)&fwidth, (int *)&fheight, &fmt); + if (data) // load external image + { + surf->gltextures[surf->numskins][f] = + TexMgr_LoadImage (mod, texname, fwidth, fheight, fmt, data, texname, 0, TEXPREF_ALPHA | TEXPREF_NOBRIGHT | TEXPREF_MIPMAP); + surf->fbtextures[surf->numskins][f] = NULL; + if (fmt == SRC_INDEXED) + { + // 8bit base texture. use it for fullbrights. + for (j = 0; j < fwidth * fheight; j++) + { + if (((byte *)data)[j] > 223) + { + surf->fbtextures[surf->numskins][f] = TexMgr_LoadImage ( + mod, va ("%s_luma", texname), fwidth, fheight, SRC_INDEXED, data, texname, 0, + TEXPREF_ALPHA | TEXPREF_FULLBRIGHT | TEXPREF_MIPMAP); + break; + } + } + } + else + { + // we found a 32bit base texture. + if (!surf->fbtextures[surf->numskins][f]) + { + q_snprintf (texname, sizeof (texname), "progs/%s_%02u_%02u_glow", com_token, surf->numskins, f); + surf->fbtextures[surf->numskins][f] = + TexMgr_LoadImage (mod, texname, surf->skinwidth, surf->skinheight, SRC_RGBA, NULL, texname, 0, TEXPREF_MIPMAP); + } + if (!surf->fbtextures[surf->numskins][f]) + { + q_snprintf (texname, sizeof (texname), "progs/%s_%02u_%02u_luma", com_token, surf->numskins, f); + surf->fbtextures[surf->numskins][f] = + TexMgr_LoadImage (mod, texname, surf->skinwidth, surf->skinheight, SRC_RGBA, NULL, texname, 0, TEXPREF_MIPMAP); + } + } + + Mem_Free (data); + } + else + break; + } + if (f == 0) + break; // no images loaded... + + // this stuff is hideous. + if (f < 2) + { + surf->gltextures[surf->numskins][1] = surf->gltextures[surf->numskins][0]; + surf->fbtextures[surf->numskins][1] = surf->fbtextures[surf->numskins][0]; + } + if (f == 3) + Con_Warning ("progs/%s_%02u_##: 3 skinframes found...\n", com_token, surf->numskins); + if (f < 4) + { + surf->gltextures[surf->numskins][3] = surf->gltextures[surf->numskins][1]; + surf->gltextures[surf->numskins][2] = surf->gltextures[surf->numskins][0]; + + surf->fbtextures[surf->numskins][3] = surf->fbtextures[surf->numskins][1]; + surf->fbtextures[surf->numskins][2] = surf->fbtextures[surf->numskins][0]; + } + } + + surf->skinwidth = surf->gltextures[0][0] ? surf->gltextures[0][0]->width : 1; + surf->skinheight = surf->gltextures[0][0] ? surf->gltextures[0][0]->height : 1; + surf->numposes = 1; + + buffer = COM_Parse (buffer); + MD5EXPECT ("numverts"); + surf->numverts_vbo = surf->numverts = MD5UINT (); + + vinfo = Mem_Alloc (sizeof (*vinfo) * surf->numverts); + poutvertexes = Mem_Realloc (poutvertexes, sizeof (*poutvertexes) * (vertex_offset + surf->numverts)); + while (MD5CHECK ("vert")) + { + size_t idx = MD5UINT (); + if (idx >= (size_t)surf->numverts) + Sys_Error ("vertex index out of bounds"); + MD5EXPECT ("("); + poutvertexes[vertex_offset + idx].st[0] = MD5FLOAT (); + poutvertexes[vertex_offset + idx].st[1] = MD5FLOAT (); + MD5EXPECT (")"); + vinfo[idx].firstweight = MD5UINT (); + vinfo[idx].count = MD5UINT (); + } + vertex_offset += surf->numverts; + + MD5EXPECT ("numtris"); + surf->numtris = MD5UINT (); + surf->numindexes = surf->numtris * 3; + poutindexes = Mem_Realloc (poutindexes, sizeof (*poutindexes) * (index_offset + surf->numindexes)); + while (MD5CHECK ("tri")) + { + size_t idx = MD5UINT (); + if (idx >= (size_t)surf->numtris) + Sys_Error ("triangle index out of bounds"); + idx *= 3; + for (j = 0; j < 3; j++) + { + size_t t = MD5UINT (); + if (t > (size_t)surf->numverts) + Sys_Error ("vertex index out of bounds"); + poutindexes[index_offset + idx + j] = t; + } + } + index_offset += surf->numindexes; + + // md5 is a gpu-unfriendly interchange format. :( + MD5EXPECT ("numweights"); + numweights = MD5UINT (); + weight = Mem_Alloc (sizeof (*weight) * numweights); + while (MD5CHECK ("weight")) + { + size_t idx = MD5UINT (); + if (idx >= numweights) + Sys_Error ("weight index out of bounds"); + + weight[idx].joint_index = MD5UINT (); + if (weight[idx].joint_index >= numjoints) + Sys_Error ("joint index out of bounds"); + weight[idx].pos[3] = MD5FLOAT (); + MD5EXPECT ("("); + weight[idx].pos[0] = MD5FLOAT () * weight[idx].pos[3]; + weight[idx].pos[1] = MD5FLOAT () * weight[idx].pos[3]; + weight[idx].pos[2] = MD5FLOAT () * weight[idx].pos[3]; + MD5EXPECT (")"); + } + // so make it gpu-friendly. + MD5_BakeInfluences (fname, joint_poses, poutvertexes, vinfo, weight, surf->numverts, numweights); + // and now make up the normals that the format lacks. we'll still probably have issues from seams, but then so did qme, so at least its faithful... :P + MD5_ComputeNormals (poutvertexes, surf->numverts, poutindexes, surf->numindexes); + + Mem_Free (weight); + Mem_Free (vinfo); + + MD5EXPECT ("}"); + } + + TEMP_ALLOC_ZEROED (jointpose_t, inverted_joints, anim.numjoints * anim.numposes); + TEMP_ALLOC_ZEROED (jointpose_t, concat_joints, anim.numjoints); + for (size_t pose_index = 0; pose_index < anim.numposes; ++pose_index) + { + const jointpose_t *in_pose = anim.posedata + (pose_index * anim.numjoints); + const jointpose_t *out_pose = inverted_joints + (pose_index * anim.numjoints); + for (size_t joint_index = 0; joint_index < anim.numjoints; ++joint_index) + { + // concat it onto the parent (relative->abs) + if (joint_infos[joint_index].parent < 0) + memcpy (concat_joints[joint_index].mat, in_pose[joint_index].mat, sizeof (jointpose_t)); + else + R_ConcatTransforms ( + (void *)concat_joints[joint_infos[joint_index].parent].mat, (void *)in_pose[joint_index].mat, (void *)concat_joints[joint_index].mat); + // and finally invert it + R_ConcatTransforms ((void *)concat_joints[joint_index].mat, (void *)joint_infos[joint_index].inverse.mat, (void *)out_pose[joint_index].mat); + } + } + Mem_Free (anim.posedata); + + GLMesh_UploadBuffers (mod, outhdr, poutindexes, (byte *)poutvertexes, NULL, inverted_joints); + TEMP_FREE (concat_joints); + TEMP_FREE (inverted_joints); + + // the md5 format does not have its own modelflags, yet we still need to know about trails and rotating etc + mod->flags = MD5_HackyModelFlags (mod->name); + + mod->synctype = ST_FRAMETIME; // keep MD5 animations synced to when .frame is changed. framegroups are otherwise not very useful. + mod->type = mod_alias; + mod->extradata = (byte *)outhdr; + + Mod_CalcAliasBounds (mod, outhdr, vertex_offset, (byte *)poutvertexes); // johnfitz + + TEMP_FREE (joint_poses); + TEMP_FREE (joint_infos) + Mem_Free (poutvertexes); + Mem_Free (poutindexes); +} + //============================================================================= /* diff --git a/Quake/gl_model.h b/Quake/gl_model.h index c3b9ccca3..66cb447d6 100644 --- a/Quake/gl_model.h +++ b/Quake/gl_model.h @@ -339,7 +339,16 @@ typedef struct mtriangle_s } mtriangle_t; #define MAX_SKINS 32 -typedef struct + +typedef struct aliashdr_s aliashdr_t; + +typedef enum +{ + PV_QUAKE1 = 1, // trivertx_t + PV_MD5, // iqmvert_t +} poseverttype_t; + +typedef struct aliashdr_s { int ident; int version; @@ -359,18 +368,34 @@ typedef struct int numindexes; int numverts_vbo; int numposes; - int poseverts; - int posedata; // numposes*poseverts trivert_t + aliashdr_t *nextsurface; // spike + int numjoints; // spike -- for md5 + poseverttype_t poseverttype; struct gltexture_s *gltextures[MAX_SKINS][4]; // johnfitz struct gltexture_s *fbtextures[MAX_SKINS][4]; // johnfitz byte *texels[MAX_SKINS]; // only for player skins maliasframedesc_t frames[1]; // variable sized } aliashdr_t; +#define NUM_JOINT_INFLUENCES 4 + +typedef struct md5vert_s +{ + float xyz[3]; + float norm[3]; + float st[2]; // these are separate for consistency + byte joint_weights[NUM_JOINT_INFLUENCES]; + byte joint_indices[NUM_JOINT_INFLUENCES]; +} md5vert_t; + +typedef struct jointpose_s +{ + float mat[12]; +} jointpose_t; // pose data for a single joint. + #define MAXALIASVERTS 2000 // johnfitz -- was 1024 #define MAXALIASFRAMES 1024 // spike -- was 256 #define MAXALIASTRIS 4096 // ericw -- was 2048 -extern aliashdr_t *pheader; extern stvert_t stverts[MAXALIASVERTS]; extern mtriangle_t triangles[MAXALIASTRIS]; extern trivertx_t *poseverts[MAXALIASFRAMES]; @@ -514,9 +539,11 @@ typedef struct qmodel_s VkBuffer index_buffer; struct glheap_s *index_heap; struct glheapnode_s *index_heap_node; - int vboindexofs; // offset in vbo of the hdr->numindexes unsigned shorts - int vboxyzofs; // offset in vbo of hdr->numposes*hdr->numverts_vbo meshxyz_t - int vbostofs; // offset in vbo of hdr->numverts_vbo meshst_t + int vbostofs; // offset in vbo of hdr->numverts_vbo meshst_t + VkBuffer joints_buffer; + struct glheap_s *joints_heap; + struct glheapnode_s *joints_heap_node; + VkDescriptorSet joints_set; // // additional model data diff --git a/Quake/gl_rmisc.c b/Quake/gl_rmisc.c index d308ace04..d13ae60b5 100644 --- a/Quake/gl_rmisc.c +++ b/Quake/gl_rmisc.c @@ -709,6 +709,39 @@ void R_StagingEndCopy () SDL_UnlockMutex (staging_mutex); } +/* +=============== +R_StagingUploadBuffer +=============== +*/ +void R_StagingUploadBuffer (const VkBuffer buffer, const size_t size, const byte *data) +{ + size_t remaining_size = size; + size_t copy_offset = 0; + + while (remaining_size > 0) + { + const int size_to_copy = q_min (remaining_size, vulkan_globals.staging_buffer_size); + VkBuffer staging_buffer; + VkCommandBuffer command_buffer; + int staging_offset; + unsigned char *staging_ptr = R_StagingAllocate (size_to_copy, 1, &command_buffer, &staging_buffer, &staging_offset); + + VkBufferCopy region; + region.srcOffset = staging_offset; + region.dstOffset = copy_offset; + region.size = size_to_copy; + vkCmdCopyBuffer (command_buffer, staging_buffer, buffer, 1, ®ion); + + R_StagingBeginCopy (); + memcpy (staging_ptr, (byte *)data + copy_offset, size_to_copy); + R_StagingEndCopy (); + + copy_offset += size_to_copy; + remaining_size -= size_to_copy; + } +} + /* =============== R_InitDynamicBuffers @@ -1232,6 +1265,25 @@ void R_CreateDescriptorSetLayouts () GL_SetObjectName ((uint64_t)vulkan_globals.ubo_set_layout.handle, VK_OBJECT_TYPE_DESCRIPTOR_SET_LAYOUT, "single dynamic UBO"); } + { + ZEROED_STRUCT (VkDescriptorSetLayoutBinding, joints_buffer_layout_bindings); + joints_buffer_layout_bindings.binding = 0; + joints_buffer_layout_bindings.descriptorCount = 1; + joints_buffer_layout_bindings.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; + joints_buffer_layout_bindings.stageFlags = VK_SHADER_STAGE_ALL_GRAPHICS; + + descriptor_set_layout_create_info.bindingCount = 1; + descriptor_set_layout_create_info.pBindings = &joints_buffer_layout_bindings; + + memset (&vulkan_globals.joints_buffer_set_layout, 0, sizeof (vulkan_globals.joints_buffer_set_layout)); + vulkan_globals.joints_buffer_set_layout.num_storage_buffers = 1; + + err = vkCreateDescriptorSetLayout (vulkan_globals.device, &descriptor_set_layout_create_info, NULL, &vulkan_globals.joints_buffer_set_layout.handle); + if (err != VK_SUCCESS) + Sys_Error ("vkCreateDescriptorSetLayout failed"); + GL_SetObjectName ((uint64_t)vulkan_globals.joints_buffer_set_layout.handle, VK_OBJECT_TYPE_DESCRIPTOR_SET_LAYOUT, "joints buffer"); + } + { ZEROED_STRUCT (VkDescriptorSetLayoutBinding, input_attachment_layout_bindings); input_attachment_layout_bindings.binding = 0; @@ -1554,11 +1606,36 @@ void R_CreatePipelineLayouts () pipeline_layout_create_info.pushConstantRangeCount = 1; pipeline_layout_create_info.pPushConstantRanges = &push_constant_range; - err = vkCreatePipelineLayout (vulkan_globals.device, &pipeline_layout_create_info, NULL, &vulkan_globals.alias_pipeline.layout.handle); + err = vkCreatePipelineLayout (vulkan_globals.device, &pipeline_layout_create_info, NULL, &vulkan_globals.alias_pipelines[0].layout.handle); + if (err != VK_SUCCESS) + Sys_Error ("vkCreatePipelineLayout failed"); + GL_SetObjectName ((uint64_t)vulkan_globals.alias_pipelines[0].layout.handle, VK_OBJECT_TYPE_PIPELINE_LAYOUT, "alias_pipeline_layout"); + vulkan_globals.alias_pipelines[0].layout.push_constant_range = push_constant_range; + } + + { + // MD5 + VkDescriptorSetLayout md5_descriptor_set_layouts[4] = { + vulkan_globals.single_texture_set_layout.handle, vulkan_globals.single_texture_set_layout.handle, vulkan_globals.ubo_set_layout.handle, + vulkan_globals.joints_buffer_set_layout.handle}; + + ZEROED_STRUCT (VkPushConstantRange, push_constant_range); + push_constant_range.offset = 0; + push_constant_range.size = 24 * sizeof (float); + push_constant_range.stageFlags = VK_SHADER_STAGE_ALL_GRAPHICS; + + ZEROED_STRUCT (VkPipelineLayoutCreateInfo, pipeline_layout_create_info); + pipeline_layout_create_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; + pipeline_layout_create_info.setLayoutCount = 4; + pipeline_layout_create_info.pSetLayouts = md5_descriptor_set_layouts; + pipeline_layout_create_info.pushConstantRangeCount = 1; + pipeline_layout_create_info.pPushConstantRanges = &push_constant_range; + + err = vkCreatePipelineLayout (vulkan_globals.device, &pipeline_layout_create_info, NULL, &vulkan_globals.md5_pipelines[0].layout.handle); if (err != VK_SUCCESS) Sys_Error ("vkCreatePipelineLayout failed"); - GL_SetObjectName ((uint64_t)vulkan_globals.alias_pipeline.layout.handle, VK_OBJECT_TYPE_PIPELINE_LAYOUT, "alias_pipeline_layout"); - vulkan_globals.alias_pipeline.layout.push_constant_range = push_constant_range; + GL_SetObjectName ((uint64_t)vulkan_globals.md5_pipelines[0].layout.handle, VK_OBJECT_TYPE_PIPELINE_LAYOUT, "md5_pipeline_layout"); + vulkan_globals.md5_pipelines[0].layout.push_constant_range = push_constant_range; } { @@ -2005,6 +2082,8 @@ static VkVertexInputAttributeDescription world_vertex_input_attribute_descriptio static VkVertexInputBindingDescription world_vertex_binding_description; static VkVertexInputAttributeDescription alias_vertex_input_attribute_descriptions[5]; static VkVertexInputBindingDescription alias_vertex_binding_descriptions[3]; +static VkVertexInputAttributeDescription md5_vertex_input_attribute_descriptions[5]; +static VkVertexInputBindingDescription md5_vertex_binding_description; #define DECLARE_SHADER_MODULE(name) static VkShaderModule name##_module #define CREATE_SHADER_MODULE(name) \ @@ -2034,6 +2113,7 @@ DECLARE_SHADER_MODULE (world_frag); DECLARE_SHADER_MODULE (alias_vert); DECLARE_SHADER_MODULE (alias_frag); DECLARE_SHADER_MODULE (alias_alphatest_frag); +DECLARE_SHADER_MODULE (md5_vert); DECLARE_SHADER_MODULE (sky_layer_vert); DECLARE_SHADER_MODULE (sky_layer_frag); DECLARE_SHADER_MODULE (sky_box_frag); @@ -2063,70 +2143,104 @@ R_InitVertexAttributes */ static void R_InitVertexAttributes () { - basic_vertex_input_attribute_descriptions[0].binding = 0; - basic_vertex_input_attribute_descriptions[0].format = VK_FORMAT_R32G32B32_SFLOAT; - basic_vertex_input_attribute_descriptions[0].location = 0; - basic_vertex_input_attribute_descriptions[0].offset = 0; - basic_vertex_input_attribute_descriptions[1].binding = 0; - basic_vertex_input_attribute_descriptions[1].format = VK_FORMAT_R32G32_SFLOAT; - basic_vertex_input_attribute_descriptions[1].location = 1; - basic_vertex_input_attribute_descriptions[1].offset = 12; - basic_vertex_input_attribute_descriptions[2].binding = 0; - basic_vertex_input_attribute_descriptions[2].format = VK_FORMAT_R8G8B8A8_UNORM; - basic_vertex_input_attribute_descriptions[2].location = 2; - basic_vertex_input_attribute_descriptions[2].offset = 20; - - basic_vertex_binding_description.binding = 0; - basic_vertex_binding_description.inputRate = VK_VERTEX_INPUT_RATE_VERTEX; - basic_vertex_binding_description.stride = 24; - - world_vertex_input_attribute_descriptions[0].binding = 0; - world_vertex_input_attribute_descriptions[0].format = VK_FORMAT_R32G32B32_SFLOAT; - world_vertex_input_attribute_descriptions[0].location = 0; - world_vertex_input_attribute_descriptions[0].offset = 0; - world_vertex_input_attribute_descriptions[1].binding = 0; - world_vertex_input_attribute_descriptions[1].format = VK_FORMAT_R32G32_SFLOAT; - world_vertex_input_attribute_descriptions[1].location = 1; - world_vertex_input_attribute_descriptions[1].offset = 12; - world_vertex_input_attribute_descriptions[2].binding = 0; - world_vertex_input_attribute_descriptions[2].format = VK_FORMAT_R32G32_SFLOAT; - world_vertex_input_attribute_descriptions[2].location = 2; - world_vertex_input_attribute_descriptions[2].offset = 20; - - world_vertex_binding_description.binding = 0; - world_vertex_binding_description.inputRate = VK_VERTEX_INPUT_RATE_VERTEX; - world_vertex_binding_description.stride = 28; - - alias_vertex_input_attribute_descriptions[0].binding = 0; - alias_vertex_input_attribute_descriptions[0].format = VK_FORMAT_R32G32_SFLOAT; - alias_vertex_input_attribute_descriptions[0].location = 0; - alias_vertex_input_attribute_descriptions[0].offset = 0; - alias_vertex_input_attribute_descriptions[1].binding = 1; - alias_vertex_input_attribute_descriptions[1].format = VK_FORMAT_R8G8B8A8_UNORM; - alias_vertex_input_attribute_descriptions[1].location = 1; - alias_vertex_input_attribute_descriptions[1].offset = 0; - alias_vertex_input_attribute_descriptions[2].binding = 1; - alias_vertex_input_attribute_descriptions[2].format = VK_FORMAT_R8G8B8A8_SNORM; - alias_vertex_input_attribute_descriptions[2].location = 2; - alias_vertex_input_attribute_descriptions[2].offset = 4; - alias_vertex_input_attribute_descriptions[3].binding = 2; - alias_vertex_input_attribute_descriptions[3].format = VK_FORMAT_R8G8B8A8_UNORM; - alias_vertex_input_attribute_descriptions[3].location = 3; - alias_vertex_input_attribute_descriptions[3].offset = 0; - alias_vertex_input_attribute_descriptions[4].binding = 2; - alias_vertex_input_attribute_descriptions[4].format = VK_FORMAT_R8G8B8A8_SNORM; - alias_vertex_input_attribute_descriptions[4].location = 4; - alias_vertex_input_attribute_descriptions[4].offset = 4; - - alias_vertex_binding_descriptions[0].binding = 0; - alias_vertex_binding_descriptions[0].inputRate = VK_VERTEX_INPUT_RATE_VERTEX; - alias_vertex_binding_descriptions[0].stride = 8; - alias_vertex_binding_descriptions[1].binding = 1; - alias_vertex_binding_descriptions[1].inputRate = VK_VERTEX_INPUT_RATE_VERTEX; - alias_vertex_binding_descriptions[1].stride = 8; - alias_vertex_binding_descriptions[2].binding = 2; - alias_vertex_binding_descriptions[2].inputRate = VK_VERTEX_INPUT_RATE_VERTEX; - alias_vertex_binding_descriptions[2].stride = 8; + { + basic_vertex_input_attribute_descriptions[0].binding = 0; + basic_vertex_input_attribute_descriptions[0].format = VK_FORMAT_R32G32B32_SFLOAT; + basic_vertex_input_attribute_descriptions[0].location = 0; + basic_vertex_input_attribute_descriptions[0].offset = 0; + basic_vertex_input_attribute_descriptions[1].binding = 0; + basic_vertex_input_attribute_descriptions[1].format = VK_FORMAT_R32G32_SFLOAT; + basic_vertex_input_attribute_descriptions[1].location = 1; + basic_vertex_input_attribute_descriptions[1].offset = 12; + basic_vertex_input_attribute_descriptions[2].binding = 0; + basic_vertex_input_attribute_descriptions[2].format = VK_FORMAT_R8G8B8A8_UNORM; + basic_vertex_input_attribute_descriptions[2].location = 2; + basic_vertex_input_attribute_descriptions[2].offset = 20; + + basic_vertex_binding_description.binding = 0; + basic_vertex_binding_description.inputRate = VK_VERTEX_INPUT_RATE_VERTEX; + basic_vertex_binding_description.stride = 24; + } + + { + world_vertex_input_attribute_descriptions[0].binding = 0; + world_vertex_input_attribute_descriptions[0].format = VK_FORMAT_R32G32B32_SFLOAT; + world_vertex_input_attribute_descriptions[0].location = 0; + world_vertex_input_attribute_descriptions[0].offset = 0; + world_vertex_input_attribute_descriptions[1].binding = 0; + world_vertex_input_attribute_descriptions[1].format = VK_FORMAT_R32G32_SFLOAT; + world_vertex_input_attribute_descriptions[1].location = 1; + world_vertex_input_attribute_descriptions[1].offset = 12; + world_vertex_input_attribute_descriptions[2].binding = 0; + world_vertex_input_attribute_descriptions[2].format = VK_FORMAT_R32G32_SFLOAT; + world_vertex_input_attribute_descriptions[2].location = 2; + world_vertex_input_attribute_descriptions[2].offset = 20; + + world_vertex_binding_description.binding = 0; + world_vertex_binding_description.inputRate = VK_VERTEX_INPUT_RATE_VERTEX; + world_vertex_binding_description.stride = 28; + } + + { + alias_vertex_input_attribute_descriptions[0].binding = 0; + alias_vertex_input_attribute_descriptions[0].format = VK_FORMAT_R32G32_SFLOAT; + alias_vertex_input_attribute_descriptions[0].location = 0; + alias_vertex_input_attribute_descriptions[0].offset = 0; + alias_vertex_input_attribute_descriptions[1].binding = 1; + alias_vertex_input_attribute_descriptions[1].format = VK_FORMAT_R8G8B8A8_UNORM; + alias_vertex_input_attribute_descriptions[1].location = 1; + alias_vertex_input_attribute_descriptions[1].offset = 0; + alias_vertex_input_attribute_descriptions[2].binding = 1; + alias_vertex_input_attribute_descriptions[2].format = VK_FORMAT_R8G8B8A8_SNORM; + alias_vertex_input_attribute_descriptions[2].location = 2; + alias_vertex_input_attribute_descriptions[2].offset = 4; + alias_vertex_input_attribute_descriptions[3].binding = 2; + alias_vertex_input_attribute_descriptions[3].format = VK_FORMAT_R8G8B8A8_UNORM; + alias_vertex_input_attribute_descriptions[3].location = 3; + alias_vertex_input_attribute_descriptions[3].offset = 0; + alias_vertex_input_attribute_descriptions[4].binding = 2; + alias_vertex_input_attribute_descriptions[4].format = VK_FORMAT_R8G8B8A8_SNORM; + alias_vertex_input_attribute_descriptions[4].location = 4; + alias_vertex_input_attribute_descriptions[4].offset = 4; + + alias_vertex_binding_descriptions[0].binding = 0; + alias_vertex_binding_descriptions[0].inputRate = VK_VERTEX_INPUT_RATE_VERTEX; + alias_vertex_binding_descriptions[0].stride = 8; + alias_vertex_binding_descriptions[1].binding = 1; + alias_vertex_binding_descriptions[1].inputRate = VK_VERTEX_INPUT_RATE_VERTEX; + alias_vertex_binding_descriptions[1].stride = 8; + alias_vertex_binding_descriptions[2].binding = 2; + alias_vertex_binding_descriptions[2].inputRate = VK_VERTEX_INPUT_RATE_VERTEX; + alias_vertex_binding_descriptions[2].stride = 8; + } + + { + // Matches md5vert_t + md5_vertex_input_attribute_descriptions[0].binding = 0; + md5_vertex_input_attribute_descriptions[0].format = VK_FORMAT_R32G32B32_SFLOAT; + md5_vertex_input_attribute_descriptions[0].location = 0; + md5_vertex_input_attribute_descriptions[0].offset = 0; + md5_vertex_input_attribute_descriptions[1].binding = 0; + md5_vertex_input_attribute_descriptions[1].format = VK_FORMAT_R32G32B32_SFLOAT; + md5_vertex_input_attribute_descriptions[1].location = 1; + md5_vertex_input_attribute_descriptions[1].offset = 12; + md5_vertex_input_attribute_descriptions[2].binding = 0; + md5_vertex_input_attribute_descriptions[2].format = VK_FORMAT_R32G32_SFLOAT; + md5_vertex_input_attribute_descriptions[2].location = 2; + md5_vertex_input_attribute_descriptions[2].offset = 24; + md5_vertex_input_attribute_descriptions[3].binding = 0; + md5_vertex_input_attribute_descriptions[3].format = VK_FORMAT_R8G8B8A8_UNORM; + md5_vertex_input_attribute_descriptions[3].location = 3; + md5_vertex_input_attribute_descriptions[3].offset = 32; + md5_vertex_input_attribute_descriptions[4].binding = 0; + md5_vertex_input_attribute_descriptions[4].format = VK_FORMAT_R8G8B8A8_UINT; + md5_vertex_input_attribute_descriptions[4].location = 4; + md5_vertex_input_attribute_descriptions[4].offset = 36; + + md5_vertex_binding_description.binding = 0; + md5_vertex_binding_description.inputRate = VK_VERTEX_INPUT_RATE_VERTEX; + md5_vertex_binding_description.stride = 40; + } } /* @@ -2855,43 +2969,42 @@ static void R_CreateAliasPipelines () infos.shader_stages[0].module = alias_vert_module; infos.shader_stages[1].module = alias_frag_module; - infos.graphics_pipeline.layout = vulkan_globals.alias_pipeline.layout.handle; + infos.graphics_pipeline.layout = vulkan_globals.alias_pipelines[0].layout.handle; - assert (vulkan_globals.alias_pipeline.handle == VK_NULL_HANDLE); - err = vkCreateGraphicsPipelines (vulkan_globals.device, VK_NULL_HANDLE, 1, &infos.graphics_pipeline, NULL, &vulkan_globals.alias_pipeline.handle); + assert (vulkan_globals.alias_pipelines[0].handle == VK_NULL_HANDLE); + err = vkCreateGraphicsPipelines (vulkan_globals.device, VK_NULL_HANDLE, 1, &infos.graphics_pipeline, NULL, &vulkan_globals.alias_pipelines[0].handle); if (err != VK_SUCCESS) Sys_Error ("vkCreateGraphicsPipelines failed (alias_pipeline)"); - GL_SetObjectName ((uint64_t)vulkan_globals.alias_pipeline.handle, VK_OBJECT_TYPE_PIPELINE, "alias"); + GL_SetObjectName ((uint64_t)vulkan_globals.alias_pipelines[0].handle, VK_OBJECT_TYPE_PIPELINE, "alias"); infos.shader_stages[1].module = alias_alphatest_frag_module; - assert (vulkan_globals.alias_alphatest_pipeline.handle == VK_NULL_HANDLE); - err = vkCreateGraphicsPipelines (vulkan_globals.device, VK_NULL_HANDLE, 1, &infos.graphics_pipeline, NULL, &vulkan_globals.alias_alphatest_pipeline.handle); + assert (vulkan_globals.alias_pipelines[1].handle == VK_NULL_HANDLE); + err = vkCreateGraphicsPipelines (vulkan_globals.device, VK_NULL_HANDLE, 1, &infos.graphics_pipeline, NULL, &vulkan_globals.alias_pipelines[1].handle); if (err != VK_SUCCESS) Sys_Error ("vkCreateGraphicsPipelines failed (alias_alphatest_pipeline)"); - GL_SetObjectName ((uint64_t)vulkan_globals.alias_alphatest_pipeline.handle, VK_OBJECT_TYPE_PIPELINE, "alias_alphatest"); - vulkan_globals.alias_alphatest_pipeline.layout = vulkan_globals.alias_pipeline.layout; + GL_SetObjectName ((uint64_t)vulkan_globals.alias_pipelines[1].handle, VK_OBJECT_TYPE_PIPELINE, "alias_alphatest"); + vulkan_globals.alias_pipelines[1].layout = vulkan_globals.alias_pipelines[0].layout; infos.depth_stencil_state.depthWriteEnable = VK_FALSE; infos.blend_attachment_state.blendEnable = VK_TRUE; infos.shader_stages[1].module = alias_frag_module; - assert (vulkan_globals.alias_blend_pipeline.handle == VK_NULL_HANDLE); - err = vkCreateGraphicsPipelines (vulkan_globals.device, VK_NULL_HANDLE, 1, &infos.graphics_pipeline, NULL, &vulkan_globals.alias_blend_pipeline.handle); + assert (vulkan_globals.alias_pipelines[2].handle == VK_NULL_HANDLE); + err = vkCreateGraphicsPipelines (vulkan_globals.device, VK_NULL_HANDLE, 1, &infos.graphics_pipeline, NULL, &vulkan_globals.alias_pipelines[2].handle); if (err != VK_SUCCESS) Sys_Error ("vkCreateGraphicsPipelines failed (alias_blend_pipeline)"); - GL_SetObjectName ((uint64_t)vulkan_globals.alias_blend_pipeline.handle, VK_OBJECT_TYPE_PIPELINE, "alias_blend"); - vulkan_globals.alias_blend_pipeline.layout = vulkan_globals.alias_pipeline.layout; + GL_SetObjectName ((uint64_t)vulkan_globals.alias_pipelines[2].handle, VK_OBJECT_TYPE_PIPELINE, "alias_blend"); + vulkan_globals.alias_pipelines[2].layout = vulkan_globals.alias_pipelines[0].layout; infos.shader_stages[1].module = alias_alphatest_frag_module; - assert (vulkan_globals.alias_alphatest_blend_pipeline.handle == VK_NULL_HANDLE); - err = vkCreateGraphicsPipelines ( - vulkan_globals.device, VK_NULL_HANDLE, 1, &infos.graphics_pipeline, NULL, &vulkan_globals.alias_alphatest_blend_pipeline.handle); + assert (vulkan_globals.alias_pipelines[3].handle == VK_NULL_HANDLE); + err = vkCreateGraphicsPipelines (vulkan_globals.device, VK_NULL_HANDLE, 1, &infos.graphics_pipeline, NULL, &vulkan_globals.alias_pipelines[3].handle); if (err != VK_SUCCESS) Sys_Error ("vkCreateGraphicsPipelines failed"); - GL_SetObjectName ((uint64_t)vulkan_globals.alias_alphatest_blend_pipeline.handle, VK_OBJECT_TYPE_PIPELINE, "alias_alphatest_blend"); - vulkan_globals.alias_alphatest_blend_pipeline.layout = vulkan_globals.alias_pipeline.layout; + GL_SetObjectName ((uint64_t)vulkan_globals.alias_pipelines[3].handle, VK_OBJECT_TYPE_PIPELINE, "alias_alphatest_blend"); + vulkan_globals.alias_pipelines[3].layout = vulkan_globals.alias_pipelines[0].layout; if (vulkan_globals.non_solid_fill) { @@ -2905,28 +3018,123 @@ static void R_CreateAliasPipelines () infos.shader_stages[0].module = alias_vert_module; infos.shader_stages[1].module = showtris_frag_module; - infos.graphics_pipeline.layout = vulkan_globals.alias_pipeline.layout.handle; + infos.graphics_pipeline.layout = vulkan_globals.alias_pipelines[0].layout.handle; - assert (vulkan_globals.alias_showtris_pipeline.handle == VK_NULL_HANDLE); - err = vkCreateGraphicsPipelines ( - vulkan_globals.device, VK_NULL_HANDLE, 1, &infos.graphics_pipeline, NULL, &vulkan_globals.alias_showtris_pipeline.handle); + assert (vulkan_globals.alias_pipelines[4].handle == VK_NULL_HANDLE); + err = vkCreateGraphicsPipelines (vulkan_globals.device, VK_NULL_HANDLE, 1, &infos.graphics_pipeline, NULL, &vulkan_globals.alias_pipelines[4].handle); if (err != VK_SUCCESS) Sys_Error ("vkCreateGraphicsPipelines failed"); - GL_SetObjectName ((uint64_t)vulkan_globals.alias_showtris_pipeline.handle, VK_OBJECT_TYPE_PIPELINE, "alias_showtris"); - vulkan_globals.alias_showtris_pipeline.layout = vulkan_globals.alias_pipeline.layout; + GL_SetObjectName ((uint64_t)vulkan_globals.alias_pipelines[4].handle, VK_OBJECT_TYPE_PIPELINE, "alias_showtris"); + vulkan_globals.alias_pipelines[4].layout = vulkan_globals.alias_pipelines[0].layout; infos.depth_stencil_state.depthTestEnable = VK_TRUE; infos.rasterization_state.depthBiasEnable = VK_TRUE; infos.rasterization_state.depthBiasConstantFactor = 500.0f; infos.rasterization_state.depthBiasSlopeFactor = 0.0f; - assert (vulkan_globals.alias_showtris_depth_test_pipeline.handle == VK_NULL_HANDLE); - err = vkCreateGraphicsPipelines ( - vulkan_globals.device, VK_NULL_HANDLE, 1, &infos.graphics_pipeline, NULL, &vulkan_globals.alias_showtris_depth_test_pipeline.handle); + assert (vulkan_globals.alias_pipelines[5].handle == VK_NULL_HANDLE); + err = vkCreateGraphicsPipelines (vulkan_globals.device, VK_NULL_HANDLE, 1, &infos.graphics_pipeline, NULL, &vulkan_globals.alias_pipelines[5].handle); + if (err != VK_SUCCESS) + Sys_Error ("vkCreateGraphicsPipelines failed"); + GL_SetObjectName ((uint64_t)vulkan_globals.alias_pipelines[5].handle, VK_OBJECT_TYPE_PIPELINE, "alias_showtris_depth_test"); + vulkan_globals.alias_pipelines[5].layout = vulkan_globals.alias_pipelines[0].layout; + } +} + +/* +=============== +R_CreateMD5Pipelines +=============== +*/ +static void R_CreateMD5Pipelines () +{ + VkResult err; + pipeline_create_infos_t infos; + R_InitDefaultStates (&infos); + + infos.depth_stencil_state.depthTestEnable = VK_TRUE; + infos.depth_stencil_state.depthWriteEnable = VK_TRUE; + infos.rasterization_state.depthBiasEnable = VK_FALSE; + infos.blend_attachment_state.blendEnable = VK_FALSE; + infos.shader_stages[1].pSpecializationInfo = NULL; + + infos.vertex_input_state.vertexAttributeDescriptionCount = 5; + infos.vertex_input_state.pVertexAttributeDescriptions = md5_vertex_input_attribute_descriptions; + infos.vertex_input_state.vertexBindingDescriptionCount = 1; + infos.vertex_input_state.pVertexBindingDescriptions = &md5_vertex_binding_description; + + infos.shader_stages[0].module = md5_vert_module; + infos.shader_stages[1].module = alias_frag_module; + + infos.graphics_pipeline.layout = vulkan_globals.md5_pipelines[0].layout.handle; + + assert (vulkan_globals.md5_pipelines[0].handle == VK_NULL_HANDLE); + err = vkCreateGraphicsPipelines (vulkan_globals.device, VK_NULL_HANDLE, 1, &infos.graphics_pipeline, NULL, &vulkan_globals.md5_pipelines[0].handle); + if (err != VK_SUCCESS) + Sys_Error ("vkCreateGraphicsPipelines failed (md5_pipeline)"); + GL_SetObjectName ((uint64_t)vulkan_globals.md5_pipelines[0].handle, VK_OBJECT_TYPE_PIPELINE, "md5"); + + infos.shader_stages[1].module = alias_alphatest_frag_module; + + assert (vulkan_globals.md5_pipelines[1].handle == VK_NULL_HANDLE); + err = vkCreateGraphicsPipelines (vulkan_globals.device, VK_NULL_HANDLE, 1, &infos.graphics_pipeline, NULL, &vulkan_globals.md5_pipelines[1].handle); + if (err != VK_SUCCESS) + Sys_Error ("vkCreateGraphicsPipelines failed (md5_alphatest_pipeline)"); + GL_SetObjectName ((uint64_t)vulkan_globals.md5_pipelines[1].handle, VK_OBJECT_TYPE_PIPELINE, "md5_alphatest"); + vulkan_globals.md5_pipelines[1].layout = vulkan_globals.md5_pipelines[0].layout; + + infos.depth_stencil_state.depthWriteEnable = VK_FALSE; + infos.blend_attachment_state.blendEnable = VK_TRUE; + infos.shader_stages[1].module = alias_frag_module; + + assert (vulkan_globals.md5_pipelines[2].handle == VK_NULL_HANDLE); + err = vkCreateGraphicsPipelines (vulkan_globals.device, VK_NULL_HANDLE, 1, &infos.graphics_pipeline, NULL, &vulkan_globals.md5_pipelines[2].handle); + if (err != VK_SUCCESS) + Sys_Error ("vkCreateGraphicsPipelines failed (md5_blend_pipeline)"); + GL_SetObjectName ((uint64_t)vulkan_globals.md5_pipelines[2].handle, VK_OBJECT_TYPE_PIPELINE, "md5_blend"); + vulkan_globals.md5_pipelines[2].layout = vulkan_globals.md5_pipelines[0].layout; + + infos.shader_stages[1].module = alias_alphatest_frag_module; + + assert (vulkan_globals.md5_pipelines[3].handle == VK_NULL_HANDLE); + err = vkCreateGraphicsPipelines (vulkan_globals.device, VK_NULL_HANDLE, 1, &infos.graphics_pipeline, NULL, &vulkan_globals.md5_pipelines[3].handle); + if (err != VK_SUCCESS) + Sys_Error ("vkCreateGraphicsPipelines failed"); + GL_SetObjectName ((uint64_t)vulkan_globals.md5_pipelines[3].handle, VK_OBJECT_TYPE_PIPELINE, "md5_alphatest_blend"); + vulkan_globals.md5_pipelines[3].layout = vulkan_globals.md5_pipelines[0].layout; + + if (vulkan_globals.non_solid_fill) + { + infos.rasterization_state.cullMode = VK_CULL_MODE_NONE; + infos.rasterization_state.polygonMode = VK_POLYGON_MODE_LINE; + infos.depth_stencil_state.depthTestEnable = VK_FALSE; + infos.depth_stencil_state.depthWriteEnable = VK_FALSE; + infos.blend_attachment_state.blendEnable = VK_FALSE; + infos.input_assembly_state.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; + + infos.shader_stages[0].module = md5_vert_module; + infos.shader_stages[1].module = showtris_frag_module; + + infos.graphics_pipeline.layout = vulkan_globals.md5_pipelines[0].layout.handle; + + assert (vulkan_globals.md5_pipelines[4].handle == VK_NULL_HANDLE); + err = vkCreateGraphicsPipelines (vulkan_globals.device, VK_NULL_HANDLE, 1, &infos.graphics_pipeline, NULL, &vulkan_globals.md5_pipelines[4].handle); if (err != VK_SUCCESS) Sys_Error ("vkCreateGraphicsPipelines failed"); - GL_SetObjectName ((uint64_t)vulkan_globals.alias_showtris_depth_test_pipeline.handle, VK_OBJECT_TYPE_PIPELINE, "alias_showtris_depth_test"); - vulkan_globals.alias_showtris_depth_test_pipeline.layout = vulkan_globals.alias_pipeline.layout; + GL_SetObjectName ((uint64_t)vulkan_globals.md5_pipelines[4].handle, VK_OBJECT_TYPE_PIPELINE, "md5_showtris"); + vulkan_globals.md5_pipelines[4].layout = vulkan_globals.md5_pipelines[0].layout; + + infos.depth_stencil_state.depthTestEnable = VK_TRUE; + infos.rasterization_state.depthBiasEnable = VK_TRUE; + infos.rasterization_state.depthBiasConstantFactor = 500.0f; + infos.rasterization_state.depthBiasSlopeFactor = 0.0f; + + assert (vulkan_globals.md5_pipelines[5].handle == VK_NULL_HANDLE); + err = vkCreateGraphicsPipelines (vulkan_globals.device, VK_NULL_HANDLE, 1, &infos.graphics_pipeline, NULL, &vulkan_globals.md5_pipelines[5].handle); + if (err != VK_SUCCESS) + Sys_Error ("vkCreateGraphicsPipelines failed"); + GL_SetObjectName ((uint64_t)vulkan_globals.md5_pipelines[5].handle, VK_OBJECT_TYPE_PIPELINE, "md5_showtris_depth_test"); + vulkan_globals.md5_pipelines[5].layout = vulkan_globals.md5_pipelines[0].layout; } } @@ -3132,6 +3340,7 @@ static void R_CreateShaderModules () CREATE_SHADER_MODULE (alias_vert); CREATE_SHADER_MODULE (alias_frag); CREATE_SHADER_MODULE (alias_alphatest_frag); + CREATE_SHADER_MODULE (md5_vert); CREATE_SHADER_MODULE (sky_layer_vert); CREATE_SHADER_MODULE (sky_layer_frag); CREATE_SHADER_MODULE (sky_box_frag); @@ -3173,6 +3382,7 @@ static void R_DestroyShaderModules () DESTROY_SHADER_MODULE (alias_vert); DESTROY_SHADER_MODULE (alias_frag); DESTROY_SHADER_MODULE (alias_alphatest_frag); + DESTROY_SHADER_MODULE (md5_vert); DESTROY_SHADER_MODULE (sky_layer_vert); DESTROY_SHADER_MODULE (sky_layer_frag); DESTROY_SHADER_MODULE (sky_box_frag); @@ -3217,6 +3427,7 @@ void R_CreatePipelines () R_CreateShowTrisPipelines (); R_CreateWorldPipelines (); R_CreateAliasPipelines (); + R_CreateMD5Pipelines (); R_CreatePostprocessPipelines (); R_CreateScreenEffectsPipelines (); R_CreateUpdateLightmapPipelines (); @@ -3280,15 +3491,19 @@ void R_DestroyPipelines (void) } vkDestroyPipeline (vulkan_globals.device, vulkan_globals.sky_box_pipeline.handle, NULL); vulkan_globals.sky_box_pipeline.handle = VK_NULL_HANDLE; - - vkDestroyPipeline (vulkan_globals.device, vulkan_globals.alias_pipeline.handle, NULL); - vulkan_globals.alias_pipeline.handle = VK_NULL_HANDLE; - vkDestroyPipeline (vulkan_globals.device, vulkan_globals.alias_alphatest_pipeline.handle, NULL); - vulkan_globals.alias_alphatest_pipeline.handle = VK_NULL_HANDLE; - vkDestroyPipeline (vulkan_globals.device, vulkan_globals.alias_alphatest_blend_pipeline.handle, NULL); - vulkan_globals.alias_alphatest_blend_pipeline.handle = VK_NULL_HANDLE; - vkDestroyPipeline (vulkan_globals.device, vulkan_globals.alias_blend_pipeline.handle, NULL); - vulkan_globals.alias_blend_pipeline.handle = VK_NULL_HANDLE; + for (i = 0; i < MODEL_PIPELINE_COUNT; ++i) + { + if (vulkan_globals.alias_pipelines[i].handle != VK_NULL_HANDLE) + { + vkDestroyPipeline (vulkan_globals.device, vulkan_globals.alias_pipelines[i].handle, NULL); + vulkan_globals.alias_pipelines[i].handle = VK_NULL_HANDLE; + } + if (vulkan_globals.md5_pipelines[i].handle != VK_NULL_HANDLE) + { + vkDestroyPipeline (vulkan_globals.device, vulkan_globals.md5_pipelines[i].handle, NULL); + vulkan_globals.md5_pipelines[i].handle = VK_NULL_HANDLE; + } + } vkDestroyPipeline (vulkan_globals.device, vulkan_globals.postprocess_pipeline.handle, NULL); vulkan_globals.postprocess_pipeline.handle = VK_NULL_HANDLE; vkDestroyPipeline (vulkan_globals.device, vulkan_globals.screen_effects_pipeline.handle, NULL); @@ -3315,13 +3530,6 @@ void R_DestroyPipelines (void) vkDestroyPipeline (vulkan_globals.device, vulkan_globals.showbboxes_pipeline.handle, NULL); vulkan_globals.showbboxes_pipeline.handle = VK_NULL_HANDLE; } - if (vulkan_globals.alias_showtris_pipeline.handle != VK_NULL_HANDLE) - { - vkDestroyPipeline (vulkan_globals.device, vulkan_globals.alias_showtris_pipeline.handle, NULL); - vulkan_globals.alias_showtris_pipeline.handle = VK_NULL_HANDLE; - vkDestroyPipeline (vulkan_globals.device, vulkan_globals.alias_showtris_depth_test_pipeline.handle, NULL); - vulkan_globals.alias_showtris_depth_test_pipeline.handle = VK_NULL_HANDLE; - } vkDestroyPipeline (vulkan_globals.device, vulkan_globals.update_lightmap_pipeline.handle, NULL); vulkan_globals.update_lightmap_pipeline.handle = VK_NULL_HANDLE; if (vulkan_globals.update_lightmap_rt_pipeline.handle != VK_NULL_HANDLE) @@ -3524,7 +3732,6 @@ void R_TranslateNewPlayerSkin (int playernum) return; paliashdr = (aliashdr_t *)Mod_Extradata (currententity->model); - skinnum = currententity->skinnum; // TODO: move these tests to the place where skinnum gets received from the server @@ -3535,6 +3742,8 @@ void R_TranslateNewPlayerSkin (int playernum) } pixels = (byte *)paliashdr->texels[skinnum]; + if (!pixels) + return; // upload new image q_snprintf (name, sizeof (name), "player_%i", playernum); diff --git a/Quake/gl_sky.c b/Quake/gl_sky.c index 6098d1252..1b6140d59 100644 --- a/Quake/gl_sky.c +++ b/Quake/gl_sky.c @@ -232,10 +232,11 @@ Sky_LoadSkyBox const char *suf[6] = {"rt", "bk", "lf", "ft", "up", "dn"}; void Sky_LoadSkyBox (const char *name) { - int i, width[6], height[6]; - char filename[6][MAX_OSPATH]; - byte *data[6]; - qboolean nonefound = true, cubemap = true; + int i, width[6], height[6]; + enum srcformat fmt[6]; + char filename[6][MAX_OSPATH]; + byte *data[6]; + qboolean nonefound = true, cubemap = true; if (strcmp (skybox_name, name) == 0) return; // no change @@ -262,9 +263,9 @@ void Sky_LoadSkyBox (const char *name) for (i = 0; i < 6; i++) { q_snprintf (filename[i], sizeof (filename[i]), "gfx/env/%s%s", name, suf[i]); - if ((data[i] = Image_LoadImage (filename[i], &width[i], &height[i]))) + if ((data[i] = Image_LoadImage (filename[i], &width[i], &height[i], &fmt[i]))) nonefound = false; - if (!data[i] || width[i] != height[i] || width[i] != width[0]) + if (!data[i] || (width[i] != height[i]) || (width[i] != width[0]) || (fmt[i] != SRC_RGBA)) cubemap = false; } @@ -277,7 +278,7 @@ void Sky_LoadSkyBox (const char *name) for (i = 0; i < 6; i++) { if (data[i]) - skybox_textures[i] = TexMgr_LoadImage (cl.worldmodel, filename[i], width[i], height[i], SRC_RGBA, data[i], filename[i], 0, TEXPREF_NONE); + skybox_textures[i] = TexMgr_LoadImage (cl.worldmodel, filename[i], width[i], height[i], fmt[i], data[i], filename[i], 0, TEXPREF_NONE); else { Con_Printf ("Couldn't load %s\n", filename[i]); diff --git a/Quake/gl_texmgr.c b/Quake/gl_texmgr.c index 30b717ee6..7de43a2ab 100644 --- a/Quake/gl_texmgr.c +++ b/Quake/gl_texmgr.c @@ -1353,7 +1353,7 @@ void TexMgr_ReloadImage (gltexture_t *glt, int shirt, int pants) } else if (glt->source_file[0] && !glt->source_offset) { - allocated = data = Image_LoadImage (glt->source_file, (int *)&glt->source_width, (int *)&glt->source_height); // simple file + allocated = data = Image_LoadImage (glt->source_file, (int *)&glt->source_width, (int *)&glt->source_height, &glt->source_format); // simple file } else if (!glt->source_file[0] && glt->source_offset) { diff --git a/Quake/gl_vidsdl.c b/Quake/gl_vidsdl.c index bdf4aaf86..e61414222 100644 --- a/Quake/gl_vidsdl.c +++ b/Quake/gl_vidsdl.c @@ -3286,6 +3286,7 @@ void VID_Init (void) R_CreateDescriptorSetLayouts (); R_CreateDescriptorPool (); R_InitGPUBuffers (); + R_InitMeshHeapMemoryIndex (); R_InitSamplers (); R_CreatePipelineLayouts (); R_CreatePaletteOctreeBuffers (palette_octree_colors, NUM_PALETTE_OCTREE_COLORS, palette_octree_nodes, NUM_PALETTE_OCTREE_NODES); diff --git a/Quake/glquake.h b/Quake/glquake.h index 83eb5f170..fe0ac6d22 100644 --- a/Quake/glquake.h +++ b/Quake/glquake.h @@ -172,6 +172,7 @@ typedef struct vulkan_memory_s } vulkan_memory_t; #define WORLD_PIPELINE_COUNT 16 +#define MODEL_PIPELINE_COUNT 6 #define FTE_PARTICLE_PIPELINE_COUNT 16 #define MAX_BATCH_SIZE 65536 #define NUM_WORLD_CBX 6 @@ -276,10 +277,8 @@ typedef struct vulkan_pipeline_t sky_box_pipeline; vulkan_pipeline_t sky_cube_pipeline[2]; vulkan_pipeline_t sky_layer_pipeline[2]; - vulkan_pipeline_t alias_pipeline; - vulkan_pipeline_t alias_blend_pipeline; - vulkan_pipeline_t alias_alphatest_pipeline; - vulkan_pipeline_t alias_alphatest_blend_pipeline; + vulkan_pipeline_t alias_pipelines[MODEL_PIPELINE_COUNT]; + vulkan_pipeline_t md5_pipelines[MODEL_PIPELINE_COUNT]; vulkan_pipeline_t postprocess_pipeline; vulkan_pipeline_t screen_effects_pipeline; vulkan_pipeline_t screen_effects_scale_pipeline; @@ -290,8 +289,6 @@ typedef struct vulkan_pipeline_t showtris_depth_test_pipeline; vulkan_pipeline_t showtris_indirect_depth_test_pipeline; vulkan_pipeline_t showbboxes_pipeline; - vulkan_pipeline_t alias_showtris_pipeline; - vulkan_pipeline_t alias_showtris_depth_test_pipeline; vulkan_pipeline_t update_lightmap_pipeline; vulkan_pipeline_t update_lightmap_rt_pipeline; vulkan_pipeline_t indirect_draw_pipeline; @@ -315,6 +312,7 @@ typedef struct vulkan_desc_set_layout_t lightmap_compute_rt_set_layout; VkDescriptorSet ray_debug_desc_set; vulkan_desc_set_layout_t ray_debug_set_layout; + vulkan_desc_set_layout_t joints_buffer_set_layout; // Samplers VkSampler point_sampler; @@ -566,7 +564,8 @@ void GL_DeleteBModelAccelerationStructures (void); void GL_BuildBModelVertexBuffer (void); void GL_BuildBModelAccelerationStructures (void); void GL_PrepareSIMDAndParallelData (void); -void GLMesh_DeleteVertexBuffers (void); +void GLMesh_UploadBuffers (qmodel_t *m, aliashdr_t *hdr, unsigned short *indexes, byte *vertexes, aliasmesh_t *desc, jointpose_t *joints); +void GLMesh_DeleteAllMeshBuffers (void); int R_LightPoint (vec3_t p, float ofs, lightcache_t *cache, vec3_t *lightcolor); @@ -686,8 +685,10 @@ void R_SubmitStagingBuffers (); byte *R_StagingAllocate (int size, int alignment, VkCommandBuffer *cb_context, VkBuffer *buffer, int *buffer_offset); void R_StagingBeginCopy (); void R_StagingEndCopy (); +void R_StagingUploadBuffer (const VkBuffer buffer, const size_t size, const byte *data); void R_InitGPUBuffers (); +void R_InitMeshHeapMemoryIndex (); void R_SwapDynamicBuffers (); void R_FlushDynamicBuffers (); void R_CollectDynamicBufferGarbage (); diff --git a/Quake/image.c b/Quake/image.c index 1697ca54e..b88f4fdc5 100644 --- a/Quake/image.c +++ b/Quake/image.c @@ -95,19 +95,33 @@ returns a pointer to hunk allocated RGBA data TODO: search order: tga png jpg pcx lmp ============ */ -byte *Image_LoadImage (const char *name, int *width, int *height) +byte *Image_LoadImage (const char *name, int *width, int *height, enum srcformat *fmt) { FILE *f; q_snprintf (loadfilename, sizeof (loadfilename), "%s.tga", name); COM_FOpenFile (loadfilename, &f, NULL); if (f) + { + *fmt = SRC_RGBA; return Image_LoadTGA (f, width, height, name); + } q_snprintf (loadfilename, sizeof (loadfilename), "%s.pcx", name); COM_FOpenFile (loadfilename, &f, NULL); if (f) + { + *fmt = SRC_RGBA; return Image_LoadPCX (f, width, height); + } + + q_snprintf (loadfilename, sizeof (loadfilename), "%s%s.lmp", "", name); + COM_FOpenFile (loadfilename, &f, NULL); + if (f) + { + *fmt = SRC_INDEXED; + return Image_LoadLMP (f, width, height); + } return NULL; } @@ -535,6 +549,52 @@ byte *Image_LoadPCX (FILE *f, int *width, int *height) return data; } +//============================================================================== +// +// QPIC (aka '.lmp') +// +//============================================================================== + +typedef struct +{ + unsigned int width, height; +} lmpheader_t; + +/* +============ +Image_LoadLMP +============ +*/ +byte *Image_LoadLMP (FILE *f, int *width, int *height) +{ + lmpheader_t qpic; + size_t pix; + void *data; + + if (fread (&qpic, 1, sizeof (qpic), f) != sizeof (qpic)) + Sys_Error ("'%s' is not a valid LMP file", loadfilename); + + qpic.width = LittleLong (qpic.width); + qpic.height = LittleLong (qpic.height); + + pix = qpic.width * qpic.height; + + if (com_filesize != 8 + pix) + { + fclose (f); + return NULL; + } + + data = (byte *)Mem_Alloc (pix); //+1 to allow reading padding byte on last line + if (fread (data, 1, pix, f) != pix) + Sys_Error ("'%s' is not a valid LMP file", loadfilename); + fclose (f); + + *width = qpic.width; + *height = qpic.height; + return data; +} + //============================================================================== // // STB_IMAGE_WRITE diff --git a/Quake/image.h b/Quake/image.h index 37d0f58c5..983367611 100644 --- a/Quake/image.h +++ b/Quake/image.h @@ -24,11 +24,13 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #define GL_IMAGE_H // image.h -- image reading / writing +enum srcformat; // be sure to free the hunk after using these loading functions byte *Image_LoadTGA (FILE *f, int *width, int *height, const char *name); byte *Image_LoadPCX (FILE *f, int *width, int *height); -byte *Image_LoadImage (const char *name, int *width, int *height); +byte *Image_LoadLMP (FILE *f, int *width, int *height); +byte *Image_LoadImage (const char *name, int *width, int *height, enum srcformat *fmt); qboolean Image_WriteTGA (const char *name, byte *data, int width, int height, int bpp, qboolean upsidedown); qboolean Image_WritePNG (const char *name, byte *data, int width, int height, int bpp, qboolean upsidedown); diff --git a/Quake/r_alias.c b/Quake/r_alias.c index 0d211b5c4..7d6a2be83 100644 --- a/Quake/r_alias.c +++ b/Quake/r_alias.c @@ -53,14 +53,25 @@ typedef struct typedef struct { - float model_matrix[16]; - float shade_vector[3]; - float blend_factor; - float light_color[3]; - float entalpha; - unsigned int flags; + float model_matrix[16]; + float shade_vector[3]; + float blend_factor; + float light_color[3]; + float entalpha; + uint32_t flags; } aliasubo_t; +typedef struct +{ + float model_matrix[16]; + float shade_vector[3]; + float blend_factor; + float light_color[3]; + float entalpha; + uint32_t flags; + uint32_t joints_offsets[2]; +} md5ubo_t; + /* ============= GLARB_GetXYZOffset @@ -72,7 +83,7 @@ model and pose. static VkDeviceSize GLARB_GetXYZOffset (entity_t *e, aliashdr_t *hdr, int pose) { const int xyzoffs = offsetof (meshxyz_t, xyz); - return e->model->vboxyzofs + (hdr->numverts_vbo * pose * sizeof (meshxyz_t)) + xyzoffs; + return hdr->numverts_vbo * pose * sizeof (meshxyz_t) + xyzoffs; } /* @@ -90,8 +101,24 @@ Based on code by MH from RMQEngine */ static void GL_DrawAliasFrame ( cb_context_t *cbx, entity_t *e, aliashdr_t *paliashdr, lerpdata_t lerpdata, gltexture_t *tx, gltexture_t *fb, float model_matrix[16], float entity_alpha, - qboolean alphatest, vec3_t shadevector, vec3_t lightcolor) + qboolean alphatest, vec3_t shadevector, vec3_t lightcolor, int showtris) { + qmodel_t *m = e->model; + + vulkan_pipeline_t pipeline; + const int pipeline_index = (showtris == 0) ? (((entity_alpha >= 1.0f) ? 0 : 2) + (alphatest ? 1 : 0)) : (3 + CLAMP (1, showtris, 2)); + switch (paliashdr->poseverttype) + { + case PV_MD5: + pipeline = vulkan_globals.md5_pipelines[pipeline_index]; + break; + default: + pipeline = vulkan_globals.alias_pipelines[pipeline_index]; + break; + } + + R_BindPipeline (cbx, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); + float blend; if (lerpdata.pose1 != lerpdata.pose2) @@ -99,49 +126,68 @@ static void GL_DrawAliasFrame ( else // poses the same means either 1. the entity has paused its animation, or 2. r_lerpmodels is disabled blend = 0; - vulkan_pipeline_t pipeline; - if (entity_alpha >= 1.0f) + switch (paliashdr->poseverttype) { - if (!alphatest) - pipeline = vulkan_globals.alias_pipeline; - else - pipeline = vulkan_globals.alias_alphatest_pipeline; + case PV_QUAKE1: + { + VkBuffer uniform_buffer; + uint32_t uniform_offset; + VkDescriptorSet ubo_set; + aliasubo_t *ubo = (aliasubo_t *)R_UniformAllocate (sizeof (aliasubo_t), &uniform_buffer, &uniform_offset, &ubo_set); + + memcpy (ubo->model_matrix, model_matrix, 16 * sizeof (float)); + memcpy (ubo->shade_vector, shadevector, 3 * sizeof (float)); + ubo->blend_factor = blend; + memcpy (ubo->light_color, lightcolor, 3 * sizeof (float)); + ubo->flags = (fb != NULL) ? 0x1 : 0x0; + if (r_fullbright_cheatsafe || (r_lightmap_cheatsafe && r_fullbright.value)) + ubo->flags |= 0x2; + ubo->entalpha = entity_alpha; + + VkDescriptorSet descriptor_sets[3] = {tx->descriptor_set, (fb != NULL) ? fb->descriptor_set : tx->descriptor_set, ubo_set}; + vulkan_globals.vk_cmd_bind_descriptor_sets ( + cbx->cb, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline.layout.handle, 0, 3, descriptor_sets, 1, &uniform_offset); + + VkBuffer vertex_buffers[3] = {m->vertex_buffer, m->vertex_buffer, m->vertex_buffer}; + VkDeviceSize vertex_offsets[3] = { + (unsigned)m->vbostofs, GLARB_GetXYZOffset (e, paliashdr, lerpdata.pose1), GLARB_GetXYZOffset (e, paliashdr, lerpdata.pose2)}; + vulkan_globals.vk_cmd_bind_vertex_buffers (cbx->cb, 0, 3, vertex_buffers, vertex_offsets); + vulkan_globals.vk_cmd_bind_index_buffer (cbx->cb, m->index_buffer, 0, VK_INDEX_TYPE_UINT16); + + vulkan_globals.vk_cmd_draw_indexed (cbx->cb, paliashdr->numindexes, 1, 0, 0, 0); + break; } - else + case PV_MD5: { - if (!alphatest) - pipeline = vulkan_globals.alias_blend_pipeline; - else - pipeline = vulkan_globals.alias_alphatest_blend_pipeline; + VkBuffer uniform_buffer; + uint32_t uniform_offset; + VkDescriptorSet ubo_set; + md5ubo_t *ubo = (md5ubo_t *)R_UniformAllocate (sizeof (md5ubo_t), &uniform_buffer, &uniform_offset, &ubo_set); + + memcpy (ubo->model_matrix, model_matrix, 16 * sizeof (float)); + memcpy (ubo->shade_vector, shadevector, 3 * sizeof (float)); + ubo->blend_factor = blend; + memcpy (ubo->light_color, lightcolor, 3 * sizeof (float)); + ubo->flags = (fb != NULL) ? 0x1 : 0x0; + if (r_fullbright_cheatsafe || (r_lightmap_cheatsafe && r_fullbright.value)) + ubo->flags |= 0x2; + ubo->entalpha = entity_alpha; + ubo->joints_offsets[0] = lerpdata.pose1 * paliashdr->numjoints; + ubo->joints_offsets[1] = lerpdata.pose2 * paliashdr->numjoints; + + VkDescriptorSet descriptor_sets[4] = {tx->descriptor_set, (fb != NULL) ? fb->descriptor_set : tx->descriptor_set, ubo_set, m->joints_set}; + vulkan_globals.vk_cmd_bind_descriptor_sets ( + cbx->cb, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline.layout.handle, 0, 4, descriptor_sets, 1, &uniform_offset); + + VkBuffer vertex_buffers[1] = {m->vertex_buffer}; + VkDeviceSize vertex_offsets[1] = {0}; + vulkan_globals.vk_cmd_bind_vertex_buffers (cbx->cb, 0, 1, vertex_buffers, vertex_offsets); + vulkan_globals.vk_cmd_bind_index_buffer (cbx->cb, m->index_buffer, 0, VK_INDEX_TYPE_UINT16); + + vulkan_globals.vk_cmd_draw_indexed (cbx->cb, paliashdr->numindexes, 1, 0, 0, 0); + break; + } } - - R_BindPipeline (cbx, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); - - VkBuffer uniform_buffer; - uint32_t uniform_offset; - VkDescriptorSet ubo_set; - aliasubo_t *ubo = (aliasubo_t *)R_UniformAllocate (sizeof (aliasubo_t), &uniform_buffer, &uniform_offset, &ubo_set); - - memcpy (ubo->model_matrix, model_matrix, 16 * sizeof (float)); - memcpy (ubo->shade_vector, shadevector, 3 * sizeof (float)); - ubo->blend_factor = blend; - memcpy (ubo->light_color, lightcolor, 3 * sizeof (float)); - ubo->flags = (fb != NULL) ? 0x1 : 0x0; - if (r_fullbright_cheatsafe || (r_lightmap_cheatsafe && r_fullbright.value)) - ubo->flags |= 0x2; - ubo->entalpha = entity_alpha; - - VkDescriptorSet descriptor_sets[3] = {tx->descriptor_set, (fb != NULL) ? fb->descriptor_set : tx->descriptor_set, ubo_set}; - vulkan_globals.vk_cmd_bind_descriptor_sets ( - cbx->cb, VK_PIPELINE_BIND_POINT_GRAPHICS, vulkan_globals.alias_pipeline.layout.handle, 0, 3, descriptor_sets, 1, &uniform_offset); - - VkBuffer vertex_buffers[3] = {e->model->vertex_buffer, e->model->vertex_buffer, e->model->vertex_buffer}; - VkDeviceSize vertex_offsets[3] = { - (unsigned)e->model->vbostofs, GLARB_GetXYZOffset (e, paliashdr, lerpdata.pose1), GLARB_GetXYZOffset (e, paliashdr, lerpdata.pose2)}; - vulkan_globals.vk_cmd_bind_vertex_buffers (cbx->cb, 0, 3, vertex_buffers, vertex_offsets); - vulkan_globals.vk_cmd_bind_index_buffer (cbx->cb, e->model->index_buffer, 0, VK_INDEX_TYPE_UINT16); - - vulkan_globals.vk_cmd_draw_indexed (cbx->cb, paliashdr->numindexes, 1, 0, 0, 0); } /* @@ -202,16 +248,19 @@ void R_SetupAliasFrame (entity_t *e, aliashdr_t *paliashdr, int frame, lerpdata_ else lerpdata->blend = CLAMP (0, (cl.time - e->lerpstart) / e->lerptime, 1); - if (e->currentpose >= paliashdr->numposes || e->currentpose < 0) - { - Con_DPrintf ("R_AliasSetupFrame: invalid current pose %d (%d total) for '%s'\n", e->currentpose, paliashdr->numposes, e->model->name); - e->currentpose = 0; - } - - if (e->previouspose >= paliashdr->numposes || e->previouspose < 0) + if (paliashdr->poseverttype == PV_QUAKE1) { - Con_DPrintf ("R_AliasSetupFrame: invalid prev pose %d (%d total) for '%s'\n", e->previouspose, paliashdr->numposes, e->model->name); - e->previouspose = e->currentpose; + if (e->currentpose >= paliashdr->numposes || e->currentpose < 0) + { + Con_DPrintf ("R_AliasSetupFrame: invalid current pose %d (%d total) for '%s'\n", e->currentpose, paliashdr->numposes, e->model->name); + e->currentpose = 0; + } + + if (e->previouspose >= paliashdr->numposes || e->previouspose < 0) + { + Con_DPrintf ("R_AliasSetupFrame: invalid prev pose %d (%d total) for '%s'\n", e->previouspose, paliashdr->numposes, e->model->name); + e->previouspose = e->currentpose; + } } lerpdata->pose1 = e->previouspose; @@ -407,9 +456,8 @@ void R_DrawAliasModel (cb_context_t *cbx, entity_t *e, int *aliaspolys) TranslationMatrix (translation_matrix, paliashdr->scale_origin[0], paliashdr->scale_origin[1] * fovscale, paliashdr->scale_origin[2] * fovscale); MatrixMultiply (model_matrix, translation_matrix); - // Scale multiplied by 255 because we use UNORM instead of USCALED in the vertex shader float scale_matrix[16]; - ScaleMatrix (scale_matrix, paliashdr->scale[0] * 255.0f, paliashdr->scale[1] * fovscale * 255.0f, paliashdr->scale[2] * fovscale * 255.0f); + ScaleMatrix (scale_matrix, paliashdr->scale[0], paliashdr->scale[1] * fovscale, paliashdr->scale[2] * fovscale); MatrixMultiply (model_matrix, scale_matrix); // @@ -470,7 +518,7 @@ void R_DrawAliasModel (cb_context_t *cbx, entity_t *e, int *aliaspolys) // // draw it // - GL_DrawAliasFrame (cbx, e, paliashdr, lerpdata, tx, fb, model_matrix, entalpha, alphatest, shadevector, lightcolor); + GL_DrawAliasFrame (cbx, e, paliashdr, lerpdata, tx, fb, model_matrix, entalpha, alphatest, shadevector, lightcolor, false); } // johnfitz -- values for shadow matrix @@ -489,12 +537,12 @@ void R_DrawAliasModel_ShowTris (cb_context_t *cbx, entity_t *e) { aliashdr_t *paliashdr; lerpdata_t lerpdata; - float blend; // // setup pose/lerp data -- do it first so we don't miss updates due to culling // paliashdr = (aliashdr_t *)Mod_Extradata (e->model); + R_SetupAliasFrame (e, paliashdr, e->frame, &lerpdata); R_SetupEntityTransform (e, &lerpdata); @@ -522,42 +570,11 @@ void R_DrawAliasModel_ShowTris (cb_context_t *cbx, entity_t *e) TranslationMatrix (translation_matrix, paliashdr->scale_origin[0], paliashdr->scale_origin[1] * fovscale, paliashdr->scale_origin[2] * fovscale); MatrixMultiply (model_matrix, translation_matrix); - // Scale multiplied by 255 because we use UNORM instead of USCALED in the vertex shader float scale_matrix[16]; - ScaleMatrix (scale_matrix, paliashdr->scale[0] * 255.0f, paliashdr->scale[1] * fovscale * 255.0f, paliashdr->scale[2] * fovscale * 255.0f); + ScaleMatrix (scale_matrix, paliashdr->scale[0], paliashdr->scale[1] * fovscale, paliashdr->scale[2] * fovscale); MatrixMultiply (model_matrix, scale_matrix); - if (r_showtris.value == 1) - R_BindPipeline (cbx, VK_PIPELINE_BIND_POINT_GRAPHICS, vulkan_globals.alias_showtris_pipeline); - else - R_BindPipeline (cbx, VK_PIPELINE_BIND_POINT_GRAPHICS, vulkan_globals.alias_showtris_depth_test_pipeline); - - if (lerpdata.pose1 != lerpdata.pose2) - blend = lerpdata.blend; - else // poses the same means either 1. the entity has paused its animation, or 2. r_lerpmodels is disabled - blend = 0; - - VkBuffer uniform_buffer; - uint32_t uniform_offset; - VkDescriptorSet ubo_set; - aliasubo_t *ubo = (aliasubo_t *)R_UniformAllocate (sizeof (aliasubo_t), &uniform_buffer, &uniform_offset, &ubo_set); - - memcpy (ubo->model_matrix, model_matrix, 16 * sizeof (float)); - memset (ubo->shade_vector, 0, 3 * sizeof (float)); - ubo->blend_factor = blend; - memset (ubo->light_color, 0, 3 * sizeof (float)); - ubo->entalpha = 1.0f; - ubo->flags = 0; - - VkDescriptorSet descriptor_sets[3] = {nulltexture->descriptor_set, nulltexture->descriptor_set, ubo_set}; - vulkan_globals.vk_cmd_bind_descriptor_sets ( - cbx->cb, VK_PIPELINE_BIND_POINT_GRAPHICS, vulkan_globals.alias_pipeline.layout.handle, 0, 3, descriptor_sets, 1, &uniform_offset); - - VkBuffer vertex_buffers[3] = {e->model->vertex_buffer, e->model->vertex_buffer, e->model->vertex_buffer}; - VkDeviceSize vertex_offsets[3] = { - (unsigned)e->model->vbostofs, GLARB_GetXYZOffset (e, paliashdr, lerpdata.pose1), GLARB_GetXYZOffset (e, paliashdr, lerpdata.pose2)}; - vulkan_globals.vk_cmd_bind_vertex_buffers (cbx->cb, 0, 3, vertex_buffers, vertex_offsets); - vulkan_globals.vk_cmd_bind_index_buffer (cbx->cb, e->model->index_buffer, 0, VK_INDEX_TYPE_UINT16); - - vulkan_globals.vk_cmd_draw_indexed (cbx->cb, paliashdr->numindexes, 1, 0, 0, 0); + vec3_t shadevector = {0.0f, 0.0f, 0.0f}; + vec3_t lightcolor = {0.0f, 0.0f, 0.0f}; + GL_DrawAliasFrame (cbx, e, paliashdr, lerpdata, nulltexture, nulltexture, model_matrix, 0.0f, false, shadevector, lightcolor, r_showtris.value); } diff --git a/Quake/r_brush.c b/Quake/r_brush.c index d6237b04e..be9e97f86 100644 --- a/Quake/r_brush.c +++ b/Quake/r_brush.c @@ -2033,9 +2033,6 @@ void GL_BuildBModelVertexBuffer (void) unsigned int varray_bytes; int i, j; qmodel_t *m; - float *varray; - int remaining_size; - int copy_offset; // count all verts in all models bmodel_numverts = 0; @@ -2053,7 +2050,7 @@ void GL_BuildBModelVertexBuffer (void) // build vertex array varray_bytes = VERTEXSIZE * sizeof (float) * bmodel_numverts; - varray = (float *)Mem_Alloc (varray_bytes); + TEMP_ALLOC_ZEROED (float, varray, varray_bytes / sizeof (float)); for (j = 1; j < MAX_MODELS; j++) { @@ -2076,33 +2073,8 @@ void GL_BuildBModelVertexBuffer (void) R_CreateBuffer ( &bmodel_vertex_buffer, &bmodel_memory, varray_bytes, usage, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, 0, &num_vulkan_bmodel_allocations, &bmodel_vertex_buffer_device_address, "BModel vertices"); - - remaining_size = varray_bytes; - copy_offset = 0; - - while (remaining_size > 0) - { - const int size_to_copy = q_min (remaining_size, vulkan_globals.staging_buffer_size); - VkBuffer staging_buffer; - VkCommandBuffer command_buffer; - int staging_offset; - unsigned char *staging_memory = R_StagingAllocate (size_to_copy, 1, &command_buffer, &staging_buffer, &staging_offset); - - VkBufferCopy region; - region.srcOffset = staging_offset; - region.dstOffset = copy_offset; - region.size = size_to_copy; - vkCmdCopyBuffer (command_buffer, staging_buffer, bmodel_vertex_buffer, 1, ®ion); - - R_StagingBeginCopy (); - memcpy (staging_memory, (byte *)varray + copy_offset, size_to_copy); - R_StagingEndCopy (); - - copy_offset += size_to_copy; - remaining_size -= size_to_copy; - } - - Mem_Free (varray); + R_StagingUploadBuffer (bmodel_vertex_buffer, varray_bytes, (byte *)varray); + TEMP_FREE (varray); } /* diff --git a/Quake/r_part_fte.c b/Quake/r_part_fte.c index df86c315a..fa39c687c 100644 --- a/Quake/r_part_fte.c +++ b/Quake/r_part_fte.c @@ -1160,21 +1160,22 @@ static void P_LoadTexture (part_type_t *ptype, qboolean warn) ptype->looks.texture = TexMgr_FindTexture (NULL, texname); if (!ptype->looks.texture) { + enum srcformat fmt = SRC_RGBA; if (!data) { q_snprintf (filename, sizeof (filename), "textures/%s", ptype->texname); - data = Image_LoadImage (filename, &fwidth, &fheight); + data = Image_LoadImage (filename, &fwidth, &fheight, &fmt); } if (!data) { q_snprintf (filename, sizeof (filename), "%s", ptype->texname); - data = Image_LoadImage (filename, &fwidth, &fheight); + data = Image_LoadImage (filename, &fwidth, &fheight, &fmt); } if (data) { ptype->looks.texture = TexMgr_LoadImage ( - NULL, texname, fwidth, fheight, SRC_RGBA, data, filename, 0, + NULL, texname, fwidth, fheight, fmt, data, filename, 0, (ptype->looks.premul ? TEXPREF_PREMULTIPLY : 0) | (ptype->looks.nearest ? TEXPREF_NEAREST : 0) | TEXPREF_NOPICMIP | TEXPREF_ALPHA); } } diff --git a/Shaders/alias.vert b/Shaders/alias.vert index 7886fb1a4..4e42d22ff 100644 --- a/Shaders/alias.vert +++ b/Shaders/alias.vert @@ -51,8 +51,8 @@ void main () { out_texcoord = in_texcoord; - vec4 lerped_position = mix (vec4 (in_pose1_position.xyz, 1.0f), vec4 (in_pose2_position.xyz, 1.0f), ubo.blend_factor); - vec4 model_space_position = ubo.model_matrix * lerped_position; + const vec4 lerped_position = vec4 (mix (in_pose1_position.xyz, in_pose2_position.xyz, ubo.blend_factor) * 255.0f, 1.0f); + const vec4 model_space_position = ubo.model_matrix * lerped_position; gl_Position = push_constants.view_projection_matrix * model_space_position; if ((ubo.flags & 0x2) == 0) diff --git a/Shaders/alias_alphatest.frag b/Shaders/alias_alphatest.frag index c45d61c9c..e32a6d9df 100644 --- a/Shaders/alias_alphatest.frag +++ b/Shaders/alias_alphatest.frag @@ -36,7 +36,7 @@ void main () vec4 result = texture (diffuse_tex, in_texcoord.xy); if (result.a < 0.666f) discard; - result *= in_color; + result *= in_color * 2.0f; if ((ubo.flags & 0x1) != 0) result += texture (fullbright_tex, in_texcoord.xy); diff --git a/Shaders/md5.vert b/Shaders/md5.vert new file mode 100644 index 000000000..0a91f8464 --- /dev/null +++ b/Shaders/md5.vert @@ -0,0 +1,96 @@ +#version 460 +#extension GL_ARB_separate_shader_objects : enable +#extension GL_ARB_shading_language_420pack : enable +#extension GL_GOOGLE_include_directive : enable + +layout (push_constant) uniform PushConsts +{ + mat4 view_projection_matrix; + vec3 fog_color; + float fog_density; +} +push_constants; + +layout (set = 2, binding = 0) uniform UBO +{ + mat4 model_matrix; + vec3 shade_vector; + float blend_factor; + vec3 light_color; + float entalpha; + uint flags; + uint joints_offset0; + uint joints_offset1; +} +ubo; + +layout (std430, set = 3, binding = 0) restrict readonly buffer joints_buffer +{ + float joint_mats[]; +}; + +layout (location = 0) in vec3 in_position; +layout (location = 1) in vec3 in_normal; +layout (location = 2) in vec2 in_texcoord; +layout (location = 3) in vec4 in_joint_weights; +layout (location = 4) in uvec4 in_joint_indices; + +layout (location = 0) out vec2 out_texcoord; +layout (location = 1) out vec4 out_color; +layout (location = 2) out float out_fog_frag_coord; + +out gl_PerVertex +{ + vec4 gl_Position; +}; + +float r_avertexnormal_dot (vec3 vertexnormal) // from MH +{ + const float dot = dot (vertexnormal, ubo.shade_vector); + // wtf - this reproduces anorm_dots within as reasonable a degree of tolerance as the >= 0 case + if (dot < 0.0) + return 1.0 + dot * (13.0 / 44.0); + else + return 1.0 + dot; +} + +void main () +{ + out_texcoord = in_texcoord; + + const vec3 xyz = in_position.xyz; + + vec3 skinned_positions[2] = {vec3 (0.0f), vec3 (0.0f)}; + uint joint_offsets[2] = {ubo.joints_offset0, ubo.joints_offset1}; + for (int j = 0; j < 2; ++j) + { + const uint joints_offset = joint_offsets[j]; + for (int i = 0; i < 4; ++i) + { + const uint joint_index = in_joint_indices[i]; + const float joint_weight = in_joint_weights[i]; + float joint_mat[12]; + for (int k = 0; k < 12; ++k) + joint_mat[k] = joint_mats[((joints_offset + joint_index) * 12) + k]; + float y = (xyz[0] * joint_mat[4] + xyz[1] * joint_mat[5] + xyz[2] * joint_mat[6] + joint_mat[7]); + float z = (xyz[0] * joint_mat[8] + xyz[1] * joint_mat[9] + xyz[2] * joint_mat[10] + joint_mat[11]); + float x = (xyz[0] * joint_mat[0] + xyz[1] * joint_mat[1] + xyz[2] * joint_mat[2] + joint_mat[3]); + const vec3 skinned_pos = vec3 (x, y, z); + skinned_positions[j] += joint_weight * skinned_pos; + } + } + + const vec4 lerped_position = vec4 (mix (skinned_positions[0], skinned_positions[1], ubo.blend_factor), 1.0f); + const vec4 model_space_position = ubo.model_matrix * lerped_position; + gl_Position = push_constants.view_projection_matrix * model_space_position; + + if ((ubo.flags & 0x2) == 0) + { + const float dot = r_avertexnormal_dot (in_normal); + out_color = vec4 (ubo.light_color * dot, 1.0); + } + else + out_color = vec4 (ubo.light_color, 1.0f); + + out_fog_frag_coord = gl_Position.w; +} \ No newline at end of file diff --git a/Shaders/shaders.h b/Shaders/shaders.h index 7251cfbd0..e9b874499 100644 --- a/Shaders/shaders.h +++ b/Shaders/shaders.h @@ -34,6 +34,7 @@ DECLARE_SHADER_SPV (world_frag); DECLARE_SHADER_SPV (alias_vert); DECLARE_SHADER_SPV (alias_frag); DECLARE_SHADER_SPV (alias_alphatest_frag); +DECLARE_SHADER_SPV (md5_vert); DECLARE_SHADER_SPV (sky_layer_vert); DECLARE_SHADER_SPV (sky_layer_frag); DECLARE_SHADER_SPV (sky_box_frag); diff --git a/Windows/VisualStudio/embedded.vcxproj b/Windows/VisualStudio/embedded.vcxproj index cb741d65f..705576379 100644 --- a/Windows/VisualStudio/embedded.vcxproj +++ b/Windows/VisualStudio/embedded.vcxproj @@ -148,6 +148,9 @@ $(VULKAN_SDK)\bin\spirv-remap.exe -s -i "$(SolutionDir)..\..\Shaders\Compiled\Re Document + + Document + @@ -181,7 +184,6 @@ $(VULKAN_SDK)\bin\spirv-remap.exe -s -i "$(SolutionDir)..\..\Shaders\Compiled\Re "$(SolutionDir)\Build-bintoc\$(PlatformShortName)\$(Configuration)\bintoc.exe" "$(SolutionDir)..\..\Quake\vkquake.pak" vkquake_pak "$(SolutionDir)..\..\Quake\embedded_pak.c" "$(SolutionDir)\Build-bintoc\$(PlatformShortName)\$(Configuration)\bintoc.exe" "$(SolutionDir)..\..\Quake\vkquake.pak" vkquake_pak "$(SolutionDir)..\..\Quake\embedded_pak.c" - 16.0 diff --git a/Windows/VisualStudio/embedded.vcxproj.filters b/Windows/VisualStudio/embedded.vcxproj.filters index f7f0eb947..3e6a443b4 100644 --- a/Windows/VisualStudio/embedded.vcxproj.filters +++ b/Windows/VisualStudio/embedded.vcxproj.filters @@ -92,6 +92,9 @@ Shaders + + Shaders + diff --git a/Windows/VisualStudio/vkquake.vcxproj b/Windows/VisualStudio/vkquake.vcxproj index d8a8ba97d..2238694d1 100644 --- a/Windows/VisualStudio/vkquake.vcxproj +++ b/Windows/VisualStudio/vkquake.vcxproj @@ -445,6 +445,14 @@ copy "$(SolutionDir)\..\SDL2\lib64\*.dll" "$(TargetDir)" NotUsing NotUsing + + true + true + NotUsing + NotUsing + NotUsing + NotUsing + true true @@ -685,6 +693,14 @@ copy "$(SolutionDir)\..\SDL2\lib64\*.dll" "$(TargetDir)" NotUsing NotUsing + + true + true + NotUsing + NotUsing + NotUsing + NotUsing + true true diff --git a/Windows/VisualStudio/vkquake.vcxproj.filters b/Windows/VisualStudio/vkquake.vcxproj.filters index 6c17a5e52..5781042b0 100644 --- a/Windows/VisualStudio/vkquake.vcxproj.filters +++ b/Windows/VisualStudio/vkquake.vcxproj.filters @@ -481,6 +481,12 @@ Main + + Shaders\Release + + + Shaders\Debug + diff --git a/meson.build b/meson.build index 5eeeb6ed0..ea58d42cb 100644 --- a/meson.build +++ b/meson.build @@ -4,6 +4,7 @@ shaders = [ 'Shaders/alias.frag', 'Shaders/alias.vert', 'Shaders/alias_alphatest.frag', + 'Shaders/md5.vert', 'Shaders/basic.frag', 'Shaders/basic.vert', 'Shaders/basic_alphatest.frag',