/* ================ GL_MakeAliasModelDisplayLists ================ */ void GL_MakeAliasModelDisplayLists (qmodel_t *m, aliashdr_t *hdr) { int i, j; int *cmds; trivertx_t *verts; float hscale, vscale; //johnfitz -- padded skins int count; //johnfitz -- precompute texcoords for padded skins int *loadcmds; //johnfitz //johnfitz -- padded skins hscale = (float)hdr->skinwidth/(float)TexMgr_PadConditional(hdr->skinwidth); vscale = (float)hdr->skinheight/(float)TexMgr_PadConditional(hdr->skinheight); //johnfitz aliasmodel = m; paliashdr = hdr; // (aliashdr_t *)Mod_Extradata (m); //johnfitz -- generate meshes Con_DPrintf2 ("meshing %s...\n",m->name); BuildTris (); // save the data out paliashdr->poseverts = numorder; cmds = (int *) Hunk_Alloc (numcommands * 4); paliashdr->commands = (byte *)cmds - (byte *)paliashdr; //johnfitz -- precompute texcoords for padded skins loadcmds = commands; while(1) { *cmds++ = count = *loadcmds++; if (!count) break; if (count < 0) count = -count; do { *(float *)cmds++ = hscale * (*(float *)loadcmds++); *(float *)cmds++ = vscale * (*(float *)loadcmds++); } while (--count); } //johnfitz verts = (trivertx_t *) Hunk_Alloc (paliashdr->numposes * paliashdr->poseverts * sizeof(trivertx_t)); paliashdr->posedata = (byte *)verts - (byte *)paliashdr; for (i=0 ; i<paliashdr->numposes ; i++) for (j=0 ; j<numorder ; j++) *verts++ = poseverts[i][vertexorder[j]]; // ericw GL_MakeAliasModelDisplayLists_VBO (); }
/* ================ Draw_PicFromWad ================ */ qpic_t *Draw_PicFromWad (const char *name) { qpic_t *p; glpic_t gl; src_offset_t offset; //johnfitz p = (qpic_t *) W_GetLumpName (name); if (!p) return pic_nul; //johnfitz // load little ones into the scrap if (p->width < 64 && p->height < 64) { int x, y; int i, j, k; int texnum; texnum = Scrap_AllocBlock (p->width, p->height, &x, &y); scrap_dirty = true; k = 0; for (i=0 ; i<p->height ; i++) { for (j=0 ; j<p->width ; j++, k++) scrap_texels[texnum][(y+i)*BLOCK_WIDTH + x + j] = p->data[k]; } gl.gltexture = scrap_textures[texnum]; //johnfitz -- changed to an array //johnfitz -- no longer go from 0.01 to 0.99 gl.sl = x/(float)BLOCK_WIDTH; gl.sh = (x+p->width)/(float)BLOCK_WIDTH; gl.tl = y/(float)BLOCK_WIDTH; gl.th = (y+p->height)/(float)BLOCK_WIDTH; } else { char texturename[64]; //johnfitz q_snprintf (texturename, sizeof(texturename), "%s:%s", WADFILENAME, name); //johnfitz offset = (src_offset_t)p - (src_offset_t)wad_base + sizeof(int)*2; //johnfitz gl.gltexture = TexMgr_LoadImage (NULL, texturename, p->width, p->height, SRC_INDEXED, p->data, WADFILENAME, offset, TEXPREF_ALPHA | TEXPREF_PAD | TEXPREF_NOPICMIP); //johnfitz -- TexMgr gl.sl = 0; gl.sh = (float)p->width/(float)TexMgr_PadConditional(p->width); //johnfitz gl.tl = 0; gl.th = (float)p->height/(float)TexMgr_PadConditional(p->height); //johnfitz } memcpy (p->data, &gl, sizeof(glpic_t)); return p; }
/* ================ Draw_CachePic ================ */ qpic_t *Draw_CachePic (const char *path) { cachepic_t *pic; int i; qpic_t *dat; glpic_t gl; for (pic=menu_cachepics, i=0 ; i<menu_numcachepics ; pic++, i++) { if (!strcmp (path, pic->name)) return &pic->pic; } if (menu_numcachepics == MAX_CACHED_PICS) Sys_Error ("menu_numcachepics == MAX_CACHED_PICS"); menu_numcachepics++; strcpy (pic->name, path); // // load the pic from disk // dat = (qpic_t *)COM_LoadTempFile (path, NULL); if (!dat) Sys_Error ("Draw_CachePic: failed to load %s", path); SwapPic (dat); // HACK HACK HACK --- we need to keep the bytes for // the translatable player picture just for the menu // configuration dialog if (!strcmp (path, "gfx/menuplyr.lmp")) memcpy (menuplyr_pixels, dat->data, dat->width*dat->height); pic->pic.width = dat->width; pic->pic.height = dat->height; gl.gltexture = TexMgr_LoadImage (NULL, path, dat->width, dat->height, SRC_INDEXED, dat->data, path, sizeof(int)*2, TEXPREF_ALPHA | TEXPREF_PAD | TEXPREF_NOPICMIP); //johnfitz -- TexMgr gl.sl = 0; gl.sh = (float)dat->width/(float)TexMgr_PadConditional(dat->width); //johnfitz gl.tl = 0; gl.th = (float)dat->height/(float)TexMgr_PadConditional(dat->height); //johnfitz memcpy (pic->pic.data, &gl, sizeof(glpic_t)); return &pic->pic; }
/* ================ Draw_MakePic -- johnfitz -- generate pics from internal data ================ */ qpic_t *Draw_MakePic (const char *name, int width, int height, byte *data) { int flags = TEXPREF_NEAREST | TEXPREF_ALPHA | TEXPREF_PERSIST | TEXPREF_NOPICMIP | TEXPREF_PAD; qpic_t *pic; glpic_t gl; pic = (qpic_t *) Hunk_Alloc (sizeof(qpic_t) - 4 + sizeof (glpic_t)); pic->width = width; pic->height = height; gl.gltexture = TexMgr_LoadImage (NULL, name, width, height, SRC_INDEXED, data, "", (src_offset_t)data, flags); gl.sl = 0; gl.sh = (float)width/(float)TexMgr_PadConditional(width); gl.tl = 0; gl.th = (float)height/(float)TexMgr_PadConditional(height); memcpy (pic->data, &gl, sizeof(glpic_t)); return pic; }
/* ================ GLMesh_LoadVertexBuffer Upload the given alias model's mesh to a VBO Original code by MH from RMQEngine ================ */ static void GLMesh_LoadVertexBuffer (qmodel_t *m, const aliashdr_t *hdr) { int totalvbosize = 0; int remaining_size; int copy_offset; const aliasmesh_t *desc; const short *indexes; const trivertx_t *trivertexes; byte *vbodata; int f; VkResult err; // count the sizes we need // 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; m->vboxyzofs = 0; totalvbosize += (hdr->numposes * hdr->numverts_vbo * sizeof (meshxyz_t)); // ericw -- what RMQEngine called nummeshframes is called numposes in QuakeSpasm m->vbostofs = totalvbosize; totalvbosize += (hdr->numverts_vbo * sizeof (meshst_t)); if (!hdr->numindexes) return; if (!totalvbosize) return; // grab the pointers to data in the extradata desc = (aliasmesh_t *) ((byte *) hdr + hdr->meshdesc); indexes = (short *) ((byte *) hdr + hdr->indexes); trivertexes = (trivertx_t *) ((byte *)hdr + hdr->vertexes); { const int totalindexsize = hdr->numindexes * sizeof (unsigned short); // Allocate index buffer & upload to GPU VkBufferCreateInfo buffer_create_info; memset(&buffer_create_info, 0, sizeof(buffer_create_info)); buffer_create_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; buffer_create_info.size = totalindexsize; buffer_create_info.usage = VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT; err = vkCreateBuffer(vulkan_globals.device, &buffer_create_info, NULL, &m->index_buffer); if (err != VK_SUCCESS) Sys_Error("vkCreateBuffer failed"); GL_SetObjectName((uint64_t)m->index_buffer, VK_DEBUG_REPORT_OBJECT_TYPE_BUFFER_EXT, m->name); 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 aligned_offset = GL_AllocateFromHeaps(GEOMETRY_MAX_HEAPS, index_buffer_heaps, heap_size, memory_type_index, memory_requirements.size, memory_requirements.alignment, &m->index_heap, &m->index_heap_node, &num_vulkan_mesh_allocations, "Index Buffers"); err = vkBindBufferMemory(vulkan_globals.device, m->index_buffer, m->index_heap->memory, 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, STAGING_BUFFER_SIZE_KB * 1024); 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); memcpy(staging_memory, (byte*)indexes + copy_offset, size_to_copy); 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); copy_offset += size_to_copy; remaining_size -= size_to_copy; } } // create the vertex buffer (empty) vbodata = (byte *) malloc(totalvbosize); memset(vbodata, 0, 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 { int v; meshxyz_t *xyz = (meshxyz_t *) (vbodata + (f * hdr->numverts_vbo * sizeof (meshxyz_t))); const trivertx_t *tv = trivertexes + (hdr->numverts * f); for (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 } } // fill in the ST coords at the end of the buffer { meshst_t *st; float hscale, vscale; //johnfitz -- padded skins hscale = (float)hdr->skinwidth/(float)TexMgr_PadConditional(hdr->skinwidth); vscale = (float)hdr->skinheight/(float)TexMgr_PadConditional(hdr->skinheight); //johnfitz st = (meshst_t *) (vbodata + m->vbostofs); for (f = 0; f < hdr->numverts_vbo; f++) { st[f].st[0] = hscale * ((float) desc[f].st[0] + 0.5f) / (float) hdr->skinwidth; st[f].st[1] = vscale * ((float) desc[f].st[1] + 0.5f) / (float) hdr->skinheight; } } // Allocate vertex buffer & upload to GPU { VkBufferCreateInfo buffer_create_info; memset(&buffer_create_info, 0, sizeof(buffer_create_info)); buffer_create_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; buffer_create_info.size = totalvbosize; buffer_create_info.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT; err = vkCreateBuffer(vulkan_globals.device, &buffer_create_info, NULL, &m->vertex_buffer); if (err != VK_SUCCESS) Sys_Error("vkCreateBuffer failed"); GL_SetObjectName((uint64_t)m->vertex_buffer, VK_DEBUG_REPORT_OBJECT_TYPE_BUFFER_EXT, m->name); 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 aligned_offset = GL_AllocateFromHeaps(GEOMETRY_MAX_HEAPS, vertex_buffer_heaps, heap_size, memory_type_index, memory_requirements.size, memory_requirements.alignment, &m->vertex_heap, &m->vertex_heap_node, &num_vulkan_mesh_allocations, "Vertex Buffers"); err = vkBindBufferMemory(vulkan_globals.device, m->vertex_buffer, m->vertex_heap->memory, aligned_offset); if (err != VK_SUCCESS) Sys_Error("vkBindBufferMemory failed"); remaining_size = totalvbosize; copy_offset = 0; while (remaining_size > 0) { const int size_to_copy = q_min(remaining_size, STAGING_BUFFER_SIZE_KB * 1024); 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); memcpy(staging_memory, (byte*)vbodata + copy_offset, size_to_copy); 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); copy_offset += size_to_copy; remaining_size -= size_to_copy; } } free (vbodata); }
/* ================ GLMesh_LoadVertexBuffer Upload the given alias model's mesh to a VBO Original code by MH from RMQEngine ================ */ static void GLMesh_LoadVertexBuffer (qmodel_t *m, const aliashdr_t *hdr) { int totalvbosize = 0; const aliasmesh_t *desc; const short *indexes; const trivertx_t *trivertexes; byte *vbodata; int f; if (!gl_glsl_alias_able) return; // count the sizes we need // 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; m->vboxyzofs = 0; totalvbosize += (hdr->numposes * hdr->numverts_vbo * sizeof (meshxyz_t)); // ericw -- what RMQEngine called nummeshframes is called numposes in QuakeSpasm m->vbostofs = totalvbosize; totalvbosize += (hdr->numverts_vbo * sizeof (meshst_t)); if (!hdr->numindexes) return; if (!totalvbosize) return; // grab the pointers to data in the extradata desc = (aliasmesh_t *) ((byte *) hdr + hdr->meshdesc); indexes = (short *) ((byte *) hdr + hdr->indexes); trivertexes = (trivertx_t *) ((byte *)hdr + hdr->vertexes); // upload indices buffer GL_DeleteBuffersFunc (1, &m->meshindexesvbo); GL_GenBuffersFunc (1, &m->meshindexesvbo); GL_BindBufferFunc (GL_ELEMENT_ARRAY_BUFFER, m->meshindexesvbo); GL_BufferDataFunc (GL_ELEMENT_ARRAY_BUFFER, hdr->numindexes * sizeof (unsigned short), indexes, GL_STATIC_DRAW); // create the vertex buffer (empty) vbodata = (byte *) malloc(totalvbosize); memset(vbodata, 0, 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 { int v; meshxyz_t *xyz = (meshxyz_t *) (vbodata + (f * hdr->numverts_vbo * sizeof (meshxyz_t))); const trivertx_t *tv = trivertexes + (hdr->numverts * f); for (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 } } // fill in the ST coords at the end of the buffer { meshst_t *st; float hscale, vscale; //johnfitz -- padded skins hscale = (float)hdr->skinwidth/(float)TexMgr_PadConditional(hdr->skinwidth); vscale = (float)hdr->skinheight/(float)TexMgr_PadConditional(hdr->skinheight); //johnfitz st = (meshst_t *) (vbodata + m->vbostofs); for (f = 0; f < hdr->numverts_vbo; f++) { st[f].st[0] = hscale * ((float) desc[f].st[0] + 0.5f) / (float) hdr->skinwidth; st[f].st[1] = vscale * ((float) desc[f].st[1] + 0.5f) / (float) hdr->skinheight; } } // upload vertexes buffer GL_DeleteBuffersFunc (1, &m->meshvbo); GL_GenBuffersFunc (1, &m->meshvbo); GL_BindBufferFunc (GL_ARRAY_BUFFER, m->meshvbo); GL_BufferDataFunc (GL_ARRAY_BUFFER, totalvbosize, vbodata, GL_STATIC_DRAW); free (vbodata); // invalidate the cached bindings GL_ClearBufferBindings (); }