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',