diff --git a/Editor/Editor.cpp b/Editor/Editor.cpp index af665f162..e71ce210a 100644 --- a/Editor/Editor.cpp +++ b/Editor/Editor.cpp @@ -127,11 +127,11 @@ void EditorComponent::ChangeRenderPath(RENDERPATH path) renderPath->Load(); renderPath->Update(0); - materialWnd = std::make_unique(&GetGUI()); + materialWnd = std::make_unique(this); postprocessWnd = std::make_unique(&GetGUI(), renderPath.get()); weatherWnd = std::make_unique(&GetGUI()); objectWnd = std::make_unique(this); - meshWnd = std::make_unique(&GetGUI()); + meshWnd = std::make_unique(this); cameraWnd = std::make_unique(&GetGUI()); rendererWnd = std::make_unique(&GetGUI(), this, renderPath.get()); envProbeWnd = std::make_unique(&GetGUI()); @@ -1483,6 +1483,7 @@ void EditorComponent::Update(float dt) } else { + meshWnd->SetEntity(picked.entity); materialWnd->SetEntity(picked.entity); } @@ -2071,6 +2072,16 @@ void EditorComponent::Unload() __super::Unload(); } +void EditorComponent::ClearSelected() +{ + translator.selected.clear(); +} +void EditorComponent::AddSelected(wiECS::Entity entity) +{ + wiScene::PickResult res; + res.entity = entity; + AddSelected(res); +} void EditorComponent::AddSelected(const wiScene::PickResult& picked) { for (auto it = translator.selected.begin(); it != translator.selected.end(); ++it) diff --git a/Editor/Editor.h b/Editor/Editor.h index d364ae249..28992b852 100644 --- a/Editor/Editor.h +++ b/Editor/Editor.h @@ -100,6 +100,8 @@ public: Translator translator; wiScene::PickResult hovered; + void ClearSelected(); + void AddSelected(wiECS::Entity entity); void AddSelected(const wiScene::PickResult& picked); bool IsSelected(wiECS::Entity entity) const; diff --git a/Editor/MaterialWindow.cpp b/Editor/MaterialWindow.cpp index 8b2ea338f..4dada4672 100644 --- a/Editor/MaterialWindow.cpp +++ b/Editor/MaterialWindow.cpp @@ -1,5 +1,6 @@ #include "stdafx.h" #include "MaterialWindow.h" +#include "Editor.h" #include @@ -8,7 +9,7 @@ using namespace wiGraphics; using namespace wiECS; using namespace wiScene; -MaterialWindow::MaterialWindow(wiGUI* gui) : GUI(gui) +MaterialWindow::MaterialWindow(EditorComponent* editor) : GUI(&editor->GetGUI()) { assert(GUI && "Invalid GUI!"); @@ -364,9 +365,21 @@ MaterialWindow::MaterialWindow(wiGUI* gui) : GUI(gui) { *name = args.sValue; } - }); + }); materialWindow->AddWidget(materialNameField); + newMaterialButton = new wiButton("New Material"); + newMaterialButton->SetPos(XMFLOAT2(10 + 5 + 300, y)); + newMaterialButton->SetSize(XMFLOAT2(100, 25)); + newMaterialButton->OnClick([=](wiEventArgs args) { + Scene& scene = wiScene::GetScene(); + Entity entity = scene.Entity_CreateMaterial("editorMaterial"); + editor->ClearSelected(); + editor->AddSelected(entity); + SetEntity(entity); + }); + materialWindow->AddWidget(newMaterialButton); + texture_baseColor_Label = new wiLabel("BaseColorMap: "); texture_baseColor_Label->SetPos(XMFLOAT2(x, y += step)); texture_baseColor_Label->SetSize(XMFLOAT2(120, 20)); @@ -846,4 +859,6 @@ void MaterialWindow::SetEntity(Entity entity) texture_emissive_uvset_Field->SetText(""); texture_occlusion_uvset_Field->SetText(""); } + + newMaterialButton->SetEnabled(true); } diff --git a/Editor/MaterialWindow.h b/Editor/MaterialWindow.h index 002395df7..687f2eee1 100644 --- a/Editor/MaterialWindow.h +++ b/Editor/MaterialWindow.h @@ -10,10 +10,12 @@ class wiButton; class wiComboBox; class wiTextInputField; +class EditorComponent; + class MaterialWindow { public: - MaterialWindow(wiGUI* gui); + MaterialWindow(EditorComponent* editor); ~MaterialWindow(); wiECS::Entity entity; @@ -23,6 +25,7 @@ public: wiWindow* materialWindow; wiTextInputField* materialNameField; + wiButton* newMaterialButton; wiCheckBox* waterCheckBox; wiCheckBox* planarReflCheckBox; wiCheckBox* shadowCasterCheckBox; diff --git a/Editor/MeshWindow.cpp b/Editor/MeshWindow.cpp index bf3f5d884..e6ef6d71b 100644 --- a/Editor/MeshWindow.cpp +++ b/Editor/MeshWindow.cpp @@ -1,5 +1,8 @@ #include "stdafx.h" #include "MeshWindow.h" +#include "Editor.h" + +#include "Utility/stb_image.h" #include @@ -7,22 +10,23 @@ using namespace std; using namespace wiECS; using namespace wiScene; -MeshWindow::MeshWindow(wiGUI* gui) : GUI(gui) +MeshWindow::MeshWindow(EditorComponent* editor) : GUI(&editor->GetGUI()) { assert(GUI && "Invalid GUI!"); meshWindow = new wiWindow(GUI, "Mesh Window"); - meshWindow->SetSize(XMFLOAT2(800, 700)); + meshWindow->SetSize(XMFLOAT2(600, 620)); GUI->AddWidget(meshWindow); - float x = 200; + float x = 150; float y = 0; - float step = 35; + float step = 30; + float hei = 25; meshInfoLabel = new wiLabel("Mesh Info"); - meshInfoLabel->SetPos(XMFLOAT2(x, y += step)); - meshInfoLabel->SetSize(XMFLOAT2(400, 150)); + meshInfoLabel->SetPos(XMFLOAT2(x - 50, y += step)); + meshInfoLabel->SetSize(XMFLOAT2(450, 150)); meshWindow->AddWidget(meshInfoLabel); y += 160; @@ -69,7 +73,7 @@ MeshWindow::MeshWindow(wiGUI* gui) : GUI(gui) massSlider = new wiSlider(0, 10, 0, 100000, "Mass: "); massSlider->SetTooltip("Set the mass amount for the physics engine."); - massSlider->SetSize(XMFLOAT2(100, 30)); + massSlider->SetSize(XMFLOAT2(100, hei)); massSlider->SetPos(XMFLOAT2(x, y += step)); massSlider->OnSlide([&](wiEventArgs args) { SoftBodyPhysicsComponent* physicscomponent = wiScene::GetScene().softbodies.GetComponent(entity); @@ -82,7 +86,7 @@ MeshWindow::MeshWindow(wiGUI* gui) : GUI(gui) frictionSlider = new wiSlider(0, 2, 0, 100000, "Friction: "); frictionSlider->SetTooltip("Set the friction amount for the physics engine."); - frictionSlider->SetSize(XMFLOAT2(100, 30)); + frictionSlider->SetSize(XMFLOAT2(100, hei)); frictionSlider->SetPos(XMFLOAT2(x, y += step)); frictionSlider->OnSlide([&](wiEventArgs args) { SoftBodyPhysicsComponent* physicscomponent = wiScene::GetScene().softbodies.GetComponent(entity); @@ -95,7 +99,7 @@ MeshWindow::MeshWindow(wiGUI* gui) : GUI(gui) impostorCreateButton = new wiButton("Create Impostor"); impostorCreateButton->SetTooltip("Create an impostor image of the mesh. The mesh will be replaced by this image when far away, to render faster."); - impostorCreateButton->SetSize(XMFLOAT2(240, 30)); + impostorCreateButton->SetSize(XMFLOAT2(240, hei)); impostorCreateButton->SetPos(XMFLOAT2(x - 50, y += step)); impostorCreateButton->OnClick([&](wiEventArgs args) { MeshComponent* mesh = wiScene::GetScene().meshes.GetComponent(entity); @@ -109,7 +113,7 @@ MeshWindow::MeshWindow(wiGUI* gui) : GUI(gui) impostorDistanceSlider = new wiSlider(0, 1000, 100, 10000, "Impostor Distance: "); impostorDistanceSlider->SetTooltip("Assign the distance where the mesh geometry should be switched to the impostor image."); - impostorDistanceSlider->SetSize(XMFLOAT2(100, 30)); + impostorDistanceSlider->SetSize(XMFLOAT2(100, hei)); impostorDistanceSlider->SetPos(XMFLOAT2(x, y += step)); impostorDistanceSlider->OnSlide([&](wiEventArgs args) { ImpostorComponent* impostor = wiScene::GetScene().impostors.GetComponent(entity); @@ -122,7 +126,7 @@ MeshWindow::MeshWindow(wiGUI* gui) : GUI(gui) tessellationFactorSlider = new wiSlider(0, 16, 0, 10000, "Tessellation Factor: "); tessellationFactorSlider->SetTooltip("Set the dynamic tessellation amount. Tessellation should be enabled in the Renderer window and your GPU must support it!"); - tessellationFactorSlider->SetSize(XMFLOAT2(100, 30)); + tessellationFactorSlider->SetSize(XMFLOAT2(100, hei)); tessellationFactorSlider->SetPos(XMFLOAT2(x, y += step)); tessellationFactorSlider->OnSlide([&](wiEventArgs args) { MeshComponent* mesh = wiScene::GetScene().meshes.GetComponent(entity); @@ -135,7 +139,7 @@ MeshWindow::MeshWindow(wiGUI* gui) : GUI(gui) flipCullingButton = new wiButton("Flip Culling"); flipCullingButton->SetTooltip("Flip faces to reverse triangle culling order."); - flipCullingButton->SetSize(XMFLOAT2(240, 30)); + flipCullingButton->SetSize(XMFLOAT2(240, hei)); flipCullingButton->SetPos(XMFLOAT2(x - 50, y += step)); flipCullingButton->OnClick([&](wiEventArgs args) { MeshComponent* mesh = wiScene::GetScene().meshes.GetComponent(entity); @@ -149,7 +153,7 @@ MeshWindow::MeshWindow(wiGUI* gui) : GUI(gui) flipNormalsButton = new wiButton("Flip Normals"); flipNormalsButton->SetTooltip("Flip surface normals."); - flipNormalsButton->SetSize(XMFLOAT2(240, 30)); + flipNormalsButton->SetSize(XMFLOAT2(240, hei)); flipNormalsButton->SetPos(XMFLOAT2(x - 50, y += step)); flipNormalsButton->OnClick([&](wiEventArgs args) { MeshComponent* mesh = wiScene::GetScene().meshes.GetComponent(entity); @@ -163,7 +167,7 @@ MeshWindow::MeshWindow(wiGUI* gui) : GUI(gui) computeNormalsSmoothButton = new wiButton("Compute Normals [SMOOTH]"); computeNormalsSmoothButton->SetTooltip("Compute surface normals of the mesh. Resulting normals will be unique per vertex."); - computeNormalsSmoothButton->SetSize(XMFLOAT2(240, 30)); + computeNormalsSmoothButton->SetSize(XMFLOAT2(240, hei)); computeNormalsSmoothButton->SetPos(XMFLOAT2(x - 50, y += step)); computeNormalsSmoothButton->OnClick([&](wiEventArgs args) { MeshComponent* mesh = wiScene::GetScene().meshes.GetComponent(entity); @@ -177,7 +181,7 @@ MeshWindow::MeshWindow(wiGUI* gui) : GUI(gui) computeNormalsHardButton = new wiButton("Compute Normals [HARD]"); computeNormalsHardButton->SetTooltip("Compute surface normals of the mesh. Resulting normals will be unique per face."); - computeNormalsHardButton->SetSize(XMFLOAT2(240, 30)); + computeNormalsHardButton->SetSize(XMFLOAT2(240, hei)); computeNormalsHardButton->SetPos(XMFLOAT2(x - 50, y += step)); computeNormalsHardButton->OnClick([&](wiEventArgs args) { MeshComponent* mesh = wiScene::GetScene().meshes.GetComponent(entity); @@ -191,7 +195,7 @@ MeshWindow::MeshWindow(wiGUI* gui) : GUI(gui) recenterButton = new wiButton("Recenter"); recenterButton->SetTooltip("Recenter mesh to AABB center."); - recenterButton->SetSize(XMFLOAT2(240, 30)); + recenterButton->SetSize(XMFLOAT2(240, hei)); recenterButton->SetPos(XMFLOAT2(x - 50, y += step)); recenterButton->OnClick([&](wiEventArgs args) { MeshComponent* mesh = wiScene::GetScene().meshes.GetComponent(entity); @@ -205,7 +209,7 @@ MeshWindow::MeshWindow(wiGUI* gui) : GUI(gui) recenterToBottomButton = new wiButton("RecenterToBottom"); recenterToBottomButton->SetTooltip("Recenter mesh to AABB bottom."); - recenterToBottomButton->SetSize(XMFLOAT2(240, 30)); + recenterToBottomButton->SetSize(XMFLOAT2(240, hei)); recenterToBottomButton->SetPos(XMFLOAT2(x - 50, y += step)); recenterToBottomButton->OnClick([&](wiEventArgs args) { MeshComponent* mesh = wiScene::GetScene().meshes.GetComponent(entity); @@ -217,10 +221,212 @@ MeshWindow::MeshWindow(wiGUI* gui) : GUI(gui) }); meshWindow->AddWidget(recenterToBottomButton); + x = 150; + y = 160; + + terrainCheckBox = new wiCheckBox("Terrain: "); + terrainCheckBox->SetTooltip("If enabled, the mesh will use multiple materials and blend between them based on vertex colors."); + terrainCheckBox->SetPos(XMFLOAT2(x, y += step)); + terrainCheckBox->OnClick([&](wiEventArgs args) { + MeshComponent* mesh = wiScene::GetScene().meshes.GetComponent(entity); + if (mesh != nullptr) + { + mesh->SetTerrain(args.bValue); + if (args.bValue && mesh->vertex_colors.empty()) + { + mesh->vertex_colors.resize(mesh->vertex_positions.size()); + std::fill(mesh->vertex_colors.begin(), mesh->vertex_colors.end(), wiColor::Red().rgba); // fill red (meaning only blend base material) + mesh->CreateRenderData(); + + for (auto& subset : mesh->subsets) + { + MaterialComponent* material = wiScene::GetScene().materials.GetComponent(subset.materialID); + if (material != nullptr) + { + material->SetUseVertexColors(true); + } + } + } + SetEntity(entity); // refresh information label + } + }); + meshWindow->AddWidget(terrainCheckBox); + + terrainMat1Combo = new wiComboBox("Terrain Material 1: "); + terrainMat1Combo->SetSize(XMFLOAT2(200, 20)); + terrainMat1Combo->SetPos(XMFLOAT2(x + 180, y)); + terrainMat1Combo->SetEnabled(false); + terrainMat1Combo->OnSelect([&](wiEventArgs args) { + MeshComponent* mesh = wiScene::GetScene().meshes.GetComponent(entity); + if (mesh != nullptr) + { + if (args.iValue == 0) + { + mesh->terrain_material1 = INVALID_ENTITY; + } + else + { + Scene& scene = wiScene::GetScene(); + mesh->terrain_material1 = scene.materials.GetEntity(args.iValue - 1); + } + } + }); + terrainMat1Combo->SetTooltip("Choose a sub terrain blend material."); + meshWindow->AddWidget(terrainMat1Combo); + + terrainMat2Combo = new wiComboBox("Terrain Material 2: "); + terrainMat2Combo->SetSize(XMFLOAT2(200, 20)); + terrainMat2Combo->SetPos(XMFLOAT2(x + 180, y += step)); + terrainMat2Combo->SetEnabled(false); + terrainMat2Combo->OnSelect([&](wiEventArgs args) { + MeshComponent* mesh = wiScene::GetScene().meshes.GetComponent(entity); + if (mesh != nullptr) + { + if (args.iValue == 0) + { + mesh->terrain_material2 = INVALID_ENTITY; + } + else + { + Scene& scene = wiScene::GetScene(); + mesh->terrain_material2 = scene.materials.GetEntity(args.iValue - 1); + } + } + }); + terrainMat2Combo->SetTooltip("Choose a sub terrain blend material."); + meshWindow->AddWidget(terrainMat2Combo); + + terrainMat3Combo = new wiComboBox("Terrain Material 3: "); + terrainMat3Combo->SetSize(XMFLOAT2(200, 20)); + terrainMat3Combo->SetPos(XMFLOAT2(x + 180, y += step)); + terrainMat3Combo->SetEnabled(false); + terrainMat3Combo->OnSelect([&](wiEventArgs args) { + MeshComponent* mesh = wiScene::GetScene().meshes.GetComponent(entity); + if (mesh != nullptr) + { + if (args.iValue == 0) + { + mesh->terrain_material3 = INVALID_ENTITY; + } + else + { + Scene& scene = wiScene::GetScene(); + mesh->terrain_material3 = scene.materials.GetEntity(args.iValue - 1); + } + } + }); + terrainMat3Combo->SetTooltip("Choose a sub terrain blend material."); + meshWindow->AddWidget(terrainMat3Combo); + + terrainFromHeightMapButton = new wiButton("Create Terrain From Heightmap"); + terrainFromHeightMapButton->SetTooltip("Load a heightmap texture, where the red channel corresponds to terrain height and the resolution to dimensions"); + terrainFromHeightMapButton->SetSize(XMFLOAT2(200, 20)); + terrainFromHeightMapButton->SetPos(XMFLOAT2(x + 180, y += step)); + terrainFromHeightMapButton->OnClick([=](wiEventArgs args) { + + wiHelper::FileDialogParams params; + wiHelper::FileDialogResult result; + params.type = wiHelper::FileDialogParams::OPEN; + params.description = "Texture"; + params.extensions.push_back("dds"); + params.extensions.push_back("png"); + params.extensions.push_back("jpg"); + params.extensions.push_back("tga"); + wiHelper::FileDialog(params, result); + + if (result.ok) { + string fileName = result.filenames.front(); + + Scene& scene = wiScene::GetScene(); + Entity entity = scene.Entity_CreateObject("editorTerrain"); + ObjectComponent& object = *scene.objects.GetComponent(entity); + object.meshID = scene.Entity_CreateMesh("terrainMesh"); + MeshComponent& mesh = *scene.meshes.GetComponent(object.meshID); + + const int channelCount = 4; + int width, height, bpp; + unsigned char* rgb = stbi_load(fileName.c_str(), &width, &height, &bpp, channelCount); + + mesh.vertex_positions.resize(width * height); + mesh.vertex_normals.resize(width * height); + mesh.vertex_colors.resize(width * height); + for (int i = 0; i < width; ++i) + { + for (int j = 0; j < height; ++j) + { + size_t index = size_t(i + j * width); + mesh.vertex_positions[index] = XMFLOAT3((float)i - (float)width * 0.5f, (float)rgb[index * channelCount] - 127.0f, (float)j - (float)height * 0.5f); + mesh.vertex_colors[index] = wiColor::Red().rgba; + } + } + mesh.indices.resize((width - 1) * (height - 1) * 6); + size_t counter = 0; + for (int x = 0; x < width - 1; x++) + { + for (int y = 0; y < height - 1; y++) + { + int lowerLeft = x + y * width; + int lowerRight = (x + 1) + y * width; + int topLeft = x + (y + 1) * width; + int topRight = (x + 1) + (y + 1) * width; + + mesh.indices[counter++] = topLeft; + mesh.indices[counter++] = lowerLeft; + mesh.indices[counter++] = lowerRight; + + mesh.indices[counter++] = topLeft; + mesh.indices[counter++] = lowerRight; + mesh.indices[counter++] = topRight; + } + } + for (size_t i = 0; i < mesh.indices.size() / 3; ++i) + { + uint32_t index1 = mesh.indices[i * 3]; + uint32_t index2 = mesh.indices[i * 3 + 1]; + uint32_t index3 = mesh.indices[i * 3 + 2]; + + XMVECTOR side1 = XMLoadFloat3(&mesh.vertex_positions[index1]) - XMLoadFloat3(&mesh.vertex_positions[index3]); + XMVECTOR side2 = XMLoadFloat3(&mesh.vertex_positions[index1]) - XMLoadFloat3(&mesh.vertex_positions[index2]); + XMVECTOR N = XMVector3Normalize(XMVector3Cross(side1, side2)); + XMFLOAT3 normal; + XMStoreFloat3(&normal, N); + + mesh.vertex_normals[index1].x += normal.x; + mesh.vertex_normals[index1].y += normal.y; + mesh.vertex_normals[index1].z += normal.z; + + mesh.vertex_normals[index2].x += normal.x; + mesh.vertex_normals[index2].y += normal.y; + mesh.vertex_normals[index2].z += normal.z; + + mesh.vertex_normals[index3].x += normal.x; + mesh.vertex_normals[index3].y += normal.y; + mesh.vertex_normals[index3].z += normal.z; + } + for (auto& normal : mesh.vertex_normals) + { + XMStoreFloat3(&normal, XMVector3Normalize(XMLoadFloat3(&normal))); + } + mesh.subsets.emplace_back(); + mesh.subsets.back().materialID = scene.Entity_CreateMaterial("terrainMaterial"); + mesh.subsets.back().indexOffset = 0; + mesh.subsets.back().indexCount = (uint32_t)mesh.indices.size(); + + mesh.CreateRenderData(); + + editor->ClearSelected(); + wiScene::PickResult pick; + pick.entity = entity; + pick.subsetIndex = 0; + editor->AddSelected(pick); + SetEntity(entity); + } + }); + meshWindow->AddWidget(terrainFromHeightMapButton); - meshWindow->Translate(XMFLOAT3((float)wiRenderer::GetDevice()->GetScreenWidth() - 910, 520, 0)); + meshWindow->Translate(XMFLOAT3((float)wiRenderer::GetDevice()->GetScreenWidth() - 1000, 80, 0)); meshWindow->SetVisible(false); SetEntity(INVALID_ENTITY); @@ -260,8 +466,39 @@ void MeshWindow::SetEntity(Entity entity) if (mesh->vertexBuffer_PRE.IsValid()) ss << "prevPos; "; if (mesh->vertexBuffer_BON.IsValid()) ss << "bone; "; if (mesh->streamoutBuffer_POS.IsValid()) ss << "streamout; "; + if (mesh->IsTerrain()) ss << endl << endl << "Terrain will use 4 blend materials and blend by vertex colors, the default one is always the subset material, the other 3 are selectable below."; meshInfoLabel->SetText(ss.str()); + terrainCheckBox->SetCheck(mesh->IsTerrain()); + + terrainMat1Combo->ClearItems(); + terrainMat1Combo->AddItem("OFF (Use subset)"); + terrainMat2Combo->ClearItems(); + terrainMat2Combo->AddItem("OFF (Use subset)"); + terrainMat3Combo->ClearItems(); + terrainMat3Combo->AddItem("OFF (Use subset)"); + for (size_t i = 0; i < scene.materials.GetCount(); ++i) + { + Entity entity = scene.materials.GetEntity(i); + const NameComponent& name = *scene.names.GetComponent(entity); + terrainMat1Combo->AddItem(name.name); + terrainMat2Combo->AddItem(name.name); + terrainMat3Combo->AddItem(name.name); + + if (mesh->terrain_material1 == entity) + { + terrainMat1Combo->SetSelected((int)i + 1); + } + if (mesh->terrain_material2 == entity) + { + terrainMat2Combo->SetSelected((int)i + 1); + } + if (mesh->terrain_material3 == entity) + { + terrainMat3Combo->SetSelected((int)i + 1); + } + } + doubleSidedCheckBox->SetCheck(mesh->IsDoubleSided()); const ImpostorComponent* impostor = scene.impostors.GetComponent(entity); @@ -287,4 +524,6 @@ void MeshWindow::SetEntity(Entity entity) meshInfoLabel->SetText("Select a mesh..."); meshWindow->SetEnabled(false); } + + terrainFromHeightMapButton->SetEnabled(true); } diff --git a/Editor/MeshWindow.h b/Editor/MeshWindow.h index c364f57c5..7d67e9c57 100644 --- a/Editor/MeshWindow.h +++ b/Editor/MeshWindow.h @@ -6,11 +6,14 @@ class wiLabel; class wiCheckBox; class wiSlider; class wiButton; +class wiComboBox; + +class EditorComponent; class MeshWindow { public: - MeshWindow(wiGUI* gui); + MeshWindow(EditorComponent* editor); ~MeshWindow(); wiGUI* GUI; @@ -33,5 +36,11 @@ public: wiButton* computeNormalsHardButton; wiButton* recenterButton; wiButton* recenterToBottomButton; + + wiCheckBox* terrainCheckBox; + wiComboBox* terrainMat1Combo; + wiComboBox* terrainMat2Combo; + wiComboBox* terrainMat3Combo; + wiButton* terrainFromHeightMapButton; }; diff --git a/WickedEngine/ArchiveVersionHistory.txt b/WickedEngine/ArchiveVersionHistory.txt index 2bd6265e3..81e5fdc9f 100644 --- a/WickedEngine/ArchiveVersionHistory.txt +++ b/WickedEngine/ArchiveVersionHistory.txt @@ -1,5 +1,6 @@ This file contains changelog of wiArchive versions +41: Serialized terrain blend materials in MeshComponent 40: Serialized WeatherComponent::windSpeed 39: Serialized indices and weights for Hair Particle 38: Serialized SpringComponent diff --git a/WickedEngine/ConstantBufferMapping.h b/WickedEngine/ConstantBufferMapping.h index 8936a55a0..750510524 100644 --- a/WickedEngine/ConstantBufferMapping.h +++ b/WickedEngine/ConstantBufferMapping.h @@ -40,6 +40,9 @@ #define CBSLOT_OTHER_OCEAN_RENDER 7 #define CBSLOT_OTHER_CLOUDGENERATOR 7 #define CBSLOT_OTHER_GPUSORTLIB 8 +#define CBSLOT_RENDERER_MATERIAL_BLEND1 8 +#define CBSLOT_RENDERER_MATERIAL_BLEND2 9 +#define CBSLOT_RENDERER_MATERIAL_BLEND3 10 diff --git a/WickedEngine/ResourceMapping.h b/WickedEngine/ResourceMapping.h index 52a3b9948..b2eec14df 100644 --- a/WickedEngine/ResourceMapping.h +++ b/WickedEngine/ResourceMapping.h @@ -44,11 +44,21 @@ #define TEXSLOT_ONDEMAND7 27 #define TEXSLOT_ONDEMAND8 28 #define TEXSLOT_ONDEMAND9 29 -#define TEXSLOT_ONDEMAND_COUNT (TEXSLOT_ONDEMAND9 - TEXSLOT_ONDEMAND0 + 1) +#define TEXSLOT_ONDEMAND10 30 +#define TEXSLOT_ONDEMAND11 31 +#define TEXSLOT_ONDEMAND12 32 +#define TEXSLOT_ONDEMAND13 33 +#define TEXSLOT_ONDEMAND14 34 +#define TEXSLOT_ONDEMAND15 35 +#define TEXSLOT_ONDEMAND16 36 +#define TEXSLOT_ONDEMAND17 37 +#define TEXSLOT_ONDEMAND18 38 +#define TEXSLOT_ONDEMAND19 39 +#define TEXSLOT_ONDEMAND_COUNT (TEXSLOT_ONDEMAND19 - TEXSLOT_ONDEMAND0 + 1) // These are reserved for demand of any type of textures in specific shaders: -#define TEXSLOT_UNIQUE0 30 -#define TEXSLOT_UNIQUE1 31 +#define TEXSLOT_UNIQUE0 40 +#define TEXSLOT_UNIQUE1 41 #define TEXSLOT_COUNT TEXSLOT_UNIQUE1 @@ -66,6 +76,18 @@ #define TEXSLOT_RENDERER_EMISSIVEMAP TEXSLOT_ONDEMAND4 #define TEXSLOT_RENDERER_OCCLUSIONMAP TEXSLOT_ONDEMAND5 +#define TEXSLOT_RENDERER_BLEND1_BASECOLORMAP TEXSLOT_ONDEMAND10 +#define TEXSLOT_RENDERER_BLEND1_NORMALMAP TEXSLOT_ONDEMAND11 +#define TEXSLOT_RENDERER_BLEND1_SURFACEMAP TEXSLOT_ONDEMAND12 + +#define TEXSLOT_RENDERER_BLEND2_BASECOLORMAP TEXSLOT_ONDEMAND13 +#define TEXSLOT_RENDERER_BLEND2_NORMALMAP TEXSLOT_ONDEMAND14 +#define TEXSLOT_RENDERER_BLEND2_SURFACEMAP TEXSLOT_ONDEMAND15 + +#define TEXSLOT_RENDERER_BLEND3_BASECOLORMAP TEXSLOT_ONDEMAND16 +#define TEXSLOT_RENDERER_BLEND3_NORMALMAP TEXSLOT_ONDEMAND17 +#define TEXSLOT_RENDERER_BLEND3_SURFACEMAP TEXSLOT_ONDEMAND18 + // RenderPath texture mappings: #define TEXSLOT_RENDERPATH_REFLECTION TEXSLOT_ONDEMAND6 #define TEXSLOT_RENDERPATH_REFRACTION TEXSLOT_ONDEMAND7 diff --git a/WickedEngine/ShaderInterop_Renderer.h b/WickedEngine/ShaderInterop_Renderer.h index f66ff80e1..156b2aeeb 100644 --- a/WickedEngine/ShaderInterop_Renderer.h +++ b/WickedEngine/ShaderInterop_Renderer.h @@ -289,6 +289,18 @@ CBUFFER(MaterialCB, CBSLOT_RENDERER_MATERIAL) { ShaderMaterial g_xMaterial; }; +CBUFFER(MaterialCB_Blend1, CBSLOT_RENDERER_MATERIAL_BLEND1) +{ + ShaderMaterial g_xMaterial_blend1; +}; +CBUFFER(MaterialCB_Blend2, CBSLOT_RENDERER_MATERIAL_BLEND2) +{ + ShaderMaterial g_xMaterial_blend2; +}; +CBUFFER(MaterialCB_Blend3, CBSLOT_RENDERER_MATERIAL_BLEND3) +{ + ShaderMaterial g_xMaterial_blend3; +}; CBUFFER(MiscCB, CBSLOT_RENDERER_MISC) { diff --git a/WickedEngine/WickedEngine_SHADERS.vcxproj b/WickedEngine/WickedEngine_SHADERS.vcxproj index 5d505aa6b..e2ebb95e5 100644 --- a/WickedEngine/WickedEngine_SHADERS.vcxproj +++ b/WickedEngine/WickedEngine_SHADERS.vcxproj @@ -244,6 +244,9 @@ Compute 5.0 + + Pixel + Compute 5.0 @@ -493,12 +496,24 @@ Pixel + + Pixel + + + Pixel + Pixel Pixel + + Pixel + + + Pixel + Pixel diff --git a/WickedEngine/WickedEngine_SHADERS.vcxproj.filters b/WickedEngine/WickedEngine_SHADERS.vcxproj.filters index 6489a8464..e16e03364 100644 --- a/WickedEngine/WickedEngine_SHADERS.vcxproj.filters +++ b/WickedEngine/WickedEngine_SHADERS.vcxproj.filters @@ -942,6 +942,21 @@ PS + + PS + + + PS + + + PS + + + PS + + + PS + diff --git a/WickedEngine/envMapPS_terrain.hlsl b/WickedEngine/envMapPS_terrain.hlsl new file mode 100644 index 000000000..095b21cc0 --- /dev/null +++ b/WickedEngine/envMapPS_terrain.hlsl @@ -0,0 +1,2 @@ +#define TERRAIN +#include "envMapPS.hlsl" diff --git a/WickedEngine/objectHF.hlsli b/WickedEngine/objectHF.hlsli index 22f98686f..d03002ea2 100644 --- a/WickedEngine/objectHF.hlsli +++ b/WickedEngine/objectHF.hlsli @@ -5,6 +5,10 @@ #define DISABLE_ALPHATEST #endif +#ifdef TERRAIN +#define DISABLE_ALPHATEST +#endif + #ifdef TRANSPARENT #define DISABLE_TRANSPARENT_SHADOWMAP #endif @@ -34,6 +38,18 @@ TEXTURE2D(texture_displacementmap, float, TEXSLOT_RENDERER_DISPLACEMENTMAP); // TEXTURE2D(texture_emissivemap, float4, TEXSLOT_RENDERER_EMISSIVEMAP); // rgba: emissive TEXTURE2D(texture_occlusionmap, float, TEXSLOT_RENDERER_OCCLUSIONMAP); // r: occlusion +TEXTURE2D(texture_blend1_basecolormap, float4, TEXSLOT_RENDERER_BLEND1_BASECOLORMAP); // rgb: baseColor, a: opacity +TEXTURE2D(texture_blend1_normalmap, float3, TEXSLOT_RENDERER_BLEND1_NORMALMAP); // rgb: normal +TEXTURE2D(texture_blend1_surfacemap, float4, TEXSLOT_RENDERER_BLEND1_SURFACEMAP); // r: occlusion, g: roughness, b: metallic, a: reflectance + +TEXTURE2D(texture_blend2_basecolormap, float4, TEXSLOT_RENDERER_BLEND2_BASECOLORMAP); // rgb: baseColor, a: opacity +TEXTURE2D(texture_blend2_normalmap, float3, TEXSLOT_RENDERER_BLEND2_NORMALMAP); // rgb: normal +TEXTURE2D(texture_blend2_surfacemap, float4, TEXSLOT_RENDERER_BLEND2_SURFACEMAP); // r: occlusion, g: roughness, b: metallic, a: reflectance + +TEXTURE2D(texture_blend3_basecolormap, float4, TEXSLOT_RENDERER_BLEND3_BASECOLORMAP); // rgb: baseColor, a: opacity +TEXTURE2D(texture_blend3_normalmap, float3, TEXSLOT_RENDERER_BLEND3_NORMALMAP); // rgb: normal +TEXTURE2D(texture_blend3_surfacemap, float4, TEXSLOT_RENDERER_BLEND3_SURFACEMAP); // r: occlusion, g: roughness, b: metallic, a: reflectance + // These are bound by RenderPath (based on Render Path): TEXTURE2D(texture_reflection, float4, TEXSLOT_RENDERPATH_REFLECTION); // rgba: scene color from reflected camera angle TEXTURE2D(texture_refraction, float4, TEXSLOT_RENDERPATH_REFRACTION); // rgba: scene color from primary camera angle @@ -671,6 +687,7 @@ inline void ApplyFog(in float dist, inout float4 color) // POM - include parallax occlusion mapping computation // WATER - include specialized water shader code // BLACKOUT - include specialized blackout shader code +// TERRAIN - include specialized terrain material blending code #if defined(ALPHATESTONLY) || defined(TEXTUREONLY) #define SIMPLE_INPUT @@ -788,6 +805,132 @@ GBUFFEROutputType_Thin main(PIXELINPUT input) { surface_occlusion_roughness_metallic_reflectance = 1; } + surface_occlusion_roughness_metallic_reflectance *= float4(1, g_xMaterial.roughness, g_xMaterial.metalness, g_xMaterial.reflectance); + +#ifdef TERRAIN + color = 0; + surface_occlusion_roughness_metallic_reflectance = 0; + float3 triplanar_normal = 0; + float4 blend_weights = input.color; + blend_weights /= blend_weights.x + blend_weights.y + blend_weights.z + blend_weights.w; + float3 triplanar = abs(surface.N); + triplanar /= triplanar.x + triplanar.y + triplanar.z; + float4 sam_x, sam_y, sam_z; + float2 uv_x, uv_y, uv_z; + + [branch] + if (blend_weights.x > 0) + { + uv_x = surface.P.yz * g_xMaterial.texMulAdd.xy + g_xMaterial.texMulAdd.zw; + uv_y = surface.P.xz * g_xMaterial.texMulAdd.xy + g_xMaterial.texMulAdd.zw; + uv_z = surface.P.xy * g_xMaterial.texMulAdd.xy + g_xMaterial.texMulAdd.zw; + sam_x = texture_basecolormap.Sample(sampler_objectshader, uv_x); + sam_y = texture_basecolormap.Sample(sampler_objectshader, uv_y); + sam_z = texture_basecolormap.Sample(sampler_objectshader, uv_z); + sam_x.rgb = DEGAMMA(sam_x.rgb); + sam_y.rgb = DEGAMMA(sam_y.rgb); + sam_z.rgb = DEGAMMA(sam_z.rgb); + color += (sam_x * triplanar.x + sam_y * triplanar.y + sam_z * triplanar.z) * g_xMaterial.baseColor * blend_weights.x; + sam_x = texture_surfacemap.Sample(sampler_objectshader, uv_x); + sam_y = texture_surfacemap.Sample(sampler_objectshader, uv_y); + sam_z = texture_surfacemap.Sample(sampler_objectshader, uv_z); + surface_occlusion_roughness_metallic_reflectance += (sam_x * triplanar.x + sam_y * triplanar.y + sam_z * triplanar.z) * + float4(1, g_xMaterial.roughness, g_xMaterial.metalness, g_xMaterial.reflectance) * blend_weights.x; + sam_x.xyz = texture_normalmap.Sample(sampler_objectshader, uv_x).rgb; + sam_y.xyz = texture_normalmap.Sample(sampler_objectshader, uv_y).rgb; + sam_z.xyz = texture_normalmap.Sample(sampler_objectshader, uv_z).rgb; + bumpColor = (sam_x.xyz * triplanar.x + sam_y.xyz * triplanar.y + sam_z.xyz * triplanar.z); + bumpColor = bumpColor.rgb * 2 - 1; + bumpColor.g *= g_xMaterial.normalMapFlip; + triplanar_normal += normalize(lerp(surface.N, mul(bumpColor, TBN), g_xMaterial.normalMapStrength)) * blend_weights.x; + } + + [branch] + if (blend_weights.y > 0) + { + uv_x = surface.P.yz * g_xMaterial_blend1.texMulAdd.xy + g_xMaterial_blend1.texMulAdd.zw; + uv_y = surface.P.xz * g_xMaterial_blend1.texMulAdd.xy + g_xMaterial_blend1.texMulAdd.zw; + uv_z = surface.P.xy * g_xMaterial_blend1.texMulAdd.xy + g_xMaterial_blend1.texMulAdd.zw; + sam_x = texture_blend1_basecolormap.Sample(sampler_objectshader, uv_x); + sam_y = texture_blend1_basecolormap.Sample(sampler_objectshader, uv_y); + sam_z = texture_blend1_basecolormap.Sample(sampler_objectshader, uv_z); + sam_x.rgb = DEGAMMA(sam_x.rgb); + sam_y.rgb = DEGAMMA(sam_y.rgb); + sam_z.rgb = DEGAMMA(sam_z.rgb); + color += (sam_x * triplanar.x + sam_y * triplanar.y + sam_z * triplanar.z) * g_xMaterial_blend1.baseColor * blend_weights.y; + sam_x = texture_blend1_surfacemap.Sample(sampler_objectshader, uv_x); + sam_y = texture_blend1_surfacemap.Sample(sampler_objectshader, uv_y); + sam_z = texture_blend1_surfacemap.Sample(sampler_objectshader, uv_z); + surface_occlusion_roughness_metallic_reflectance += (sam_x * triplanar.x + sam_y * triplanar.y + sam_z * triplanar.z) * + float4(1, g_xMaterial_blend1.roughness, g_xMaterial_blend1.metalness, g_xMaterial_blend1.reflectance) * blend_weights.y; + sam_x.xyz = texture_blend1_normalmap.Sample(sampler_objectshader, uv_x).rgb; + sam_y.xyz = texture_blend1_normalmap.Sample(sampler_objectshader, uv_y).rgb; + sam_z.xyz = texture_blend1_normalmap.Sample(sampler_objectshader, uv_z).rgb; + bumpColor = (sam_x.xyz * triplanar.x + sam_y.xyz * triplanar.y + sam_z.xyz * triplanar.z); + bumpColor = bumpColor.rgb * 2 - 1; + bumpColor.g *= g_xMaterial_blend1.normalMapFlip; + triplanar_normal += normalize(lerp(surface.N, mul(bumpColor, TBN), g_xMaterial_blend1.normalMapStrength)) * blend_weights.y; + } + + [branch] + if (blend_weights.z > 0) + { + uv_x = surface.P.yz * g_xMaterial_blend2.texMulAdd.xy + g_xMaterial_blend2.texMulAdd.zw; + uv_y = surface.P.xz * g_xMaterial_blend2.texMulAdd.xy + g_xMaterial_blend2.texMulAdd.zw; + uv_z = surface.P.xy * g_xMaterial_blend2.texMulAdd.xy + g_xMaterial_blend2.texMulAdd.zw; + sam_x = texture_blend2_basecolormap.Sample(sampler_objectshader, uv_x); + sam_y = texture_blend2_basecolormap.Sample(sampler_objectshader, uv_y); + sam_z = texture_blend2_basecolormap.Sample(sampler_objectshader, uv_z); + sam_x.rgb = DEGAMMA(sam_x.rgb); + sam_y.rgb = DEGAMMA(sam_y.rgb); + sam_z.rgb = DEGAMMA(sam_z.rgb); + color += (sam_x * triplanar.x + sam_y * triplanar.y + sam_z * triplanar.z) * g_xMaterial_blend2.baseColor * blend_weights.z; + sam_x = texture_blend2_surfacemap.Sample(sampler_objectshader, uv_x); + sam_y = texture_blend2_surfacemap.Sample(sampler_objectshader, uv_y); + sam_z = texture_blend2_surfacemap.Sample(sampler_objectshader, uv_z); + surface_occlusion_roughness_metallic_reflectance += (sam_x * triplanar.x + sam_y * triplanar.y + sam_z * triplanar.z) * + float4(1, g_xMaterial_blend2.roughness, g_xMaterial_blend2.metalness, g_xMaterial_blend2.reflectance) * blend_weights.z; + sam_x.xyz = texture_blend2_normalmap.Sample(sampler_objectshader, uv_x).rgb; + sam_y.xyz = texture_blend2_normalmap.Sample(sampler_objectshader, uv_y).rgb; + sam_z.xyz = texture_blend2_normalmap.Sample(sampler_objectshader, uv_z).rgb; + bumpColor = (sam_x.xyz * triplanar.x + sam_y.xyz * triplanar.y + sam_z.xyz * triplanar.z); + bumpColor = bumpColor.rgb * 2 - 1; + bumpColor.g *= g_xMaterial_blend2.normalMapFlip; + triplanar_normal += normalize(lerp(surface.N, mul(bumpColor, TBN), g_xMaterial_blend2.normalMapStrength)) * blend_weights.z; + } + + [branch] + if (blend_weights.w > 0) + { + uv_x = surface.P.yz * g_xMaterial_blend3.texMulAdd.xy + g_xMaterial_blend3.texMulAdd.zw; + uv_y = surface.P.xz * g_xMaterial_blend3.texMulAdd.xy + g_xMaterial_blend3.texMulAdd.zw; + uv_z = surface.P.xy * g_xMaterial_blend3.texMulAdd.xy + g_xMaterial_blend3.texMulAdd.zw; + sam_x = texture_blend3_basecolormap.Sample(sampler_objectshader, uv_x); + sam_y = texture_blend3_basecolormap.Sample(sampler_objectshader, uv_y); + sam_z = texture_blend3_basecolormap.Sample(sampler_objectshader, uv_z); + sam_x.rgb = DEGAMMA(sam_x.rgb); + sam_y.rgb = DEGAMMA(sam_y.rgb); + sam_z.rgb = DEGAMMA(sam_z.rgb); + color += (sam_x * triplanar.x + sam_y * triplanar.y + sam_z * triplanar.z) * g_xMaterial_blend3.baseColor * blend_weights.w; + sam_x = texture_blend3_surfacemap.Sample(sampler_objectshader, uv_x); + sam_y = texture_blend3_surfacemap.Sample(sampler_objectshader, uv_y); + sam_z = texture_blend3_surfacemap.Sample(sampler_objectshader, uv_z); + surface_occlusion_roughness_metallic_reflectance += (sam_x * triplanar.x + sam_y * triplanar.y + sam_z * triplanar.z) * + float4(1, g_xMaterial_blend3.roughness, g_xMaterial_blend3.metalness, g_xMaterial_blend3.reflectance) * blend_weights.w; + sam_x.xyz = texture_blend3_normalmap.Sample(sampler_objectshader, uv_x).rgb; + sam_y.xyz = texture_blend3_normalmap.Sample(sampler_objectshader, uv_y).rgb; + sam_z.xyz = texture_blend3_normalmap.Sample(sampler_objectshader, uv_z).rgb; + bumpColor = (sam_x.xyz * triplanar.x + sam_y.xyz * triplanar.y + sam_z.xyz * triplanar.z); + bumpColor = bumpColor.rgb * 2 - 1; + bumpColor.g *= g_xMaterial_blend3.normalMapFlip; + triplanar_normal += normalize(lerp(surface.N, mul(bumpColor, TBN), g_xMaterial_blend3.normalMapStrength)) * blend_weights.w; + } + + color.a = 1; + surface.N = triplanar_normal; + +#endif // TERRAIN + // Emissive map: float4 emissiveColor = g_xMaterial.emissiveColor; @@ -824,10 +967,10 @@ GBUFFEROutputType_Thin main(PIXELINPUT input) surface.N, surface.V, color, - g_xMaterial.roughness * surface_occlusion_roughness_metallic_reflectance.g, + surface_occlusion_roughness_metallic_reflectance.g, surface_occlusion_roughness_metallic_reflectance.r, - g_xMaterial.metalness * surface_occlusion_roughness_metallic_reflectance.b, - g_xMaterial.reflectance * surface_occlusion_roughness_metallic_reflectance.a, + surface_occlusion_roughness_metallic_reflectance.b, + surface_occlusion_roughness_metallic_reflectance.a, emissiveColor, g_xMaterial.subsurfaceScattering ); diff --git a/WickedEngine/objectPS_deferred_terrain.hlsl b/WickedEngine/objectPS_deferred_terrain.hlsl new file mode 100644 index 000000000..bb1b0d8db --- /dev/null +++ b/WickedEngine/objectPS_deferred_terrain.hlsl @@ -0,0 +1,4 @@ +#define COMPILE_OBJECTSHADER_PS +#define DEFERRED +#define TERRAIN +#include "objectHF.hlsli" diff --git a/WickedEngine/objectPS_forward_terrain.hlsl b/WickedEngine/objectPS_forward_terrain.hlsl new file mode 100644 index 000000000..7b260a991 --- /dev/null +++ b/WickedEngine/objectPS_forward_terrain.hlsl @@ -0,0 +1,4 @@ +#define COMPILE_OBJECTSHADER_PS +#define FORWARD +#define TERRAIN +#include "objectHF.hlsli" diff --git a/WickedEngine/objectPS_tiledforward_terrain.hlsl b/WickedEngine/objectPS_tiledforward_terrain.hlsl new file mode 100644 index 000000000..24ebf9612 --- /dev/null +++ b/WickedEngine/objectPS_tiledforward_terrain.hlsl @@ -0,0 +1,4 @@ +#define COMPILE_OBJECTSHADER_PS +#define TILEDFORWARD +#define TERRAIN +#include "objectHF.hlsli" diff --git a/WickedEngine/objectPS_voxelizer_terrain.hlsl b/WickedEngine/objectPS_voxelizer_terrain.hlsl new file mode 100644 index 000000000..88517a702 --- /dev/null +++ b/WickedEngine/objectPS_voxelizer_terrain.hlsl @@ -0,0 +1,2 @@ +#define TERRAIN +#include "objectPS_voxelizer.hlsl" diff --git a/WickedEngine/wiArchive.cpp b/WickedEngine/wiArchive.cpp index c5a4df46e..2abcbdb46 100644 --- a/WickedEngine/wiArchive.cpp +++ b/WickedEngine/wiArchive.cpp @@ -7,7 +7,7 @@ using namespace std; // this should always be only INCREMENTED and only if a new serialization is implemeted somewhere! -uint64_t __archiveVersion = 40; +uint64_t __archiveVersion = 41; // this is the version number of which below the archive is not compatible with the current version uint64_t __archiveVersionBarrier = 22; diff --git a/WickedEngine/wiEnums.h b/WickedEngine/wiEnums.h index b4523d08c..c8e71326e 100644 --- a/WickedEngine/wiEnums.h +++ b/WickedEngine/wiEnums.h @@ -154,6 +154,7 @@ enum PSTYPES PSTYPE_OBJECT_DEFERRED_PLANARREFLECTION, PSTYPE_OBJECT_DEFERRED_NORMALMAP_POM, PSTYPE_OBJECT_DEFERRED_NORMALMAP_PLANARREFLECTION, + PSTYPE_OBJECT_DEFERRED_TERRAIN, PSTYPE_IMPOSTOR_DEFERRED, PSTYPE_OBJECT_FORWARD, @@ -169,6 +170,7 @@ enum PSTYPES PSTYPE_OBJECT_FORWARD_TRANSPARENT_POM, PSTYPE_OBJECT_FORWARD_TRANSPARENT_NORMALMAP_POM, PSTYPE_OBJECT_FORWARD_WATER, + PSTYPE_OBJECT_FORWARD_TERRAIN, PSTYPE_IMPOSTOR_FORWARD, PSTYPE_OBJECT_TILEDFORWARD, @@ -184,6 +186,7 @@ enum PSTYPES PSTYPE_OBJECT_TILEDFORWARD_TRANSPARENT_POM, PSTYPE_OBJECT_TILEDFORWARD_TRANSPARENT_NORMALMAP_POM, PSTYPE_OBJECT_TILEDFORWARD_WATER, + PSTYPE_OBJECT_TILEDFORWARD_TERRAIN, PSTYPE_IMPOSTOR_TILEDFORWARD, PSTYPE_OBJECT_HOLOGRAM, @@ -221,6 +224,7 @@ enum PSTYPES PSTYPE_SKY_DYNAMIC, PSTYPE_SUN, PSTYPE_ENVMAP, + PSTYPE_ENVMAP_TERRAIN, PSTYPE_ENVMAP_SKY_STATIC, PSTYPE_ENVMAP_SKY_DYNAMIC, PSTYPE_CUBEMAP, @@ -228,6 +232,7 @@ enum PSTYPES PSTYPE_CAPTUREIMPOSTOR_NORMAL, PSTYPE_CAPTUREIMPOSTOR_SURFACE, PSTYPE_VOXELIZER, + PSTYPE_VOXELIZER_TERRAIN, PSTYPE_VOXEL, PSTYPE_FORCEFIELDVISUALIZER, PSTYPE_RENDERLIGHTMAP, diff --git a/WickedEngine/wiGraphicsDevice_SharedInternals.h b/WickedEngine/wiGraphicsDevice_SharedInternals.h index 97272eaaf..bca385523 100644 --- a/WickedEngine/wiGraphicsDevice_SharedInternals.h +++ b/WickedEngine/wiGraphicsDevice_SharedInternals.h @@ -3,7 +3,7 @@ // Descriptor layout counts: #define GPU_RESOURCE_HEAP_CBV_COUNT 12 -#define GPU_RESOURCE_HEAP_SRV_COUNT 32 +#define GPU_RESOURCE_HEAP_SRV_COUNT 42 #define GPU_RESOURCE_HEAP_UAV_COUNT 8 #define GPU_SAMPLER_HEAP_COUNT 16 diff --git a/WickedEngine/wiRenderer.cpp b/WickedEngine/wiRenderer.cpp index 8e40d3b62..45e0a4c22 100644 --- a/WickedEngine/wiRenderer.cpp +++ b/WickedEngine/wiRenderer.cpp @@ -500,6 +500,8 @@ inline const PipelineState* GetObjectPSO(RENDERPASS renderPass, bool doublesided return &pso; } +PipelineState PSO_terrain[RENDERPASS_COUNT]; + PipelineState PSO_object_hologram; std::vector customShaders; int RegisterCustomShader(const CustomShader& customShader) @@ -1209,6 +1211,7 @@ void LoadShaders() wiJobSystem::Execute(ctx, [] { LoadShader(PS, pixelShaders[PSTYPE_OBJECT_DEFERRED_PLANARREFLECTION], "objectPS_deferred_planarreflection.cso"); }); wiJobSystem::Execute(ctx, [] { LoadShader(PS, pixelShaders[PSTYPE_OBJECT_DEFERRED_NORMALMAP_POM], "objectPS_deferred_normalmap_pom.cso"); }); wiJobSystem::Execute(ctx, [] { LoadShader(PS, pixelShaders[PSTYPE_OBJECT_DEFERRED_NORMALMAP_PLANARREFLECTION], "objectPS_deferred_normalmap_planarreflection.cso"); }); + wiJobSystem::Execute(ctx, [] { LoadShader(PS, pixelShaders[PSTYPE_OBJECT_DEFERRED_TERRAIN], "objectPS_deferred_terrain.cso"); }); wiJobSystem::Execute(ctx, [] { LoadShader(PS, pixelShaders[PSTYPE_IMPOSTOR_DEFERRED], "impostorPS_deferred.cso"); }); wiJobSystem::Execute(ctx, [] { LoadShader(PS, pixelShaders[PSTYPE_OBJECT_FORWARD], "objectPS_forward.cso"); }); @@ -1224,6 +1227,7 @@ void LoadShaders() wiJobSystem::Execute(ctx, [] { LoadShader(PS, pixelShaders[PSTYPE_OBJECT_FORWARD_TRANSPARENT_POM], "objectPS_forward_transparent_pom.cso"); }); wiJobSystem::Execute(ctx, [] { LoadShader(PS, pixelShaders[PSTYPE_OBJECT_FORWARD_TRANSPARENT_NORMALMAP_POM], "objectPS_forward_transparent_normalmap_pom.cso"); }); wiJobSystem::Execute(ctx, [] { LoadShader(PS, pixelShaders[PSTYPE_OBJECT_FORWARD_WATER], "objectPS_forward_water.cso"); }); + wiJobSystem::Execute(ctx, [] { LoadShader(PS, pixelShaders[PSTYPE_OBJECT_FORWARD_TERRAIN], "objectPS_forward_terrain.cso"); }); wiJobSystem::Execute(ctx, [] { LoadShader(PS, pixelShaders[PSTYPE_IMPOSTOR_FORWARD], "impostorPS_forward.cso"); }); wiJobSystem::Execute(ctx, [] { LoadShader(PS, pixelShaders[PSTYPE_OBJECT_TILEDFORWARD], "objectPS_tiledforward.cso"); }); @@ -1239,6 +1243,7 @@ void LoadShaders() wiJobSystem::Execute(ctx, [] { LoadShader(PS, pixelShaders[PSTYPE_OBJECT_TILEDFORWARD_TRANSPARENT_POM], "objectPS_tiledforward_transparent_pom.cso"); }); wiJobSystem::Execute(ctx, [] { LoadShader(PS, pixelShaders[PSTYPE_OBJECT_TILEDFORWARD_TRANSPARENT_NORMALMAP_POM], "objectPS_tiledforward_transparent_normalmap_pom.cso"); }); wiJobSystem::Execute(ctx, [] { LoadShader(PS, pixelShaders[PSTYPE_OBJECT_TILEDFORWARD_WATER], "objectPS_tiledforward_water.cso"); }); + wiJobSystem::Execute(ctx, [] { LoadShader(PS, pixelShaders[PSTYPE_OBJECT_TILEDFORWARD_TERRAIN], "objectPS_tiledforward_terrain.cso"); }); wiJobSystem::Execute(ctx, [] { LoadShader(PS, pixelShaders[PSTYPE_IMPOSTOR_TILEDFORWARD], "impostorPS_tiledforward.cso"); }); wiJobSystem::Execute(ctx, [] { LoadShader(PS, pixelShaders[PSTYPE_OBJECT_HOLOGRAM], "objectPS_hologram.cso"); }); @@ -1266,6 +1271,7 @@ void LoadShaders() wiJobSystem::Execute(ctx, [] { LoadShader(PS, pixelShaders[PSTYPE_VOLUMETRICLIGHT_SPOT], "volumetricLight_SpotPS.cso"); }); wiJobSystem::Execute(ctx, [] { LoadShader(PS, pixelShaders[PSTYPE_DECAL], "decalPS.cso"); }); wiJobSystem::Execute(ctx, [] { LoadShader(PS, pixelShaders[PSTYPE_ENVMAP], "envMapPS.cso"); }); + wiJobSystem::Execute(ctx, [] { LoadShader(PS, pixelShaders[PSTYPE_ENVMAP_TERRAIN], "envMapPS_terrain.cso"); }); wiJobSystem::Execute(ctx, [] { LoadShader(PS, pixelShaders[PSTYPE_ENVMAP_SKY_STATIC], "envMap_skyPS_static.cso"); }); wiJobSystem::Execute(ctx, [] { LoadShader(PS, pixelShaders[PSTYPE_ENVMAP_SKY_DYNAMIC], "envMap_skyPS_dynamic.cso"); }); wiJobSystem::Execute(ctx, [] { LoadShader(PS, pixelShaders[PSTYPE_CAPTUREIMPOSTOR_ALBEDO], "captureImpostorPS_albedo.cso"); }); @@ -1281,6 +1287,7 @@ void LoadShaders() wiJobSystem::Execute(ctx, [] { LoadShader(PS, pixelShaders[PSTYPE_SHADOW_WATER], "shadowPS_water.cso"); }); wiJobSystem::Execute(ctx, [] { LoadShader(PS, pixelShaders[PSTYPE_TRAIL], "trailPS.cso"); }); wiJobSystem::Execute(ctx, [] { LoadShader(PS, pixelShaders[PSTYPE_VOXELIZER], "objectPS_voxelizer.cso"); }); + wiJobSystem::Execute(ctx, [] { LoadShader(PS, pixelShaders[PSTYPE_VOXELIZER_TERRAIN], "objectPS_voxelizer_terrain.cso"); }); wiJobSystem::Execute(ctx, [] { LoadShader(PS, pixelShaders[PSTYPE_VOXEL], "voxelPS.cso"); }); wiJobSystem::Execute(ctx, [] { LoadShader(PS, pixelShaders[PSTYPE_FORCEFIELDVISUALIZER], "forceFieldVisualizerPS.cso"); }); wiJobSystem::Execute(ctx, [] { LoadShader(PS, pixelShaders[PSTYPE_RENDERLIGHTMAP], "renderlightmapPS.cso"); }); @@ -1541,6 +1548,58 @@ void LoadShaders() } } + wiJobSystem::Dispatch(ctx, RENDERPASS_COUNT, 1, [device](wiJobDispatchArgs args) { + + VSTYPES realVS = GetVSTYPE((RENDERPASS)args.jobIndex, false, false, false); + ILTYPES realVL = GetILTYPE((RENDERPASS)args.jobIndex, false, false, false); + + PipelineStateDesc desc; + desc.rs = &rasterizers[RSTYPE_FRONT]; + desc.bs = &blendStates[BSTYPE_OPAQUE]; + desc.dss = &depthStencils[DSSTYPE_DEFAULT]; + desc.il = &inputLayouts[realVL]; + desc.vs = &vertexShaders[realVS]; + + switch (args.jobIndex) + { + case RENDERPASS_TEXTURE: + desc.ps = &pixelShaders[PSTYPE_OBJECT_TEXTUREONLY]; // textureonly doesn't have worldpos or normal inputs, so it will not use terrain blending + break; + case RENDERPASS_DEFERRED: + desc.ps = &pixelShaders[PSTYPE_OBJECT_DEFERRED_TERRAIN]; + break; + case RENDERPASS_FORWARD: + desc.dss = &depthStencils[DSSTYPE_DEPTHREADEQUAL]; + desc.ps = &pixelShaders[PSTYPE_OBJECT_FORWARD_TERRAIN]; + break; + case RENDERPASS_TILEDFORWARD: + desc.dss = &depthStencils[DSSTYPE_DEPTHREADEQUAL]; + desc.ps = &pixelShaders[PSTYPE_OBJECT_TILEDFORWARD_TERRAIN]; + break; + case RENDERPASS_DEPTHONLY: + desc.ps = nullptr; + break; + case RENDERPASS_VOXELIZE: + desc.dss = nullptr; + desc.rs = &rasterizers[RSTYPE_VOXELIZE]; + desc.ps = &pixelShaders[PSTYPE_VOXELIZER_TERRAIN]; + break; + case RENDERPASS_ENVMAPCAPTURE: + desc.ps = &pixelShaders[PSTYPE_ENVMAP_TERRAIN]; + break; + case RENDERPASS_SHADOW: + case RENDERPASS_SHADOWCUBE: + desc.dss = &depthStencils[DSSTYPE_SHADOW]; + desc.rs = &rasterizers[RSTYPE_SHADOW_DOUBLESIDED]; + desc.ps = nullptr; + break; + default: + return; + } + + device->CreatePipelineState(&desc, &PSO_terrain[args.jobIndex]); + }); + // Clear custom shaders (Custom shaders coming from user will need to be handled by the user in case of shader reload): customShaders.clear(); @@ -3135,6 +3194,7 @@ void RenderMeshes(const RenderQueue& renderQueue, RENDERPASS renderPass, uint32_ const float tessF = mesh.GetTessellationFactor(); const bool tessellatorRequested = tessF > 0 && tessellation; + const bool terrain = mesh.IsTerrain(); if (tessellatorRequested) { @@ -3173,13 +3233,20 @@ void RenderMeshes(const RenderQueue& renderQueue, RENDERPASS renderPass, uint32_ const MaterialComponent& material = *scene.materials.GetComponent(subset.materialID); const PipelineState* pso = nullptr; - if (material.IsCustomShader()) + if (terrain) { - pso = GetCustomShaderPSO(renderPass, renderTypeFlags, material.GetCustomShaderID()); + pso = &PSO_terrain[renderPass]; } else { - pso = GetObjectPSO(renderPass, mesh.IsDoubleSided(), tessellatorRequested, material, forceAlphaTestForDithering); + if (material.IsCustomShader()) + { + pso = GetCustomShaderPSO(renderPass, renderTypeFlags, material.GetCustomShaderID()); + } + else + { + pso = GetObjectPSO(renderPass, mesh.IsDoubleSided(), tessellatorRequested, material, forceAlphaTestForDithering); + } } if (pso == nullptr) @@ -3373,6 +3440,75 @@ void RenderMeshes(const RenderQueue& renderQueue, RENDERPASS renderPass, uint32_ device->BindConstantBuffer(DS, &material.constantBuffer, CB_GETBINDSLOT(MaterialCB), cmd); } + if (terrain) + { + if (mesh.terrain_material1 == INVALID_ENTITY || !scene.materials.Contains(mesh.terrain_material1)) + { + const GPUResource* res[] = { + material.GetBaseColorMap(), + material.GetNormalMap(), + material.GetSurfaceMap(), + }; + device->BindResources(PS, res, TEXSLOT_RENDERER_BLEND1_BASECOLORMAP, arraysize(res), cmd); + device->BindConstantBuffer(PS, &material.constantBuffer, CB_GETBINDSLOT(MaterialCB_Blend1), cmd); + } + else + { + const MaterialComponent& blendmat = *scene.materials.GetComponent(mesh.terrain_material1); + const GPUResource* res[] = { + blendmat.GetBaseColorMap(), + blendmat.GetNormalMap(), + blendmat.GetSurfaceMap(), + }; + device->BindResources(PS, res, TEXSLOT_RENDERER_BLEND1_BASECOLORMAP, arraysize(res), cmd); + device->BindConstantBuffer(PS, &blendmat.constantBuffer, CB_GETBINDSLOT(MaterialCB_Blend1), cmd); + } + + if (mesh.terrain_material2 == INVALID_ENTITY || !scene.materials.Contains(mesh.terrain_material2)) + { + const GPUResource* res[] = { + material.GetBaseColorMap(), + material.GetNormalMap(), + material.GetSurfaceMap(), + }; + device->BindResources(PS, res, TEXSLOT_RENDERER_BLEND2_BASECOLORMAP, arraysize(res), cmd); + device->BindConstantBuffer(PS, &material.constantBuffer, CB_GETBINDSLOT(MaterialCB_Blend2), cmd); + } + else + { + const MaterialComponent& blendmat = *scene.materials.GetComponent(mesh.terrain_material2); + const GPUResource* res[] = { + blendmat.GetBaseColorMap(), + blendmat.GetNormalMap(), + blendmat.GetSurfaceMap(), + }; + device->BindResources(PS, res, TEXSLOT_RENDERER_BLEND2_BASECOLORMAP, arraysize(res), cmd); + device->BindConstantBuffer(PS, &blendmat.constantBuffer, CB_GETBINDSLOT(MaterialCB_Blend2), cmd); + } + + if (mesh.terrain_material3 == INVALID_ENTITY || !scene.materials.Contains(mesh.terrain_material3)) + { + const GPUResource* res[] = { + material.GetBaseColorMap(), + material.GetNormalMap(), + material.GetSurfaceMap(), + }; + device->BindResources(PS, res, TEXSLOT_RENDERER_BLEND3_BASECOLORMAP, arraysize(res), cmd); + device->BindConstantBuffer(PS, &material.constantBuffer, CB_GETBINDSLOT(MaterialCB_Blend3), cmd); + } + else + { + const MaterialComponent& blendmat = *scene.materials.GetComponent(mesh.terrain_material3); + const GPUResource* res[] = { + blendmat.GetBaseColorMap(), + blendmat.GetNormalMap(), + blendmat.GetSurfaceMap(), + }; + device->BindResources(PS, res, TEXSLOT_RENDERER_BLEND3_BASECOLORMAP, arraysize(res), cmd); + device->BindConstantBuffer(PS, &blendmat.constantBuffer, CB_GETBINDSLOT(MaterialCB_Blend3) + 3, cmd); + } + } + device->DrawIndexedInstanced(subset.indexCount, instancedBatch.instanceCount, subset.indexOffset, 0, 0, cmd); } } diff --git a/WickedEngine/wiScene.h b/WickedEngine/wiScene.h index e42d03458..d522fec14 100644 --- a/WickedEngine/wiScene.h +++ b/WickedEngine/wiScene.h @@ -248,6 +248,7 @@ namespace wiScene RENDERABLE = 1 << 0, DOUBLE_SIDED = 1 << 1, DYNAMIC = 1 << 2, + TERRAIN = 1 << 3, }; uint32_t _flags = RENDERABLE; @@ -272,6 +273,15 @@ namespace wiScene float tessellationFactor = 0.0f; wiECS::Entity armatureID = wiECS::INVALID_ENTITY; + // Terrain blend materials: + // There are 4 blend materials, the first one (default) being the subset material + // Must have TERRAIN flag enabled + // Must have vertex colors to blend between materials + // extra materials that are not set will use the base subset material + wiECS::Entity terrain_material1 = wiECS::INVALID_ENTITY; + wiECS::Entity terrain_material2 = wiECS::INVALID_ENTITY; + wiECS::Entity terrain_material3 = wiECS::INVALID_ENTITY; + // Non-serialized attributes: AABB aabb; wiGraphics::GPUBuffer indexBuffer; @@ -289,10 +299,12 @@ namespace wiScene inline void SetRenderable(bool value) { if (value) { _flags |= RENDERABLE; } else { _flags &= ~RENDERABLE; } } inline void SetDoubleSided(bool value) { if (value) { _flags |= DOUBLE_SIDED; } else { _flags &= ~DOUBLE_SIDED; } } inline void SetDynamic(bool value) { if (value) { _flags |= DYNAMIC; } else { _flags &= ~DYNAMIC; } } + inline void SetTerrain(bool value) { if (value) { _flags |= TERRAIN; } else { _flags &= ~TERRAIN; } } inline bool IsRenderable() const { return _flags & RENDERABLE; } inline bool IsDoubleSided() const { return _flags & DOUBLE_SIDED; } inline bool IsDynamic() const { return _flags & DYNAMIC; } + inline bool IsTerrain() const { return _flags & TERRAIN; } inline float GetTessellationFactor() const { return tessellationFactor; } inline wiGraphics::INDEXBUFFER_FORMAT GetIndexFormat() const { return vertex_positions.size() > 65535 ? wiGraphics::INDEXFORMAT_32BIT : wiGraphics::INDEXFORMAT_16BIT; } diff --git a/WickedEngine/wiScene_Serializers.cpp b/WickedEngine/wiScene_Serializers.cpp index b8c08ea03..e21e59e95 100644 --- a/WickedEngine/wiScene_Serializers.cpp +++ b/WickedEngine/wiScene_Serializers.cpp @@ -282,6 +282,13 @@ namespace wiScene archive >> vertex_uvset_1; } + if (archive.GetVersion() >= 41) + { + SerializeEntity(archive, terrain_material1, seed); + SerializeEntity(archive, terrain_material2, seed); + SerializeEntity(archive, terrain_material3, seed); + } + CreateRenderData(); } else @@ -312,6 +319,13 @@ namespace wiScene archive << vertex_uvset_1; } + if (archive.GetVersion() >= 41) + { + SerializeEntity(archive, terrain_material1, seed); + SerializeEntity(archive, terrain_material2, seed); + SerializeEntity(archive, terrain_material3, seed); + } + } } void ImpostorComponent::Serialize(wiArchive& archive, uint32_t seed) diff --git a/WickedEngine/wiVersion.cpp b/WickedEngine/wiVersion.cpp index c990a5fb4..d566cf9b1 100644 --- a/WickedEngine/wiVersion.cpp +++ b/WickedEngine/wiVersion.cpp @@ -9,7 +9,7 @@ namespace wiVersion // minor features, major updates const int minor = 39; // minor bug fixes, alterations, refactors, updates - const int revision = 40; + const int revision = 41; long GetVersion() diff --git a/features.txt b/features.txt index bf8280384..8f13a806a 100644 --- a/features.txt +++ b/features.txt @@ -83,3 +83,5 @@ Entity-Component System (Data oriented design) Lightmap baking (with GPU path tracing) Job system Inverse Kinematics +Springs +Terrain Rendering (material blending)