/* =========================================================================== Copyright (C) 2023-2024 Gian 'myT' Schellenbaum This file is part of Challenge Quake 3 (CNQ3). Challenge Quake 3 is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Challenge Quake 3 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Challenge Quake 3. If not, see . =========================================================================== */ // Cinematic Rendering Pipeline - vertex and index buffer management #include "crp_local.h" void GeoBuffers::Create(const char* name, uint32_t vertexCount, uint32_t indexCount) { baseVertexBuffers[BaseBufferId::Position].CreateVertexBuffer( va("%s position", name), MemoryUsage::Upload, vertexCount, sizeof(vec3_t)); baseVertexBuffers[BaseBufferId::Normal].CreateVertexBuffer( va("%s normal", name), MemoryUsage::Upload, vertexCount, sizeof(vec3_t)); stageVertexBuffers[StageBufferId::TexCoords].CreateVertexBuffer( va("%s tc", name), MemoryUsage::Upload, vertexCount * MAX_SHADER_STAGES, sizeof(vec2_t)); stageVertexBuffers[StageBufferId::Color].CreateVertexBuffer( va("%s color", name), MemoryUsage::Upload, vertexCount * MAX_SHADER_STAGES, sizeof(color4ub_t)); indexBuffer.Create(name, MemoryUsage::Upload, indexCount); vertexBuffers[0] = baseVertexBuffers[BaseBufferId::Position].buffer; vertexBuffers[1] = baseVertexBuffers[BaseBufferId::Normal].buffer; vertexBuffers[2] = stageVertexBuffers[StageBufferId::TexCoords].buffer; vertexBuffers[3] = stageVertexBuffers[StageBufferId::Color].buffer; vertexBufferStrides[0] = sizeof(vec3_t); vertexBufferStrides[1] = sizeof(vec3_t); vertexBufferStrides[2] = sizeof(vec2_t); vertexBufferStrides[3] = sizeof(color4ub_t); } void GeoBuffers::Rewind() { for(uint32_t b = 0; b < ARRAY_LEN(baseVertexBuffers); ++b) { baseVertexBuffers[b].Rewind(); } for(uint32_t b = 0; b < ARRAY_LEN(stageVertexBuffers); ++b) { stageVertexBuffers[b].Rewind(); } indexBuffer.Rewind(); } void GeoBuffers::BeginUpload() { for(uint32_t b = 0; b < ARRAY_LEN(baseVertexBuffers); ++b) { baseVertexBuffers[b].BeginUpload(); } for(uint32_t b = 0; b < ARRAY_LEN(stageVertexBuffers); ++b) { stageVertexBuffers[b].BeginUpload(); } indexBuffer.BeginUpload(); } void GeoBuffers::EndUpload() { for(uint32_t b = 0; b < ARRAY_LEN(baseVertexBuffers); ++b) { baseVertexBuffers[b].EndUpload(); } for(uint32_t b = 0; b < ARRAY_LEN(stageVertexBuffers); ++b) { stageVertexBuffers[b].EndUpload(); } indexBuffer.EndUpload(); } void GeoBuffers::UploadBase() { indexBuffer.Upload(); const uint32_t batchOffset = baseVertexBuffers[0].batchFirst + baseVertexBuffers[0].batchCount; float* pos = (float*)baseVertexBuffers[BaseBufferId::Position].mapped + 3 * batchOffset; for(int v = 0; v < tess.numVertexes; ++v) { pos[0] = tess.xyz[v][0]; pos[1] = tess.xyz[v][1]; pos[2] = tess.xyz[v][2]; pos += 3; } float* nor = (float*)baseVertexBuffers[BaseBufferId::Normal].mapped + 3 * batchOffset; for(int v = 0; v < tess.numVertexes; ++v) { nor[0] = tess.normal[v][0]; nor[1] = tess.normal[v][1]; nor[2] = tess.normal[v][2]; nor += 3; } } void GeoBuffers::UploadStage(uint32_t svarsIndex) { const uint32_t batchOffset = stageVertexBuffers[0].batchFirst + stageVertexBuffers[0].batchCount; const stageVars_t& sv = tess.svars[svarsIndex]; uint8_t* const tcBuffer = stageVertexBuffers[StageBufferId::TexCoords].mapped; float* tc = (float*)tcBuffer + 2 * batchOffset; memcpy(tc, &sv.texcoords[0], tess.numVertexes * sizeof(vec2_t)); uint8_t* const colBuffer = stageVertexBuffers[StageBufferId::Color].mapped; uint32_t* col = (uint32_t*)colBuffer + batchOffset; memcpy(col, &sv.colors[0], tess.numVertexes * sizeof(color4ub_t)); } void GeoBuffers::EndBaseBatch(uint32_t vertexCount) { baseVertexBuffers[BaseBufferId::Position].EndBatch(vertexCount); baseVertexBuffers[BaseBufferId::Normal].EndBatch(vertexCount); indexBuffer.EndBatch(tess.numIndexes); } bool GeoBuffers::CanAdd(uint32_t vertexCount, uint32_t indexCount, uint32_t stageCount) { return baseVertexBuffers[0].CanAdd(vertexCount) && stageVertexBuffers[0].CanAdd(vertexCount * stageCount) && indexBuffer.CanAdd(indexCount); } void GeoBuffers::DrawStage(uint32_t vertexCount, uint32_t indexCount) { const uint32_t vertexOffset = stageVertexBuffers[0].batchFirst - baseVertexBuffers[0].batchFirst; uint32_t byteOffsets[BaseBufferId::Count + StageBufferId::Count] = {}; byteOffsets[BaseBufferId::Count + StageBufferId::TexCoords] = vertexOffset * sizeof(vec2_t); byteOffsets[BaseBufferId::Count + StageBufferId::Color] = vertexOffset * sizeof(color4ub_t); CmdBindVertexBuffers(ARRAY_LEN(vertexBuffers), vertexBuffers, vertexBufferStrides, byteOffsets); CmdDrawIndexed(indexCount, indexBuffer.batchFirst, baseVertexBuffers[0].batchFirst); // @NOTE: must happen after the final vertex buffer byte offsets have been computed stageVertexBuffers[StageBufferId::TexCoords].EndBatch(vertexCount); stageVertexBuffers[StageBufferId::Color].EndBatch(vertexCount); } void GeoBuffers::DrawPositionOnly(uint32_t vertexCount, uint32_t indexCount) { const HBuffer buffer = vertexBuffers[BaseBufferId::Position]; const uint32_t stride = vertexBufferStrides[BaseBufferId::Position]; const uint32_t byteOffset = 0; CmdBindVertexBuffers(1, &buffer, &stride, &byteOffset); CmdDrawIndexed(indexCount, indexBuffer.batchFirst, baseVertexBuffers[0].batchFirst); } void GeoBuffers::UploadAndDrawDebugNormals() { if(!baseVertexBuffers[0].CanAdd(tess.numVertexes * 2) || !indexBuffer.CanAdd(tess.numVertexes * 3)) { return; } const uint32_t posBatchOffset = baseVertexBuffers[BaseBufferId::Position].batchFirst + baseVertexBuffers[BaseBufferId::Position].batchCount; float* positions = (float*)baseVertexBuffers[BaseBufferId::Position].mapped + 3 * posBatchOffset; for(uint32_t v = 0; v < tess.numVertexes; v++) { vec3_t newPoint; VectorMA(tess.xyz[v], 4.0f, tess.normal[v], newPoint); *positions++ = tess.xyz[v][0]; *positions++ = tess.xyz[v][1]; *positions++ = tess.xyz[v][2]; *positions++ = newPoint[0]; *positions++ = newPoint[1]; *positions++ = newPoint[2]; } const uint32_t colBatchOffset = stageVertexBuffers[StageBufferId::Color].batchFirst + stageVertexBuffers[StageBufferId::Color].batchCount; uint8_t* const colBuffer = stageVertexBuffers[StageBufferId::Color].mapped; uint32_t* col = (uint32_t*)colBuffer + colBatchOffset; for(uint32_t v = 0; v < tess.numVertexes; v++) { *col++ = 0xFF0000FF; *col++ = 0xFFFF7F00; } uint32_t* indices = indexBuffer.GetCurrentAddress(); for(uint32_t i = 0; i < tess.numVertexes; i++) { *indices++ = i * 2 + 0; *indices++ = i * 2 + 0; *indices++ = i * 2 + 1; } const uint32_t vertexOffset = stageVertexBuffers[0].batchFirst - baseVertexBuffers[0].batchFirst; const uint32_t byteOffsets[2] = { 0, vertexOffset * sizeof(color4ub_t) }; HBuffer vb[2]; vb[0] = vertexBuffers[BaseBufferId::Position]; vb[1] = vertexBuffers[BaseBufferId::Count + StageBufferId::Color]; uint32_t strides[2]; strides[0] = vertexBufferStrides[BaseBufferId::Position]; strides[1] = vertexBufferStrides[BaseBufferId::Count + StageBufferId::Color]; CmdBindVertexBuffers(ARRAY_LEN(vb), vb, strides, byteOffsets); CmdDrawIndexed(tess.numVertexes * 3, indexBuffer.batchFirst, baseVertexBuffers[0].batchFirst); for(int b = 0; b < ARRAY_LEN(baseVertexBuffers); b++) { baseVertexBuffers[b].EndBatch(tess.numVertexes * 2); } for(int b = 0; b < ARRAY_LEN(stageVertexBuffers); b++) { stageVertexBuffers[b].EndBatch(tess.numVertexes * 2); } indexBuffer.EndBatch(tess.numVertexes * 3); }