From bcd5609b9b1efef55355cc0c1d0a3c8544f4dd36 Mon Sep 17 00:00:00 2001 From: Joshua Goins Date: Tue, 25 Dec 2018 08:32:38 -0500 Subject: [PATCH] Add actual material editing --- CMakeLists.txt | 3 +- data/basic.material | 4 ++ data/matpreview.world | 2 +- data/test.material | 1 + include/material.h | 1 + include/renderer.h | 8 +++ shaders/mesh.frag | 11 ++- shaders/mesh.vert | 1 + src/assetmanager.cpp | 11 ++- src/renderer.cpp | 92 ++++++++++++++++++++++++- src/worldpass.cpp | 8 ++- tools/common/CMakeLists.txt | 4 +- tools/common/include/coloredit.h | 17 +++++ tools/common/src/coloredit.cpp | 64 +++++++++++++++++ tools/materialeditor/src/mainwindow.cpp | 30 +++++++- 15 files changed, 246 insertions(+), 11 deletions(-) create mode 100644 data/basic.material create mode 100644 tools/common/include/coloredit.h create mode 100644 tools/common/src/coloredit.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 708baa9..ca7ff51 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -126,6 +126,7 @@ add_data(Graph data/empty.world data/player.obj data/sphere.obj - data/matpreview.world) + data/matpreview.world + data/basic.material) add_subdirectory(tools) diff --git a/data/basic.material b/data/basic.material new file mode 100644 index 0000000..e0a73c7 --- /dev/null +++ b/data/basic.material @@ -0,0 +1,4 @@ +{ + "color": "1,1,1", + "albedoTexture": "" +} diff --git a/data/matpreview.world b/data/matpreview.world index cc00009..411300a 100644 --- a/data/matpreview.world +++ b/data/matpreview.world @@ -24,7 +24,7 @@ { "type": "Mesh", "path": "sphere.obj", - "material": "test.material" + "material": "basic.material" } ] }, diff --git a/data/test.material b/data/test.material index 0a16252..f296b2a 100644 --- a/data/test.material +++ b/data/test.material @@ -1,3 +1,4 @@ { + "color": "1,1,1", "albedoTexture": "tile.jpg" } diff --git a/include/material.h b/include/material.h index 6a40c3f..e4b5925 100644 --- a/include/material.h +++ b/include/material.h @@ -4,6 +4,7 @@ #include struct MaterialAsset { + glm::vec3 color = glm::vec3(1); std::string albedoTexturePath; VkImage albedoImage = nullptr; diff --git a/include/renderer.h b/include/renderer.h index 56bbc3c..0c6e457 100644 --- a/include/renderer.h +++ b/include/renderer.h @@ -169,6 +169,7 @@ private: void createPresentationRenderPass(); void createDescriptorPool(); void createMaterialSetLayout(); + void createEmptyMaterialSet(); GraphicsConfig config_; @@ -202,6 +203,13 @@ private: VkDescriptorSetLayout materialSetLayout_ = nullptr; + VkImage emptyImage_ = nullptr; + VkDeviceMemory emptyMemory_ = nullptr; + VkImageView emptyImageView_ = nullptr; + VkSampler emptySampler_ = nullptr; + + VkDescriptorSet emptyMaterialSet_ = nullptr; + WorldPass* worldPass_ = nullptr; PostPass* postPass_ = nullptr; DoFPass* dofPass_ = nullptr; diff --git a/shaders/mesh.frag b/shaders/mesh.frag index 997183c..b6eda8e 100644 --- a/shaders/mesh.frag +++ b/shaders/mesh.frag @@ -7,6 +7,11 @@ layout(location = 3) in vec2 inUV; layout(location = 0) out vec4 outColor; +layout(push_constant) uniform PushConstants { + mat4 m; + vec3 c; +} pushConstants; + struct Light { vec4 position, color; }; @@ -85,5 +90,9 @@ void main() { diffuse += add; } - outColor = vec4(vec3(0.1) + diffuse * texture(albedoSampler, inUV).rgb, 1.0); + vec3 matColor = pushConstants.c; + if(textureSize(albedoSampler, 0).x > 1) + matColor *= texture(albedoSampler, inUV).rgb; + + outColor = vec4((matColor * vec3(0.1)) + diffuse * matColor, 1.0); } diff --git a/shaders/mesh.vert b/shaders/mesh.vert index 7c72059..1eaf65c 100644 --- a/shaders/mesh.vert +++ b/shaders/mesh.vert @@ -15,6 +15,7 @@ layout(set = 0, binding = 0) uniform SceneInfo { layout(push_constant) uniform PushConstants { mat4 m; + vec3 c; } pushConstants; const mat4 biasMat = mat4( diff --git a/src/assetmanager.cpp b/src/assetmanager.cpp index e0b45a0..9bbb163 100644 --- a/src/assetmanager.cpp +++ b/src/assetmanager.cpp @@ -10,6 +10,7 @@ #include "mesh.h" #include "material.h" #include "renderer.h" +#include "stringutils.h" MeshAsset* AssetManager::loadMesh(const std::string& path) { std::string fixedPath = "data/" + path; @@ -57,7 +58,15 @@ MaterialAsset* AssetManager::loadMaterial(const std::string& path) { file >> json; MaterialAsset* material = new MaterialAsset(); - material->albedoTexturePath = "data/" + json["albedoTexture"].get(); + + auto tokens = tokenize(json["color"]); + + material->color[0] = atof(tokens[0].c_str()); + material->color[1] = atof(tokens[1].c_str()); + material->color[2] = atof(tokens[2].c_str()); + + if(json["albedoTexture"].get().length() > 0) + material->albedoTexturePath = "data/" + json["albedoTexture"].get(); renderer->fillMaterialBuffers(material); diff --git a/src/renderer.cpp b/src/renderer.cpp index 4d2235f..36c69ce 100644 --- a/src/renderer.cpp +++ b/src/renderer.cpp @@ -27,6 +27,7 @@ Renderer::Renderer(GraphicsConfig config) : config_(config) { createPresentationRenderPass(); createDescriptorPool(); createMaterialSetLayout(); + createEmptyMaterialSet(); shadowPass_ = new ShadowPass(*this); worldPass_ = new WorldPass(*this); @@ -1233,8 +1234,11 @@ void Renderer::fillMeshBuffers(MeshAsset* mesh) { void Renderer::fillMaterialBuffers(MaterialAsset* material) { int width = 0, height = 0, channels = 0; stbi_uc* pixels = stbi_load(material->albedoTexturePath.c_str(), &width, &height, &channels, STBI_rgb_alpha); - if(pixels == nullptr) - return; // haha what + if(pixels == nullptr) { + material->set = emptyMaterialSet_; + + return; + } VkImageCreateInfo imageCreateInfo = {}; imageCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; @@ -1546,3 +1550,87 @@ void Renderer::createMaterialSetLayout() { vkCreateDescriptorSetLayout(device_, &createInfo, nullptr, &materialSetLayout_); } + +void Renderer::createEmptyMaterialSet() { + VkImageCreateInfo imageCreateInfo = {}; + imageCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; + imageCreateInfo.imageType = VK_IMAGE_TYPE_2D; + imageCreateInfo.extent.width = 1; + imageCreateInfo.extent.height = 1; + imageCreateInfo.extent.depth = 1; + imageCreateInfo.mipLevels = 1; + imageCreateInfo.arrayLayers = 1; + imageCreateInfo.format = VK_FORMAT_R8G8B8A8_UNORM; + imageCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL; + imageCreateInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; + imageCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + imageCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT; + + vkCreateImage(device_, &imageCreateInfo, nullptr, &emptyImage_); + + VkMemoryRequirements memRequirements; + vkGetImageMemoryRequirements(device_, emptyImage_, &memRequirements); + + VkMemoryAllocateInfo allocInfo = {}; + allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + allocInfo.allocationSize = memRequirements.size; + allocInfo.memoryTypeIndex = findMemoryType(memRequirements.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); + + vkAllocateMemory(device_, &allocInfo, nullptr, &emptyMemory_); + vkBindImageMemory(device_, emptyImage_, emptyMemory_, 0); + + char img[4] = {0}; + + uploadImageData( + emptyImage_, + 1, + 1, + 4, + img + ); + + VkImageViewCreateInfo createInfo = {}; + createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + createInfo.image = emptyImage_; + createInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; + createInfo.format = VK_FORMAT_R8G8B8A8_UNORM; + createInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + createInfo.subresourceRange.levelCount = 1; + createInfo.subresourceRange.layerCount = 1; + + vkCreateImageView(device_, &createInfo, nullptr, &emptyImageView_); + + VkSamplerCreateInfo samplerInfo = {}; + samplerInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; + samplerInfo.magFilter = VK_FILTER_LINEAR; + samplerInfo.minFilter = VK_FILTER_LINEAR; + samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER; + samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER; + samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER; + samplerInfo.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK; + samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; + + vkCreateSampler(device_, &samplerInfo, nullptr, &emptySampler_); + + VkDescriptorSetAllocateInfo setAllocInfo = {}; + setAllocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; + setAllocInfo.descriptorPool = descriptorPool_; + setAllocInfo.descriptorSetCount = 1; + setAllocInfo.pSetLayouts = &materialSetLayout_; + + vkAllocateDescriptorSets(device_, &setAllocInfo, &emptyMaterialSet_); + + VkDescriptorImageInfo albedoImageInfo = {}; + albedoImageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + albedoImageInfo.imageView = emptyImageView_; + albedoImageInfo.sampler = emptySampler_; + + VkWriteDescriptorSet albedoDescriptorWrite = {}; + albedoDescriptorWrite.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + albedoDescriptorWrite.descriptorCount = 1; + albedoDescriptorWrite.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + albedoDescriptorWrite.dstSet = emptyMaterialSet_; + albedoDescriptorWrite.pImageInfo = &albedoImageInfo; + + vkUpdateDescriptorSets(device_, 1, &albedoDescriptorWrite, 0, nullptr); +} diff --git a/src/worldpass.cpp b/src/worldpass.cpp index 46e4459..16a4f91 100644 --- a/src/worldpass.cpp +++ b/src/worldpass.cpp @@ -79,7 +79,9 @@ void WorldPass::render(VkCommandBuffer commandBuffer, RenderCollection& collecti glm::mat4 m(1.0f); m = glm::translate(m, mesh.transform->position); - vkCmdPushConstants(commandBuffer, pipelineLayout_, VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(glm::mat4), &m); + vkCmdPushConstants(commandBuffer, pipelineLayout_, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, 0, sizeof(glm::mat4), &m); + + vkCmdPushConstants(commandBuffer, pipelineLayout_, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, sizeof(glm::mat4), sizeof(glm::vec3), &mesh.mesh->material->color); VkDeviceSize offsets[] = {0}; vkCmdBindVertexBuffers(commandBuffer, 0, 1, &mesh.mesh->mesh->vertexBuffer, offsets); @@ -294,8 +296,8 @@ void WorldPass::createPipeline() { dynamicState.pDynamicStates = dynamicStates.data(); VkPushConstantRange mvpPushConstant = {}; - mvpPushConstant.size = sizeof(glm::mat4); - mvpPushConstant.stageFlags = VK_SHADER_STAGE_VERTEX_BIT; + mvpPushConstant.size = sizeof(glm::mat4) + sizeof(glm::vec3); + mvpPushConstant.stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT; const std::array setLayouts = { setLayout_, diff --git a/tools/common/CMakeLists.txt b/tools/common/CMakeLists.txt index 6aff008..1ebf98f 100644 --- a/tools/common/CMakeLists.txt +++ b/tools/common/CMakeLists.txt @@ -1,5 +1,6 @@ set(INCLUDE_FILES - include/renderwindow.h) + include/renderwindow.h + include/coloredit.h) qt5_wrap_cpp(EDITOR_SRC ${INCLUDE_FILES}) @@ -7,6 +8,7 @@ add_library(EditorCommon src/qt.cpp src/renderwindow.cpp src/editorstyle.cpp + src/coloredit.cpp ${EDITOR_SRC}) target_include_directories(EditorCommon PUBLIC include) target_link_libraries(EditorCommon PRIVATE Engine Qt5::Widgets) diff --git a/tools/common/include/coloredit.h b/tools/common/include/coloredit.h new file mode 100644 index 0000000..ac8c6d9 --- /dev/null +++ b/tools/common/include/coloredit.h @@ -0,0 +1,17 @@ +#pragma once + +#include +#include +#include + +class ColorEdit : public QWidget { + Q_OBJECT +public: + ColorEdit(glm::vec3& ref, QWidget* parent = nullptr); + +signals: + void onValueChanged(); + +private: + glm::vec3& reference; +}; diff --git a/tools/common/src/coloredit.cpp b/tools/common/src/coloredit.cpp new file mode 100644 index 0000000..3ae0fa1 --- /dev/null +++ b/tools/common/src/coloredit.cpp @@ -0,0 +1,64 @@ +#include "coloredit.h" + +#include +#include + +QColor fromVec3(glm::vec3 vec) { + int r = 0, g = 0, b = 0; + + if(vec.x != 0) + r = static_cast(255.0f * vec.x); + + if(vec.y != 0) + g = static_cast(255.0f * vec.y); + + if(vec.z != 0) + b = static_cast(255.0f * vec.z); + + return QColor::fromRgb(r, g, b); +} + +glm::vec3 fromQColor(QColor color) { + glm::vec3 vec; + + int r, g, b, a; + color.getRgb(&r, &g, &b, &a); + + vec.x = r / 255.0f; + vec.y = g / 255.0f; + vec.z = b / 255.0f; + + return vec; +} + +ColorEdit::ColorEdit(glm::vec3& ref, QWidget* parent) : QWidget(parent), reference(ref) { + QHBoxLayout* itemsLayout = new QHBoxLayout(this); + + QPushButton* colorButton = new QPushButton(); + colorButton->setFlat(true); + + QPalette pal = colorButton->palette(); + pal.setColor(QPalette::Button, fromVec3(reference)); + + colorButton->setAutoFillBackground(true); + colorButton->setPalette(pal); + colorButton->update(); + + connect(colorButton, &QPushButton::clicked, [=](bool) { + QColor oldcolor = fromVec3(reference); + + QColor newcolor = QColorDialog::getColor(oldcolor); + + reference = fromQColor(newcolor); + + QPalette newpal = colorButton->palette(); + newpal.setColor(QPalette::Button, newcolor); + + colorButton->setPalette(newpal); + colorButton->update(); + + emit onValueChanged(); + }); + + itemsLayout->addWidget(colorButton); +} diff --git a/tools/materialeditor/src/mainwindow.cpp b/tools/materialeditor/src/mainwindow.cpp index b7dff1d..8da324a 100644 --- a/tools/materialeditor/src/mainwindow.cpp +++ b/tools/materialeditor/src/mainwindow.cpp @@ -5,9 +5,16 @@ #include #include #include +#include +#include +#include #include "renderwindow.h" #include "renderer.h" +#include "coloredit.h" +#include "ecs.h" +#include "worldmanager.h" +#include "material.h" MainWindow::MainWindow(Context& context) : context(context) { setWindowTitle("Material Editor"); @@ -56,6 +63,27 @@ MainWindow::MainWindow(Context& context) : context(context) { fileMenu->addAction(quitAction); } + QWidget* centralWidget = new QWidget(); + setCentralWidget(centralWidget); + + QHBoxLayout* layout = new QHBoxLayout(); + centralWidget->setLayout(layout); + + QGridLayout* attributesLayout = new QGridLayout(); + attributesLayout->setAlignment(Qt::AlignTop); + attributesLayout->setSpacing(0); + layout->addLayout(attributesLayout); + + QLabel* label = new QLabel("Color"); + attributesLayout->addWidget(label, 0, 0); + + auto meshComponents = ECS::getWorldComponents(worldManager.getCurrentWorld()); + + auto& [id, mesh] = meshComponents[0]; + + ColorEdit* colorEdit = new ColorEdit(mesh->material->color); + attributesLayout->addWidget(colorEdit, 0, 1); + instance = new QVulkanInstance(); instance->setVkInstance(context.renderer->getInstance()); instance->create(); @@ -64,6 +92,6 @@ MainWindow::MainWindow(Context& context) : context(context) { window->setVulkanInstance(instance); QWidget* wrapper = QWidget::createWindowContainer(window); - setCentralWidget(wrapper); + layout->addWidget(wrapper); }