diff --git a/include/resources/font.hpp b/include/resources/font.hpp index c0f9102..cff944f 100644 --- a/include/resources/font.hpp +++ b/include/resources/font.hpp @@ -22,7 +22,8 @@ public: struct CharData { glm::vec2 atlas_top_left{}; glm::vec2 atlas_bottom_right{}; - glm::vec2 offset{}; + glm::vec2 char_top_left{}; + glm::vec2 char_bottom_right{}; float xAdvance{}; }; diff --git a/src/components/text_ui_renderer.cpp b/src/components/text_ui_renderer.cpp index 253b204..4f055d8 100644 --- a/src/components/text_ui_renderer.cpp +++ b/src/components/text_ui_renderer.cpp @@ -13,12 +13,13 @@ UI::UI(Object* parent) : Component(parent, TypeEnum::UI) m_shader = parent->res.get("shaders/font.glsl"); m_atlasMesh = std::make_unique(std::vector{ - { { 1.0f, 1.0f, 0.0f }, { 0.0f, 1.0f, 0.0f }, { 1.0f, 0.0f } }, - { { 0.0f, 1.0f, 0.0f }, { 0.0f, 1.0f, 0.0f }, { 0.0f, 0.0f } }, - { { 0.0f, 0.0f, 0.0f }, { 0.0f, 1.0f, 0.0f }, { 0.0f, 1.0f } }, - { { 1.0f, 1.0f, 0.0f }, { 0.0f, 1.0f, 0.0f }, { 1.0f, 0.0f } }, - { { 0.0f, 0.0f, 0.0f }, { 0.0f, 1.0f, 0.0f }, { 0.0f, 1.0f } }, - { { 1.0f, 0.0f, 0.0f }, { 0.0f, 1.0f, 0.0f }, { 1.0f, 1.0f } } + { { 0.0f, 0.0f, 0.0f }, { 0.0f, 1.0f, 0.0f }, { 0.0f, 0.0f } }, + { { 0.0f, 1.0f, 0.0f }, { 0.0f, 1.0f, 0.0f }, { 0.0f, 1.0f } }, + { { 1.0f, 1.0f, 0.0f }, { 0.0f, 1.0f, 0.0f }, { 1.0f, 1.0f } }, + + { { 1.0f, 0.0f, 0.0f }, { 0.0f, 1.0f, 0.0f }, { 1.0f, 0.0f } }, + { { 0.0f, 0.0f, 0.0f }, { 0.0f, 1.0f, 0.0f }, { 0.0f, 0.0f } }, + { { 1.0f, 1.0f, 0.0f }, { 0.0f, 1.0f, 0.0f }, { 1.0f, 1.0f } }, }); } @@ -38,14 +39,15 @@ void UI::render(glm::mat4 transform, glm::mat4 view) glm::vec2 size; } pushConsts{}; - int advance = 0; - + float advance = 0.0f; for (char c : m_text) { auto charData = m_font->getCharData(c); - pushConsts.m = glm::mat4{1.0f}; + charData.char_top_left.y *= -1; + pushConsts.m = transform; pushConsts.atlas_top_left = charData.atlas_top_left; pushConsts.atlas_bottom_right = charData.atlas_bottom_right; - pushConsts.offset = charData.offset; + pushConsts.size = (charData.char_bottom_right - charData.char_top_left); + pushConsts.offset = glm::vec2{ charData.char_top_left.x + advance, charData.char_top_left.y }; gfxdev->draw( m_shader->getPipeline(), m_atlasMesh->vb, m_atlasMesh->ib, m_atlasMesh->m_indices.size(), &pushConsts, sizeof(pushConsts), m_font->getAtlasTexture()); diff --git a/src/gfx_device_vulkan.cpp b/src/gfx_device_vulkan.cpp index 0a0fe71..62b7be1 100644 --- a/src/gfx_device_vulkan.cpp +++ b/src/gfx_device_vulkan.cpp @@ -95,6 +95,7 @@ namespace engine { }; struct DrawCall { + const gfx::Pipeline* pipeline = nullptr; // for performance, keep this the same for consecutive draw calls const gfx::Buffer* vertexBuffer = nullptr; const gfx::Buffer* indexBuffer = nullptr; // if this is nullptr, don't use indexed uint32_t count = 0; @@ -1011,8 +1012,8 @@ namespace engine { std::array commandBuffers{}; std::array inFlightFences{}; - std::map> drawQueues{}; - + std::queue drawQueue{}; + VkDescriptorSetLayoutBinding uboLayoutBinding{}; VkDescriptorSetLayout descriptorSetLayout{}; @@ -1510,12 +1511,14 @@ namespace engine { void GFXDevice::draw(const gfx::Pipeline* pipeline, const gfx::Buffer* vertexBuffer, const gfx::Buffer* indexBuffer, uint32_t count, const void* pushConstantData, size_t pushConstantSize, const gfx::Texture* texture) { - assert(vertexBuffer->type == gfx::BufferType::VERTEX); + assert(pipeline != nullptr); assert(vertexBuffer != nullptr); + assert(vertexBuffer->type == gfx::BufferType::VERTEX); assert(indexBuffer == nullptr || indexBuffer->type == gfx::BufferType::INDEX); assert(pushConstantSize <= PUSH_CONSTANT_MAX_SIZE); DrawCall call{ + .pipeline = pipeline, .vertexBuffer = vertexBuffer, .indexBuffer = indexBuffer, // will be ignored if nullptr .count = count, @@ -1525,7 +1528,7 @@ namespace engine { call.texture = texture; // will be ignored if nullptr - pimpl->drawQueues[pipeline].push(call); + pimpl->drawQueue.push(call); } @@ -1570,7 +1573,7 @@ namespace engine { renderPassInfo.renderArea.extent = pimpl->swapchain.extent; std::array clearValues{}; - clearValues[0].color = { {0.1f, 0.1f, 0.8f, 1.0f} }; + clearValues[0].color = { {0.0f, 0.0f, 0.0f, 1.0f} }; clearValues[1].depthStencil = { 1.0f, 0 }; renderPassInfo.clearValueCount = (uint32_t)clearValues.size(); renderPassInfo.pClearValues = clearValues.data(); @@ -1595,33 +1598,50 @@ namespace engine { VkDeviceSize offsets[] = { 0 }; - for (auto& [pipeline, queue] : pimpl->drawQueues) { - vkCmdBindPipeline(pimpl->commandBuffers[frameIndex], VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline->handle); - vkCmdBindDescriptorSets(pimpl->commandBuffers[frameIndex], VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline->layout, 0, 1, &pipeline->descriptorSets[frameIndex], 0, nullptr); - while (queue.empty() == false) { + const gfx::Pipeline* lastPipeline = nullptr; + const gfx::Texture* lastTexture = nullptr; + const gfx::Buffer* lastVertexBuffer = nullptr; + const gfx::Buffer* lastIndexBuffer = nullptr; + while (pimpl->drawQueue.empty() == false) { + + DrawCall call = pimpl->drawQueue.front(); - DrawCall call = queue.front(); - - vkCmdBindDescriptorSets(pimpl->commandBuffers[frameIndex], VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline->layout, 1, 1, &call.texture->descriptorSets[frameIndex], 0, nullptr); - - vkCmdPushConstants(pimpl->commandBuffers[frameIndex], pipeline->layout, VK_SHADER_STAGE_VERTEX_BIT, 0, PUSH_CONSTANT_MAX_SIZE, call.pushConstantData); - - vkCmdBindVertexBuffers(pimpl->commandBuffers[frameIndex], 0, 1, &call.vertexBuffer->buffer, offsets); - - if (call.indexBuffer == nullptr) { - // no index buffer - vkCmdDraw(pimpl->commandBuffers[frameIndex], call.count, 1, 0, 0); - } else { - // use index buffer - vkCmdBindIndexBuffer(pimpl->commandBuffers[frameIndex], call.indexBuffer->buffer, 0, VK_INDEX_TYPE_UINT32); - vkCmdDrawIndexed(pimpl->commandBuffers[frameIndex], call.count, 1, 0, 0, 0); - } - - queue.pop(); + if (call.pipeline != lastPipeline) { + vkCmdBindPipeline(pimpl->commandBuffers[frameIndex], VK_PIPELINE_BIND_POINT_GRAPHICS, call.pipeline->handle); + // bind pipeline uniform-buffer + vkCmdBindDescriptorSets(pimpl->commandBuffers[frameIndex], VK_PIPELINE_BIND_POINT_GRAPHICS, call.pipeline->layout, 0, 1, &call.pipeline->descriptorSets[frameIndex], 0, nullptr); } - } + + if (call.texture != lastTexture) { + // set the texture + vkCmdBindDescriptorSets(pimpl->commandBuffers[frameIndex], VK_PIPELINE_BIND_POINT_GRAPHICS, call.pipeline->layout, 1, 1, &call.texture->descriptorSets[frameIndex], 0, nullptr); + } + + // like uniforms but faster + vkCmdPushConstants(pimpl->commandBuffers[frameIndex], call.pipeline->layout, VK_SHADER_STAGE_VERTEX_BIT, 0, PUSH_CONSTANT_MAX_SIZE, call.pushConstantData); - pimpl->drawQueues.clear(); + if (call.vertexBuffer != lastVertexBuffer) { + vkCmdBindVertexBuffers(pimpl->commandBuffers[frameIndex], 0, 1, &call.vertexBuffer->buffer, offsets); + } + if (call.indexBuffer == nullptr) { + // no index buffer + vkCmdDraw(pimpl->commandBuffers[frameIndex], call.count, 1, 0, 0); + } else { + // use index buffer + if (call.indexBuffer != lastIndexBuffer) { + vkCmdBindIndexBuffer(pimpl->commandBuffers[frameIndex], call.indexBuffer->buffer, 0, VK_INDEX_TYPE_UINT32); + } + vkCmdDrawIndexed(pimpl->commandBuffers[frameIndex], call.count, 1, 0, 0, 0); + } + + lastPipeline = call.pipeline; + lastTexture = call.texture; + lastVertexBuffer = call.vertexBuffer; + lastIndexBuffer = call.indexBuffer; + + pimpl->drawQueue.pop(); + + } vkCmdEndRenderPass(pimpl->commandBuffers[frameIndex]); @@ -1783,7 +1803,7 @@ namespace engine { vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; vertexInputInfo.vertexBindingDescriptionCount = 1; vertexInputInfo.pVertexBindingDescriptions = &bindingDescription; - vertexInputInfo.vertexAttributeDescriptionCount = attribDescs.size(); + vertexInputInfo.vertexAttributeDescriptionCount = (uint32_t)attribDescs.size(); vertexInputInfo.pVertexAttributeDescriptions = attribDescs.data(); VkPipelineInputAssemblyStateCreateInfo inputAssembly{}; @@ -1810,7 +1830,7 @@ namespace engine { VkPipelineDynamicStateCreateInfo dynamicState{}; dynamicState.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; - dynamicState.dynamicStateCount = dynamicStates.size(); + dynamicState.dynamicStateCount = (uint32_t)dynamicStates.size(); dynamicState.pDynamicStates = dynamicStates.data(); VkPipelineViewportStateCreateInfo viewportState{}; @@ -1898,7 +1918,7 @@ namespace engine { VkPipelineLayoutCreateInfo layoutInfo{}; layoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; - layoutInfo.setLayoutCount = setLayouts.size(); + layoutInfo.setLayoutCount = (uint32_t)setLayouts.size(); layoutInfo.pSetLayouts = setLayouts.data(); layoutInfo.pushConstantRangeCount = 1; layoutInfo.pPushConstantRanges = &pushConstantRange; diff --git a/src/resources/font.cpp b/src/resources/font.cpp index b583b22..9f0fb55 100644 --- a/src/resources/font.cpp +++ b/src/resources/font.cpp @@ -19,32 +19,35 @@ Font::Font(const std::filesystem::path& resPath) : Resource(resPath, "font") constexpr int FIRST_CHAR = 32; constexpr int NUM_CHARS = 96; + constexpr float SCALE = 128.0f; + constexpr int BITMAP_WIDTH = 1024; constexpr int BITMAP_HEIGHT = 1024; auto pixels = std::make_unique(BITMAP_WIDTH * BITMAP_HEIGHT); auto bakedChars = std::make_unique(NUM_CHARS); - stbtt_BakeFontBitmap(fontBuffer->data(), 0, 128.0f, pixels.get(), BITMAP_WIDTH, BITMAP_HEIGHT, FIRST_CHAR, NUM_CHARS, bakedChars.get()); + stbtt_BakeFontBitmap(fontBuffer->data(), 0, SCALE, pixels.get(), BITMAP_WIDTH, BITMAP_HEIGHT, FIRST_CHAR, NUM_CHARS, bakedChars.get()); auto textureData = std::make_unique(BITMAP_WIDTH * BITMAP_HEIGHT * 4); for (int i = 0; i < BITMAP_WIDTH * BITMAP_HEIGHT; i++) { - textureData[i * 4 + 0] = pixels[i]; - textureData[i * 4 + 1] = pixels[i]; - textureData[i * 4 + 2] = pixels[i]; + textureData[i * 4 + 0] = 255; + textureData[i * 4 + 1] = 255; + textureData[i * 4 + 2] = 255; textureData[i * 4 + 3] = pixels[i]; } m_atlas = gfxdev->createTexture(textureData.get(), BITMAP_WIDTH, BITMAP_HEIGHT, gfx::TextureFilter::LINEAR, gfx::TextureFilter::LINEAR); - for (int i = FIRST_CHAR; i < NUM_CHARS; i++) { + for (int i = FIRST_CHAR; i < FIRST_CHAR + NUM_CHARS; i++) { CharData charData{}; - charData.atlas_top_left = { bakedChars[i].x0, bakedChars[i].y0 }; - charData.atlas_bottom_right = { bakedChars[i].x1, bakedChars[i].y1 }; - charData.offset = { bakedChars[i].xoff, bakedChars[i].yoff }; - charData.xAdvance = bakedChars[i].xadvance; + stbtt_bakedchar baked = bakedChars[i - FIRST_CHAR]; + charData.atlas_top_left = { (float)baked.x0 / (float)BITMAP_WIDTH, (float)baked.y0 / (float)BITMAP_HEIGHT }; + charData.atlas_bottom_right = { (float)baked.x1 / (float)BITMAP_WIDTH, (float)baked.y1 / (float)BITMAP_HEIGHT}; + charData.char_top_left = { baked.xoff, baked.yoff }; + charData.char_bottom_right = charData.char_top_left + glm::vec2{ baked.x1 - baked.x0, baked.y1 - baked.y0 }; + charData.xAdvance = baked.xadvance; m_charData[i] = charData; - // TODO } } diff --git a/test/res/shaders/font.vert b/test/res/shaders/font.vert index c4f8735..718b3fa 100644 --- a/test/res/shaders/font.vert +++ b/test/res/shaders/font.vert @@ -2,6 +2,8 @@ layout( push_constant ) uniform Constants { mat4 model; + vec2 atlas_top_left; + vec2 atlas_bottom_right; vec2 offset; vec2 size; } constants; @@ -13,6 +15,8 @@ layout(location = 2) in vec2 inUV; layout(location = 0) out vec2 fragUV; void main() { - gl_Position = constants.model * vec4(inPosition, 1.0); - fragUV = inUV; + vec2 position = inPosition.xy * constants.size + constants.offset; + position *= 0.001; + gl_Position = constants.model * vec4(position, 0.0, 1.0); + fragUV = constants.atlas_top_left + (inUV * (constants.atlas_bottom_right - constants.atlas_top_left)); } \ No newline at end of file diff --git a/test/src/game.cpp b/test/src/game.cpp index 1ddd36a..66c88c7 100644 --- a/test/src/game.cpp +++ b/test/src/game.cpp @@ -58,8 +58,6 @@ void playGame() auto camCamera = cam->createComponent(); camCamera->usePerspective(130.0f); cam->createComponent(); - cam->createComponent()->m_mesh = genSphereMesh(0.2f, 20); - cam->getComponent()->setTexture("textures/cobble_stone.png"); /* auto gun = cam->createChild("gun"); @@ -73,8 +71,6 @@ void playGame() */ // FLOOR - - /* constexpr float GRASS_DENSITY = 128.0f * 20.0f; auto floor = app.scene()->createChild("floor"); auto floorRenderer = floor->createComponent(); @@ -89,15 +85,12 @@ void playGame() { { -16.0f, 0.0f, 16.0f }, { 0.0f, 1.0f, 0.0f }, { 0.0f, GRASS_DENSITY } } }); floor->transform.scale = { 100.0f, 1.0f, 100.0f }; - */ - /* auto cube = app.scene()->createChild("cube"); auto cubeRen = cube->createComponent(); cubeRen->setMesh("meshes/cube.mesh"); cube->transform.position = glm::vec3{ -5.0f, 1.0f, 0.0f }; class Spin : public engine::components::CustomComponent { - public: Spin(engine::Object* parent) : CustomComponent(parent) { @@ -123,32 +116,34 @@ void playGame() pitchQuat.w = glm::cos(halfPitch); parent.transform.rotation *= pitchQuat; } - private: float m_yaw = 0.0f; - }; - app.scene()->getChild("cube")->createComponent(); - */ - - /*auto sphere = app.scene()->createChild("sphere"); - sphere->transform.position = { 10.0f, 5.0f, 10.0f }; - */ - - /*auto sphereRenderer = sphere->createComponent(); - sphereRenderer->m_mesh = genSphereMesh(5.0f, 100, false); - sphereRenderer->setTexture("textures/cobble_stone.png"); + cube->createComponent(); // boundary auto bounds = app.scene()->createChild("bounds"); auto boundsRen = bounds->createComponent(); boundsRen->m_mesh = genSphereMesh(100.0f, 36, true); boundsRen->setTexture("textures/metal.jpg"); - */ auto message = app.scene()->createChild("message"); - message->transform.position = { 0.0f, 2.0f, 0.0f }; + message->transform.position = { -1.0f, 0.95f, 0.0f }; + message->transform.scale *= 0.5f; auto messageUI = message->createComponent(); + class FPSTextUpdater : public engine::components::CustomComponent { + engine::components::UI* textUI = nullptr; + public: + FPSTextUpdater(engine::Object* parent) : CustomComponent(parent) + { + textUI = parent->getComponent(); + } + void onUpdate(glm::mat4 t) override + { + textUI->m_text = std::to_string(parent.win.dt() * 1000.0f) + " ms"; + } + }; + message->createComponent(); /* auto myModel = engine::util::loadAssimpMeshFromFile(app.scene(), app.resources()->getFilePath("models/pyramid/pyramid.dae").string()); @@ -156,17 +151,11 @@ void playGame() auto myRoom = engine::util::loadAssimpMeshFromFile(app.scene(), app.resources()->getFilePath("models/room/room.dae").string()); myRoom->transform.position = { 9.0f, 0.1f, 3.0f }; - + */ auto astronaut = engine::util::loadAssimpMeshFromFile(app.scene(), app.resources()->getFilePath("models/astronaut/astronaut.dae").string()); astronaut->transform.position.z += 5.0f; astronaut->createComponent(); - auto plane = engine::util::loadAssimpMeshFromFile(app.scene(), app.resources()->getFilePath("models/plane/plane.dae").string()); - plane->transform.position = { -30.0f, 2.0f, 10.0f }; - */ - - auto van = engine::util::loadAssimpMeshFromFile(app.scene(), app.resources()->getFilePath("models/van/van.dae").string()); - /* auto lego = engine::util::loadAssimpMeshFromFile(app.scene(), app.resources()->getFilePath("models/lego/lego.dae").string()); lego->transform.position = { -30.0f, -33.0f, -30.0f };