diff --git a/Editor/Editor_SOURCE.vcxitems b/Editor/Editor_SOURCE.vcxitems
index 215de9b8b6..d1d9d79276 100644
--- a/Editor/Editor_SOURCE.vcxitems
+++ b/Editor/Editor_SOURCE.vcxitems
@@ -232,10 +232,50 @@
true
true
+
+ true
+ true
+
+
+ true
+ true
+
+
+ true
+ true
+
+
+ true
+ true
+
+
+ true
+ true
+
+
+ true
+ true
+
true
true
+
+ true
+ true
+
+
+ true
+ true
+
+
+ true
+ true
+
+
+ true
+ true
+
true
true
@@ -244,6 +284,18 @@
true
true
+
+ true
+ true
+
+
+ true
+ true
+
+
+ true
+ true
+
true
true
@@ -252,6 +304,30 @@
true
true
+
+ true
+ true
+
+
+ true
+ true
+
+
+ true
+ true
+
+
+ true
+ true
+
+
+ true
+ true
+
+
+ true
+ true
+
true
true
@@ -260,6 +336,14 @@
true
true
+
+ true
+ true
+
+
+ true
+ true
+
@@ -270,7 +354,10 @@
true
true
-
+
+ true
+ true
+
diff --git a/Editor/Editor_SOURCE.vcxitems.filters b/Editor/Editor_SOURCE.vcxitems.filters
index 7dfd93e12b..0c66530a62 100644
--- a/Editor/Editor_SOURCE.vcxitems.filters
+++ b/Editor/Editor_SOURCE.vcxitems.filters
@@ -206,6 +206,69 @@
terrain
+
+ terrain
+
+
+ terrain
+
+
+ terrain
+
+
+ terrain
+
+
+ terrain
+
+
+ terrain
+
+
+ terrain
+
+
+ terrain
+
+
+ terrain
+
+
+ terrain
+
+
+ terrain
+
+
+ terrain
+
+
+ terrain
+
+
+ terrain
+
+
+ terrain
+
+
+ terrain
+
+
+ terrain
+
+
+ terrain
+
+
+ terrain
+
+
+ terrain
+
+
+ terrain
+
diff --git a/Editor/MaterialWindow.cpp b/Editor/MaterialWindow.cpp
index af18d28088..de27494097 100644
--- a/Editor/MaterialWindow.cpp
+++ b/Editor/MaterialWindow.cpp
@@ -9,7 +9,7 @@ void MaterialWindow::Create(EditorComponent* _editor)
{
editor = _editor;
wi::gui::Window::Create(ICON_MATERIAL " Material", wi::gui::Window::WindowControls::COLLAPSE | wi::gui::Window::WindowControls::CLOSE);
- SetSize(XMFLOAT2(300, 1360));
+ SetSize(XMFLOAT2(300, 1380));
closeButton.SetTooltip("Delete MaterialComponent");
OnClose([=](wi::gui::EventArgs args) {
@@ -476,6 +476,19 @@ void MaterialWindow::Create(EditorComponent* _editor)
});
AddWidget(&clearcoatRoughnessSlider);
+ blendTerrainSlider.Create(0, 2, 0, 1000, "Blend with terrain: ");
+ blendTerrainSlider.SetTooltip("Blend with terrain height.");
+ blendTerrainSlider.SetSize(XMFLOAT2(wid, hei));
+ blendTerrainSlider.SetPos(XMFLOAT2(x, y += step));
+ blendTerrainSlider.OnSlide([&](wi::gui::EventArgs args) {
+ MaterialComponent* material = editor->GetCurrentScene().materials.GetComponent(entity);
+ if (material != nullptr)
+ {
+ material->blend_with_terrain_height = args.fValue;
+ }
+ });
+ AddWidget(&blendTerrainSlider);
+
//
hei = 20;
@@ -822,6 +835,7 @@ void MaterialWindow::SetEntity(Entity entity)
shaderTypeComboBox.AddItem("Cloth", MaterialComponent::SHADERTYPE_PBR_CLOTH);
shaderTypeComboBox.AddItem("Clear coat", MaterialComponent::SHADERTYPE_PBR_CLEARCOAT);
shaderTypeComboBox.AddItem("Cloth + Clear coat", MaterialComponent::SHADERTYPE_PBR_CLOTH_CLEARCOAT);
+ shaderTypeComboBox.AddItem("Terrain blended", MaterialComponent::SHADERTYPE_PBR_TERRAINBLENDED);
shaderTypeComboBox.AddItem("Water", MaterialComponent::SHADERTYPE_WATER);
shaderTypeComboBox.AddItem("Cartoon", MaterialComponent::SHADERTYPE_CARTOON);
shaderTypeComboBox.AddItem("Unlit", MaterialComponent::SHADERTYPE_UNLIT);
@@ -883,6 +897,7 @@ void MaterialWindow::SetEntity(Entity entity)
sheenRoughnessSlider.SetValue(material->sheenRoughness);
clearcoatSlider.SetValue(material->clearcoat);
clearcoatRoughnessSlider.SetValue(material->clearcoatRoughness);
+ blendTerrainSlider.SetValue(material->blend_with_terrain_height);
shadingRateComboBox.SetEnabled(wi::graphics::GetDevice()->CheckCapability(GraphicsDeviceCapability::VARIABLE_RATE_SHADING));
@@ -989,6 +1004,7 @@ void MaterialWindow::ResizeLayout()
add(sheenRoughnessSlider);
add(clearcoatSlider);
add(clearcoatRoughnessSlider);
+ add(blendTerrainSlider);
add(colorComboBox);
add_fullwidth(colorPicker);
add(textureSlotComboBox);
diff --git a/Editor/MaterialWindow.h b/Editor/MaterialWindow.h
index 419099f36f..34fbb8e83e 100644
--- a/Editor/MaterialWindow.h
+++ b/Editor/MaterialWindow.h
@@ -46,6 +46,7 @@ class MaterialWindow : public wi::gui::Window
wi::gui::Slider sheenRoughnessSlider;
wi::gui::Slider clearcoatSlider;
wi::gui::Slider clearcoatRoughnessSlider;
+ wi::gui::Slider blendTerrainSlider;
wi::gui::ComboBox colorComboBox;
wi::gui::ColorPicker colorPicker;
diff --git a/Editor/PaintToolWindow.cpp b/Editor/PaintToolWindow.cpp
index c8dd483137..8794354547 100644
--- a/Editor/PaintToolWindow.cpp
+++ b/Editor/PaintToolWindow.cpp
@@ -5,6 +5,7 @@
using namespace wi::ecs;
using namespace wi::scene;
using namespace wi::graphics;
+using namespace wi::primitive;
void PaintToolWindow::Create(EditorComponent* _editor)
{
@@ -26,20 +27,21 @@ void PaintToolWindow::Create(EditorComponent* _editor)
modeComboBox.SetTooltip("Choose paint tool mode");
modeComboBox.SetPos(XMFLOAT2(x, y));
modeComboBox.SetSize(XMFLOAT2(wid, hei));
- modeComboBox.AddItem(ICON_DISABLED " Disabled");
- modeComboBox.AddItem(ICON_MATERIAL " Texture");
- modeComboBox.AddItem(ICON_MESH " Vertexcolor");
- modeComboBox.AddItem(ICON_MESH " Sculpting - Add");
- modeComboBox.AddItem(ICON_MESH " Sculpting - Subtract");
- modeComboBox.AddItem(ICON_SOFTBODY " Softbody - Pinning");
- modeComboBox.AddItem(ICON_SOFTBODY " Softbody - Physics");
- modeComboBox.AddItem(ICON_HAIR " Hairparticle - Add Triangle");
- modeComboBox.AddItem(ICON_HAIR " Hairparticle - Remove Triangle");
- modeComboBox.AddItem(ICON_HAIR " Hairparticle - Length (Alpha)");
- modeComboBox.AddItem(ICON_MESH " Wind weight (Alpha)");
+ modeComboBox.AddItem(ICON_DISABLED " Disabled", MODE_DISABLED);
+ modeComboBox.AddItem(ICON_MATERIAL " Texture", MODE_TEXTURE);
+ modeComboBox.AddItem(ICON_MESH " Vertexcolor", MODE_VERTEXCOLOR);
+ modeComboBox.AddItem(ICON_TERRAIN " Terrain material", MODE_TERRAIN_MATERIAL);
+ modeComboBox.AddItem(ICON_MESH " Sculpting - Add", MODE_SCULPTING_ADD);
+ modeComboBox.AddItem(ICON_MESH " Sculpting - Subtract", MODE_SCULPTING_SUBTRACT);
+ modeComboBox.AddItem(ICON_SOFTBODY " Softbody - Pinning", MODE_SOFTBODY_PINNING);
+ modeComboBox.AddItem(ICON_SOFTBODY " Softbody - Physics", MODE_SOFTBODY_PHYSICS);
+ modeComboBox.AddItem(ICON_HAIR " Hairparticle - Add Triangle", MODE_HAIRPARTICLE_ADD_TRIANGLE);
+ modeComboBox.AddItem(ICON_HAIR " Hairparticle - Remove Triangle", MODE_HAIRPARTICLE_REMOVE_TRIANGLE);
+ modeComboBox.AddItem(ICON_HAIR " Hairparticle - Length (Alpha)", MODE_HAIRPARTICLE_LENGTH);
+ modeComboBox.AddItem(ICON_MESH " Wind weight (Alpha)", MODE_WIND);
modeComboBox.SetSelected(0);
modeComboBox.OnSelect([&](wi::gui::EventArgs args) {
- switch (args.iValue)
+ switch (args.userdata)
{
case MODE_DISABLED:
infoLabel.SetText("Paint Tool is disabled.");
@@ -47,8 +49,11 @@ void PaintToolWindow::Create(EditorComponent* _editor)
case MODE_TEXTURE:
infoLabel.SetText("In texture paint mode, you can paint on textures. Brush will be applied in texture space.\nREMEMBER to save texture when finished to save texture file!\nREMEMBER to save scene to retain new texture bindings on materials!");
break;
+ case MODE_TERRAIN_MATERIAL:
+ infoLabel.SetText("You can paint terrain material layers. The paintable materials are those which are referenced by the terrain.");
+ break;
case MODE_VERTEXCOLOR:
- infoLabel.SetText("In vertex color mode, you can paint colors on selected geometry (per vertex). \"Use vertex colors\" will be automatically enabled for the selected material, or all materials if the whole object is selected. If there is no vertexcolors vertex buffer, one will be created with white as default for every vertex.");
+ infoLabel.SetText("In vertex color mode, you can paint colors on geometry (per vertex). \"Use vertex colors\" will be automatically enabled for the affected materials. If there is no vertexcolors vertex buffer, one will be created with white as default for every vertex.");
break;
case MODE_SCULPTING_ADD:
infoLabel.SetText("In sculpt - ADD mode, you can modify vertex positions by ADD operation along normal vector (average normal of vertices touched by brush).");
@@ -57,10 +62,10 @@ void PaintToolWindow::Create(EditorComponent* _editor)
infoLabel.SetText("In sculpt - SUBTRACT mode, you can modify vertex positions by SUBTRACT operation along normal vector (average normal of vertices touched by brush).");
break;
case MODE_SOFTBODY_PINNING:
- infoLabel.SetText("In soft body pinning mode, the selected object's soft body vertices can be pinned down (so they will be fixed and drive physics)");
+ infoLabel.SetText("In soft body pinning mode, the soft body vertices can be pinned down (so they will be fixed and drive physics)");
break;
case MODE_SOFTBODY_PHYSICS:
- infoLabel.SetText("In soft body physics mode, the selected object's soft body vertices can be unpinned (so they will be simulated by physics)");
+ infoLabel.SetText("In soft body physics mode, the soft body vertices can be unpinned (so they will be simulated by physics)");
break;
case MODE_HAIRPARTICLE_ADD_TRIANGLE:
infoLabel.SetText("In hair particle add triangle mode, you can add triangles to the hair base mesh.\nThis will modify random distribution of hair!");
@@ -75,6 +80,32 @@ void PaintToolWindow::Create(EditorComponent* _editor)
infoLabel.SetText("Paint the wind affection amount onto the vertices. Use the Alpha channel to control the amount.");
break;
}
+
+ if (args.userdata == MODE_TEXTURE)
+ {
+ radiusSlider.SetRange(1, 200);
+ radiusSlider.SetValue(texture_paint_radius);
+ }
+ else if (args.userdata == MODE_TERRAIN_MATERIAL)
+ {
+ radiusSlider.SetRange(1, 100);
+ radiusSlider.SetValue(terrain_paint_radius);
+ }
+ else
+ {
+ radiusSlider.SetRange(0, 10);
+ radiusSlider.SetValue(vertex_paint_radius);
+ }
+
+ if (args.userdata == MODE_TERRAIN_MATERIAL)
+ {
+ SetSize(XMFLOAT2(GetSize().x, 1200));
+ }
+ else
+ {
+ SetSize(XMFLOAT2(GetSize().x, 800));
+ }
+
});
AddWidget(&modeComboBox);
@@ -88,10 +119,24 @@ void PaintToolWindow::Create(EditorComponent* _editor)
y += infoLabel.GetScale().y - step + 5;
- radiusSlider.Create(1.0f, 500.0f, 50, 10000, "Brush Radius: ");
+ radiusSlider.Create(0.1f, 20.0f, 1, 10000, "Brush Radius: ");
radiusSlider.SetTooltip("Set the brush radius in pixel units");
radiusSlider.SetSize(XMFLOAT2(wid, hei));
radiusSlider.SetPos(XMFLOAT2(x, y += step));
+ radiusSlider.OnSlide([this](wi::gui::EventArgs args) {
+ if (GetMode() == MODE_TEXTURE)
+ {
+ texture_paint_radius = args.fValue;
+ }
+ else if (GetMode() == MODE_TERRAIN_MATERIAL)
+ {
+ terrain_paint_radius = args.fValue;
+ }
+ else
+ {
+ vertex_paint_radius = args.fValue;
+ }
+ });
AddWidget(&radiusSlider);
amountSlider.Create(0, 1, 1, 10000, "Power: ");
@@ -150,7 +195,7 @@ void PaintToolWindow::Create(EditorComponent* _editor)
wireCheckBox.SetTooltip("Set whether to draw wireframe on top of geometry or not");
wireCheckBox.SetSize(XMFLOAT2(hei, hei));
wireCheckBox.SetPos(XMFLOAT2(x - 20 + 100, y));
- wireCheckBox.SetCheck(true);
+ wireCheckBox.SetCheck(false);
if (editor->main->config.GetSection("paint_tool").Has("wireframe"))
{
wireCheckBox.SetCheck(editor->main->config.GetSection("paint_tool").GetBool("wireframe"));
@@ -182,6 +227,13 @@ void PaintToolWindow::Create(EditorComponent* _editor)
alphaCheckBox.SetPos(XMFLOAT2(x - 20 + 200, y));
AddWidget(&alphaCheckBox);
+ terrainCheckBox.Create("Terrain only: ");
+ terrainCheckBox.SetTooltip("Terrain specific sculpting mode.");
+ terrainCheckBox.SetSize(XMFLOAT2(hei, hei));
+ terrainCheckBox.SetPos(XMFLOAT2(x - 20 + 200, y));
+ terrainCheckBox.SetCheckText(ICON_TERRAIN);
+ AddWidget(&terrainCheckBox);
+
axisCombo.Create("Axis Lock: ");
axisCombo.SetTooltip("You can lock modification to an axis here.");
axisCombo.SetPos(XMFLOAT2(x, y));
@@ -343,7 +395,7 @@ void PaintToolWindow::Create(EditorComponent* _editor)
void PaintToolWindow::Update(float dt)
{
- RecordHistory(false);
+ RecordHistory(INVALID_ENTITY);
if (GetMode() == MODE_TEXTURE)
{
@@ -353,6 +405,7 @@ void PaintToolWindow::Update(float dt)
saveTextureButton.SetVisible(true);
brushTextureButton.SetVisible(true);
revealTextureButton.SetVisible(true);
+ alphaCheckBox.SetVisible(true);
}
else
{
@@ -362,15 +415,35 @@ void PaintToolWindow::Update(float dt)
saveTextureButton.SetVisible(false);
brushTextureButton.SetVisible(false);
revealTextureButton.SetVisible(false);
+ alphaCheckBox.SetVisible(false);
+ }
+
+ if (GetMode() == MODE_TERRAIN_MATERIAL)
+ {
+ colorPicker.SetVisible(false);
+ for (auto& x : terrain_material_buttons)
+ {
+ x.SetVisible(true);
+ }
+ }
+ else
+ {
+ colorPicker.SetVisible(true);
+ for (auto& x : terrain_material_buttons)
+ {
+ x.SetVisible(false);
+ }
}
if (GetMode() == MODE_SCULPTING_ADD || GetMode() == MODE_SCULPTING_SUBTRACT)
{
axisCombo.SetVisible(true);
+ terrainCheckBox.SetVisible(true);
}
else
{
axisCombo.SetVisible(false);
+ terrainCheckBox.SetVisible(false);
}
rot -= dt;
@@ -408,6 +481,7 @@ void PaintToolWindow::Update(float dt)
const XMFLOAT4 color_float = color.toFloat4();
const bool backfaces = backfaceCheckBox.GetCheck();
const bool wireframe = wireCheckBox.GetCheck();
+ const bool terrain_only = terrainCheckBox.GetCheck();
const float spacing = spacingSlider.GetValue();
const size_t stabilizer = (size_t)stabilizerSlider.GetValue();
CommandList cmd;
@@ -429,6 +503,12 @@ void PaintToolWindow::Update(float dt)
pos = strokes.front().position;
}
+ Scene& scene = editor->GetCurrentScene();
+ const CameraComponent& camera = editor->GetCurrentEditorScene().camera;
+ const XMMATRIX VP = camera.GetViewProjection();
+ const XMVECTOR F = camera.GetAt();
+ const float brush_rotation = wi::random::GetRandom(0.0f, rotationSlider.GetValue() * XM_2PI);
+
const XMVECTOR spline_p0 = strokes.empty() ? XMVectorSet(posNew.x, posNew.y, pressureNew, 0) : XMVectorSet(strokes[0].position.x, strokes[0].position.y, strokes[0].pressure, 0);
const XMVECTOR spline_p1 = strokes.size() < 2 ? spline_p0 : XMVectorSet(strokes[1].position.x, strokes[1].position.y, strokes[1].pressure, 0);
const XMVECTOR spline_p2 = strokes.size() < 3 ? spline_p1 : XMVectorSet(strokes[2].position.x, strokes[2].position.y, strokes[2].pressure, 0);
@@ -437,6 +517,8 @@ void PaintToolWindow::Update(float dt)
int substep_count = (int)std::ceil(wi::math::Distance(pos, posNew) / (radius * pressureNew));
substep_count = std::max(1, std::min(100, substep_count));
+ wi::jobsystem::context ctx;
+
for (int substep = 0; substep < substep_count; ++substep)
{
const float t = float(substep) / float(substep_count);
@@ -463,189 +545,273 @@ void PaintToolWindow::Update(float dt)
}
const bool painting = pointer_moved && strokes.size() >= stabilizer;
- Scene& scene = editor->GetCurrentScene();
- const CameraComponent& camera = editor->GetCurrentEditorScene().camera;
- const XMVECTOR C = XMLoadFloat2(&pos);
- const XMMATRIX VP = camera.GetViewProjection();
- const XMVECTOR MUL = XMVectorSet(0.5f, -0.5f, 1, 1);
- const XMVECTOR ADD = XMVectorSet(0.5f, 0.5f, 0, 0);
- const XMVECTOR SCREEN = XMVectorSet((float)editor->GetLogicalWidth(), (float)editor->GetLogicalHeight(), 1, 1);
- const XMVECTOR F = camera.GetAt();
- const float brush_rotation = wi::random::GetRandom(0.0f, rotationSlider.GetValue() * XM_2PI);
-
- for (auto& selected : editor->translator.selected)
+ switch (mode)
{
- switch (mode)
+ case MODE_TEXTURE:
+ {
+ wi::primitive::Ray pickRay = wi::renderer::GetPickRay((long)pos.x, (long)pos.y, *editor, camera);
+ brushIntersect = wi::scene::Pick(pickRay, ~0u, ~0u, scene);
+
+ ObjectComponent* object = scene.objects.GetComponent(brushIntersect.entity);
+ if (object == nullptr || object->meshID == INVALID_ENTITY)
+ break;
+
+ MeshComponent* mesh = scene.meshes.GetComponent(object->meshID);
+ if (mesh == nullptr || (mesh->vertex_uvset_0.empty() && mesh->vertex_uvset_1.empty()))
+ break;
+
+ Entity materialID = mesh->subsets[brushIntersect.subsetIndex].materialID;
+ MaterialComponent* material = brushIntersect.subsetIndex >= 0 && brushIntersect.subsetIndex < (int)mesh->subsets.size() ? scene.materials.GetComponent(materialID) : nullptr;
+ if (material == nullptr)
+ break;
+
+ int uvset = 0;
+ TextureSlot editTexture = GetEditTextureSlot(*material, &uvset);
+
+ if (has_flag(editTexture.texture.desc.misc_flags, ResourceMiscFlag::SPARSE))
+ break;
+
+ // Missing texture will be created a blank one:
+ if (!editTexture.texture.IsValid())
{
- case MODE_TEXTURE:
+ std::string texturename = "painttool/";
+ const NameComponent* materialname = scene.names.GetComponent(materialID);
+ if (materialname != nullptr)
+ {
+ texturename += materialname->name;
+ texturename += "_";
+ }
+ texturename += std::to_string(wi::random::GetRandom(std::numeric_limits::max()));
+ texturename += ".PNG";
+ uint64_t sel = textureSlotComboBox.GetItemUserData(textureSlotComboBox.GetSelected());
+ material->textures[sel].name = texturename;
+ material->textures[sel].resource = wi::renderer::CreatePaintableTexture(1024, 1024, 1, wi::Color::White());
+ editTexture = GetEditTextureSlot(*material, &uvset);
+
+ wi::backlog::post("Paint Tool created default texture: " + texturename);
+ }
+
+ if (!editTexture.texture.IsValid())
+ break;
+ const TextureDesc& desc = editTexture.texture.GetDesc();
+ auto& vertex_uvset = uvset == 0 ? mesh->vertex_uvset_0 : mesh->vertex_uvset_1;
+
+ const float u = brushIntersect.bary.x;
+ const float v = brushIntersect.bary.y;
+ const float w = 1 - u - v;
+ XMFLOAT2 uv;
+ uv.x = vertex_uvset[brushIntersect.vertexID0].x * w +
+ vertex_uvset[brushIntersect.vertexID1].x * u +
+ vertex_uvset[brushIntersect.vertexID2].x * v;
+ uv.y = vertex_uvset[brushIntersect.vertexID0].y * w +
+ vertex_uvset[brushIntersect.vertexID1].y * u +
+ vertex_uvset[brushIntersect.vertexID2].y * v;
+ uv.x = uv.x * material->texMulAdd.x + material->texMulAdd.z;
+ uv.y = uv.y * material->texMulAdd.y + material->texMulAdd.w;
+ uint2 center = XMUINT2(uint32_t(int(uv.x * desc.width) % desc.width), uint32_t(int(uv.y * desc.height) % desc.height));
+
+ if (painting)
{
- wi::primitive::Ray pickRay = wi::renderer::GetPickRay((long)pos.x, (long)pos.y, *editor, camera);
- const wi::scene::PickResult intersect = wi::scene::Pick(pickRay, ~0u, ~0u, scene);
- if (intersect.entity != selected.entity)
- break;
+ GraphicsDevice* device = wi::graphics::GetDevice();
+ if (!cmd.IsValid())
+ {
+ cmd = device->BeginCommandList();
+ }
- ObjectComponent* object = scene.objects.GetComponent(selected.entity);
- if (object == nullptr || object->meshID == INVALID_ENTITY)
- break;
+ RecordHistory(materialID, cmd);
- MeshComponent* mesh = scene.meshes.GetComponent(object->meshID);
- if (mesh == nullptr || (mesh->vertex_uvset_0.empty() && mesh->vertex_uvset_1.empty()))
- break;
+ // Need to requery this because RecordHistory might swap textures on material:
+ editTexture = GetEditTextureSlot(*material, &uvset);
- Entity materialID = mesh->subsets[selected.subsetIndex].materialID;
- MaterialComponent* material = selected.subsetIndex >= 0 && selected.subsetIndex < (int)mesh->subsets.size() ? scene.materials.GetComponent(materialID) : nullptr;
- if (material == nullptr)
- break;
+ wi::renderer::PaintTextureParams paintparams = {};
- int uvset = 0;
- TextureSlot editTexture = GetEditTextureSlot(*material, &uvset);
+ paintparams.editTex = editTexture.texture;
+ if (brushTex.IsValid())
+ {
+ paintparams.brushTex = brushTex.GetTexture();
+ }
+ if (revealTex.IsValid())
+ {
+ paintparams.revealTex = revealTex.GetTexture();
+ }
- // Missing texture will be created a blank one:
- if (!editTexture.texture.IsValid())
+ paintparams.push.xPaintBrushCenter = center;
+ paintparams.push.xPaintBrushRadius = (uint32_t)pressure_radius;
+ if (brushShapeComboBox.GetSelected() == 1)
{
- std::string texturename = "painttool/";
- const NameComponent* materialname = scene.names.GetComponent(materialID);
- if (materialname != nullptr)
+ paintparams.push.xPaintBrushRadius = (uint)std::ceil((float(paintparams.push.xPaintBrushRadius) * 2 / std::sqrt(2.0f))); // square shape, diagonal dispatch size
+ }
+ paintparams.push.xPaintBrushAmount = amount;
+ paintparams.push.xPaintBrushSmoothness = smoothness;
+ paintparams.push.xPaintBrushColor = color.rgba;
+ paintparams.push.xPaintRedirectAlpha = alphaCheckBox.GetCheck();
+ paintparams.push.xPaintBrushRotation = brush_rotation;
+ paintparams.push.xPaintBrushShape = (uint)brushShapeComboBox.GetSelected();
+
+ wi::renderer::PaintIntoTexture(paintparams);
+ }
+ if (substep == substep_count - 1)
+ {
+ wi::renderer::PaintRadius paintrad;
+ paintrad.objectEntity = brushIntersect.entity;
+ paintrad.subset = brushIntersect.subsetIndex;
+ paintrad.radius = radius;
+ paintrad.center = center;
+ paintrad.uvset = uvset;
+ paintrad.dimensions.x = desc.width;
+ paintrad.dimensions.y = desc.height;
+ paintrad.rotation = brush_rotation;
+ paintrad.shape = (uint)brushShapeComboBox.GetSelected();
+ wi::renderer::DrawPaintRadius(paintrad);
+ }
+ }
+ break;
+
+ case MODE_TERRAIN_MATERIAL:
+ {
+ Ray pickRay = wi::renderer::GetPickRay((long)pos.x, (long)pos.y, *editor, camera);
+ brushIntersect = wi::scene::Pick(pickRay, wi::enums::FILTER_TERRAIN, ~0u, scene);
+ if (brushIntersect.entity == INVALID_ENTITY)
+ break;
+
+ const Sphere sphere = Sphere(brushIntersect.position, radius);
+ const XMVECTOR CENTER = XMLoadFloat3(&sphere.center);
+
+ wi::terrain::Terrain* terrain = nullptr;
+ for (size_t i = 0; i < scene.terrains.GetCount(); ++i)
+ {
+ for (auto& chunk : scene.terrains[i].chunks)
+ {
+ if (brushIntersect.entity == chunk.second.entity)
{
- texturename += materialname->name;
- texturename += "_";
+ terrain = &scene.terrains[i];
+ break;
}
- texturename += std::to_string(wi::random::GetRandom(std::numeric_limits::max()));
- texturename += ".PNG";
- uint64_t sel = textureSlotComboBox.GetItemUserData(textureSlotComboBox.GetSelected());
- material->textures[sel].name = texturename;
- material->textures[sel].resource = wi::renderer::CreatePaintableTexture(1024, 1024, 1, wi::Color::White());
- editTexture = GetEditTextureSlot(*material, &uvset);
-
- wi::backlog::post("Paint Tool created default texture: " + texturename);
}
+ }
+ if (terrain == nullptr)
+ break;
- if (!editTexture.texture.IsValid())
- break;
- const TextureDesc& desc = editTexture.texture.GetDesc();
- auto& vertex_uvset = uvset == 0 ? mesh->vertex_uvset_0 : mesh->vertex_uvset_1;
-
- const float u = intersect.bary.x;
- const float v = intersect.bary.y;
- const float w = 1 - u - v;
- XMFLOAT2 uv;
- uv.x = vertex_uvset[intersect.vertexID0].x * w +
- vertex_uvset[intersect.vertexID1].x * u +
- vertex_uvset[intersect.vertexID2].x * v;
- uv.y = vertex_uvset[intersect.vertexID0].y * w +
- vertex_uvset[intersect.vertexID1].y * u +
- vertex_uvset[intersect.vertexID2].y * v;
- uv.x = uv.x * material->texMulAdd.x + material->texMulAdd.z;
- uv.y = uv.y * material->texMulAdd.y + material->texMulAdd.w;
- uint2 center = XMUINT2(uint32_t(uv.x * desc.width), uint32_t(uv.y * desc.height));
+ for (auto& chunk : terrain->chunks)
+ {
+ auto& chunk_data = chunk.second;
+
+ const ObjectComponent* object = scene.objects.GetComponent(chunk_data.entity);
+ if (object == nullptr || object->meshID == INVALID_ENTITY)
+ continue;
+
+ const size_t objectIndex = scene.objects.GetIndex(chunk_data.entity);
+ const wi::primitive::AABB& aabb = scene.aabb_objects[objectIndex];
+ if (!sphere.intersects(aabb))
+ continue;
+
+ const MeshComponent* mesh = scene.meshes.GetComponent(chunk_data.entity);
+ if (mesh == nullptr || (mesh->vertex_uvset_0.empty() && mesh->vertex_uvset_1.empty()))
+ continue;
+
+ const XMMATRIX W = XMLoadFloat4x4(&scene.matrix_objects[objectIndex]);
if (painting)
{
- GraphicsDevice* device = wi::graphics::GetDevice();
- if (!cmd.IsValid())
+ chunk_data.enable_blendmap_layer(terrain_material_layer);
+ uint8_t* pixels = chunk_data.blendmap_layers[terrain_material_layer].pixels.data();
+
+ bool rebuild = false;
+ for (size_t j = 0; j < mesh->vertex_positions.size(); ++j)
{
- cmd = device->BeginCommandList();
- }
+ XMVECTOR P = XMLoadFloat3(&mesh->vertex_positions[j]);
+ P = XMVector3Transform(P, W);
- RecordHistory(true, cmd);
+ if (!sphere.intersects(P))
+ continue;
- // Need to requery this because RecordHistory might swap textures on material:
- editTexture = GetEditTextureSlot(*material, &uvset);
+ XMVECTOR N = XMLoadFloat3(&mesh->vertex_normals[j]);
+ N = XMVector3Normalize(XMVector3TransformNormal(N, W));
- wi::renderer::PaintTextureParams paintparams = {};
+ if (!backfaces && XMVectorGetX(XMVector3Dot(F, N)) > 0)
+ continue;
- paintparams.editTex = editTexture.texture;
- if (brushTex.IsValid())
- {
- paintparams.brushTex = brushTex.GetTexture();
- }
- if (revealTex.IsValid())
- {
- paintparams.revealTex = revealTex.GetTexture();
- }
+ const float dist = wi::math::Distance(P, CENTER);
+ if (dist <= pressure_radius)
+ {
+ RecordHistory(chunk_data.entity);
+ rebuild = true;
+ const float affection = amount * wi::math::SmoothStep(0, smoothness, 1 - dist / pressure_radius);
+
+ float vcol = float(pixels[j]) / 255.0f;
+ vcol = wi::math::Lerp(vcol, 1, affection);
+ pixels[j] = uint8_t(vcol * 255);
- paintparams.push.xPaintBrushCenter = center;
- paintparams.push.xPaintBrushRadius = (uint32_t)pressure_radius;
- if (brushShapeComboBox.GetSelected() == 1)
+ // blend out layers above:
+ for (size_t l = terrain_material_layer + 1; l < chunk_data.blendmap_layers.size(); ++l)
+ {
+ vcol = float(chunk_data.blendmap_layers[l].pixels[j]) / 255.0f;
+ vcol = wi::math::Lerp(vcol, 0, affection);
+ chunk_data.blendmap_layers[l].pixels[j] = uint8_t(vcol * 255);
+ }
+ }
+ }
+ if (rebuild)
{
- paintparams.push.xPaintBrushRadius = (uint)std::ceil((float(paintparams.push.xPaintBrushRadius) * 2 / std::sqrt(2.0f))); // square shape, diagonal dispatch size
+ chunk_data.blendmap = {};
+ terrain->CreateChunkRegionTexture(chunk_data);
+ if (chunk_data.vt != nullptr)
+ {
+ chunk_data.vt->invalidate();
+ }
}
- paintparams.push.xPaintBrushAmount = amount;
- paintparams.push.xPaintBrushSmoothness = smoothness;
- paintparams.push.xPaintBrushColor = color.rgba;
- paintparams.push.xPaintRedirectAlpha = alphaCheckBox.GetCheck();
- paintparams.push.xPaintBrushRotation = brush_rotation;
- paintparams.push.xPaintBrushShape = (uint)brushShapeComboBox.GetSelected();
-
- wi::renderer::PaintIntoTexture(paintparams);
- }
- if(substep == substep_count - 1)
- {
- wi::renderer::PaintRadius paintrad;
- paintrad.objectEntity = selected.entity;
- paintrad.subset = selected.subsetIndex;
- paintrad.radius = radius;
- paintrad.center = center;
- paintrad.uvset = uvset;
- paintrad.dimensions.x = desc.width;
- paintrad.dimensions.y = desc.height;
- paintrad.rotation = brush_rotation;
- paintrad.shape = (uint)brushShapeComboBox.GetSelected();
- wi::renderer::DrawPaintRadius(paintrad);
}
}
- break;
+ }
+ break;
- case MODE_VERTEXCOLOR:
- case MODE_WIND:
+ case MODE_VERTEXCOLOR:
+ case MODE_WIND:
+ {
+ Ray pickRay = wi::renderer::GetPickRay((long)pos.x, (long)pos.y, *editor, camera);
+ brushIntersect = wi::scene::Pick(pickRay, wi::enums::FILTER_OBJECT_ALL, ~0u, scene);
+ if (brushIntersect.entity == INVALID_ENTITY)
+ break;
+
+ const Sphere sphere = Sphere(brushIntersect.position, radius);
+ const XMVECTOR CENTER = XMLoadFloat3(&sphere.center);
+
+ for (size_t objectIndex = 0; objectIndex < scene.objects.GetCount(); ++objectIndex)
{
- ObjectComponent* object = scene.objects.GetComponent(selected.entity);
- if (object == nullptr || object->meshID == INVALID_ENTITY)
- break;
+ if (!sphere.intersects(scene.aabb_objects[objectIndex]))
+ continue;
- MeshComponent* mesh = scene.meshes.GetComponent(object->meshID);
+ Entity entity = scene.objects.GetEntity(objectIndex);
+ ObjectComponent& object = scene.objects[objectIndex];
+ if (object.meshID == INVALID_ENTITY)
+ continue;
+
+ MeshComponent* mesh = scene.meshes.GetComponent(object.meshID);
if (mesh == nullptr)
- break;
+ continue;
- MaterialComponent* material = selected.subsetIndex >= 0 && selected.subsetIndex < (int)mesh->subsets.size() ? scene.materials.GetComponent(mesh->subsets[selected.subsetIndex].materialID) : nullptr;
- if (material == nullptr)
+ for (auto& x : mesh->subsets)
{
- for (auto& x : mesh->subsets)
+ MaterialComponent* material = scene.materials.GetComponent(x.materialID);
+ if (material != nullptr)
{
- material = scene.materials.GetComponent(x.materialID);
- if (material != nullptr)
+ switch (mode)
{
- switch (mode)
- {
- case MODE_VERTEXCOLOR:
- material->SetUseVertexColors(true);
- break;
- case MODE_WIND:
- material->SetUseWind(true);
- break;
- }
+ case MODE_VERTEXCOLOR:
+ material->SetUseVertexColors(true);
+ break;
+ case MODE_WIND:
+ material->SetUseWind(true);
+ break;
}
}
- material = nullptr;
- }
- else
- {
- switch (mode)
- {
- case MODE_VERTEXCOLOR:
- material->SetUseVertexColors(true);
- break;
- case MODE_WIND:
- material->SetUseWind(true);
- break;
- }
}
const ArmatureComponent* armature = mesh->IsSkinned() ? scene.armatures.GetComponent(mesh->armatureID) : nullptr;
- const TransformComponent* transform = scene.transforms.GetComponent(selected.entity);
+ const TransformComponent* transform = scene.transforms.GetComponent(entity);
if (transform == nullptr)
- break;
+ continue;
const XMMATRIX W = XMLoadFloat4x4(&transform->world);
@@ -691,15 +857,10 @@ void PaintToolWindow::Update(float dt)
if (!backfaces && XMVectorGetX(XMVector3Dot(F, N)) > 0)
continue;
- P = XMVector3TransformCoord(P, VP);
- P = P * MUL + ADD;
- P = P * SCREEN;
-
- const float z = XMVectorGetZ(P);
- const float dist = XMVectorGetX(XMVector2Length(C - P));
- if (z >= 0 && z <= 1 && dist <= pressure_radius)
+ const float dist = wi::math::Distance(P, CENTER);
+ if (dist <= pressure_radius)
{
- RecordHistory(true);
+ RecordHistory(object.meshID);
rebuild = true;
const float affection = amount * wi::math::SmoothStep(0, smoothness, 1 - dist / pressure_radius);
@@ -772,24 +933,41 @@ void PaintToolWindow::Update(float dt)
mesh->CreateRenderData();
}
}
- break;
+ }
+ break;
+
+ case MODE_SCULPTING_ADD:
+ case MODE_SCULPTING_SUBTRACT:
+ {
+ Ray pickRay = wi::renderer::GetPickRay((long)pos.x, (long)pos.y, *editor, camera);
+ brushIntersect = wi::scene::Pick(pickRay, terrain_only ? wi::enums::FILTER_TERRAIN : wi::enums::FILTER_OBJECT_ALL, ~0u, scene);
+ if (brushIntersect.entity == INVALID_ENTITY)
+ break;
+
+ const Sphere sphere = Sphere(brushIntersect.position, radius);
+ const XMVECTOR CENTER = XMLoadFloat3(&sphere.center);
- case MODE_SCULPTING_ADD:
- case MODE_SCULPTING_SUBTRACT:
+ for (size_t objectIndex = 0; objectIndex < scene.objects.GetCount(); ++objectIndex)
{
- ObjectComponent* object = scene.objects.GetComponent(selected.entity);
- if (object == nullptr || object->meshID == INVALID_ENTITY)
- break;
+ if (!sphere.intersects(scene.aabb_objects[objectIndex]))
+ continue;
+
+ Entity entity = scene.objects.GetEntity(objectIndex);
+ ObjectComponent& object = scene.objects[objectIndex];
+ if (object.meshID == INVALID_ENTITY)
+ continue;
+ if (terrain_only && (object.GetFilterMask() & wi::enums::FILTER_TERRAIN) == 0)
+ continue;
- MeshComponent* mesh = scene.meshes.GetComponent(object->meshID);
+ MeshComponent* mesh = scene.meshes.GetComponent(object.meshID);
if (mesh == nullptr)
- break;
+ continue;
const ArmatureComponent* armature = mesh->IsSkinned() ? scene.armatures.GetComponent(mesh->armatureID) : nullptr;
- const TransformComponent* transform = scene.transforms.GetComponent(selected.entity);
+ const TransformComponent* transform = scene.transforms.GetComponent(entity);
if (transform == nullptr)
- break;
+ continue;
const XMMATRIX W = XMLoadFloat4x4(&transform->world);
@@ -819,7 +997,6 @@ void PaintToolWindow::Update(float dt)
break;
}
-
bool rebuild = false;
if (painting)
{
@@ -845,22 +1022,35 @@ void PaintToolWindow::Update(float dt)
if (!backfaces && dotN > 0)
continue;
- P = XMVector3TransformCoord(P, VP);
- P = P * MUL + ADD;
- P = P * SCREEN;
-
- const float z = XMVectorGetZ(P);
- const float dist = XMVectorGetX(XMVector2Length(C - P));
- if (z >= 0 && z <= 1 && dist <= pressure_radius)
+ const float dist = wi::math::Distance(P, CENTER);
+ if (dist <= pressure_radius)
{
const float affection = amount * wi::math::SmoothStep(0, smoothness, 1 - dist / pressure_radius);
sculpting_indices.push_back({ j, affection });
}
}
+ wi::terrain::Terrain* terrain = nullptr;
+ wi::terrain::ChunkData* chunk_data = nullptr;
+ if (object.GetFilterMask() & wi::enums::FILTER_TERRAIN)
+ {
+ for (size_t i = 0; i < scene.terrains.GetCount(); ++i)
+ {
+ for (auto& it : scene.terrains[i].chunks)
+ {
+ if (it.second.entity == entity)
+ {
+ terrain = &scene.terrains[i];
+ chunk_data = &it.second;
+ break;
+ }
+ }
+ }
+ }
+
if (!sculpting_indices.empty())
{
- RecordHistory(true);
+ RecordHistory(object.meshID);
rebuild = true;
for (auto& x : sculpting_indices)
@@ -876,7 +1066,26 @@ void PaintToolWindow::Update(float dt)
break;
}
XMStoreFloat3(&mesh->vertex_positions[x.ind], PL);
+
+ if (chunk_data != nullptr)
+ {
+ float height = mesh->vertex_positions[x.ind].y;
+ chunk_data->heightmap_data[x.ind] = uint16_t(wi::math::InverseLerp(terrain->bottomLevel, terrain->topLevel, height) * 65535);
+ }
}
+
+ wi::jobsystem::Execute(ctx, [=](wi::jobsystem::JobArgs args) {
+ if (mesh->bvh.IsValid())
+ {
+ mesh->BuildBVH();
+ }
+
+ if (chunk_data != nullptr)
+ {
+ chunk_data->heightmap = {};
+ terrain->CreateChunkRegionTexture(*chunk_data);
+ }
+ });
}
}
@@ -926,25 +1135,42 @@ void PaintToolWindow::Update(float dt)
if (rebuild)
{
- mesh->ComputeNormals(MeshComponent::COMPUTE_NORMALS_SMOOTH_FAST);
+ wi::jobsystem::Execute(ctx, [=](wi::jobsystem::JobArgs args) {
+ mesh->ComputeNormals(MeshComponent::COMPUTE_NORMALS_SMOOTH_FAST);
+ });
}
}
- break;
+ }
+ break;
+
+ case MODE_SOFTBODY_PINNING:
+ case MODE_SOFTBODY_PHYSICS:
+ {
+ Ray pickRay = wi::renderer::GetPickRay((long)pos.x, (long)pos.y, *editor, camera);
+ brushIntersect = wi::scene::Pick(pickRay, wi::enums::FILTER_OBJECT_ALL, ~0u, scene);
+ if (brushIntersect.entity == INVALID_ENTITY)
+ break;
- case MODE_SOFTBODY_PINNING:
- case MODE_SOFTBODY_PHYSICS:
+ const Sphere sphere = Sphere(brushIntersect.position, radius);
+ const XMVECTOR CENTER = XMLoadFloat3(&sphere.center);
+
+ for (size_t objectIndex = 0; objectIndex < scene.objects.GetCount(); ++objectIndex)
{
- ObjectComponent* object = scene.objects.GetComponent(selected.entity);
- if (object == nullptr || object->meshID == INVALID_ENTITY)
- break;
+ if (!sphere.intersects(scene.aabb_objects[objectIndex]))
+ continue;
+
+ Entity entity = scene.objects.GetEntity(objectIndex);
+ ObjectComponent& object = scene.objects[objectIndex];
+ if (object.meshID == INVALID_ENTITY)
+ continue;
- const MeshComponent* mesh = scene.meshes.GetComponent(object->meshID);
+ const MeshComponent* mesh = scene.meshes.GetComponent(object.meshID);
if (mesh == nullptr)
- break;
+ continue;
- SoftBodyPhysicsComponent* softbody = scene.softbodies.GetComponent(object->meshID);
+ SoftBodyPhysicsComponent* softbody = scene.softbodies.GetComponent(object.meshID);
if (softbody == nullptr || !softbody->HasVertices())
- break;
+ continue;
// Painting:
if (painting)
@@ -953,13 +1179,11 @@ void PaintToolWindow::Update(float dt)
for (auto& ind : softbody->physicsToGraphicsVertexMapping)
{
XMVECTOR P = softbody->vertex_positions_simulation[ind].LoadPOS();
- P = XMVector3TransformCoord(P, VP);
- P = P * MUL + ADD;
- P = P * SCREEN;
- const float z = XMVectorGetZ(P);
- if (z >= 0 && z <= 1 && XMVectorGetX(XMVector2Length(C - P)) <= pressure_radius)
+
+ const float dist = wi::math::Distance(P, CENTER);
+ if (dist <= pressure_radius)
{
- RecordHistory(true);
+ RecordHistory(object.meshID);
softbody->weights[j] = (mode == MODE_SOFTBODY_PINNING ? 0.0f : 1.0f);
softbody->_flags |= SoftBodyPhysicsComponent::FORCE_RESET;
}
@@ -1023,25 +1247,37 @@ void PaintToolWindow::Update(float dt)
}
}
}
- break;
+ }
+ break;
+
+ case MODE_HAIRPARTICLE_ADD_TRIANGLE:
+ case MODE_HAIRPARTICLE_REMOVE_TRIANGLE:
+ case MODE_HAIRPARTICLE_LENGTH:
+ {
+ Ray pickRay = wi::renderer::GetPickRay((long)pos.x, (long)pos.y, *editor, camera);
+ brushIntersect = wi::scene::Pick(pickRay, wi::enums::FILTER_OBJECT_ALL, ~0u, scene);
+ if (brushIntersect.entity == INVALID_ENTITY)
+ break;
- case MODE_HAIRPARTICLE_ADD_TRIANGLE:
- case MODE_HAIRPARTICLE_REMOVE_TRIANGLE:
- case MODE_HAIRPARTICLE_LENGTH:
+ const Sphere sphere = Sphere(brushIntersect.position, radius);
+ const XMVECTOR CENTER = XMLoadFloat3(&sphere.center);
+
+ for (size_t i = 0; i < scene.hairs.GetCount(); ++i)
{
- wi::HairParticleSystem* hair = scene.hairs.GetComponent(selected.entity);
- if (hair == nullptr || hair->meshID == INVALID_ENTITY)
- break;
+ wi::HairParticleSystem& hair = scene.hairs[i];
+ if (hair.meshID == INVALID_ENTITY || !sphere.intersects(hair.aabb))
+ continue;
- MeshComponent* mesh = scene.meshes.GetComponent(hair->meshID);
+ const Entity hairEntity = scene.hairs.GetEntity(i);
+ MeshComponent* mesh = scene.meshes.GetComponent(hair.meshID);
if (mesh == nullptr)
- break;
+ continue;
const ArmatureComponent* armature = mesh->IsSkinned() ? scene.armatures.GetComponent(mesh->armatureID) : nullptr;
- const TransformComponent* transform = scene.transforms.GetComponent(selected.entity);
+ const TransformComponent* transform = scene.transforms.GetComponent(hairEntity);
if (transform == nullptr)
- break;
+ continue;
const XMMATRIX W = XMLoadFloat4x4(&transform->world);
@@ -1065,35 +1301,30 @@ void PaintToolWindow::Update(float dt)
if (!backfaces && XMVectorGetX(XMVector3Dot(F, N)) > 0)
continue;
- P = XMVector3TransformCoord(P, VP);
- P = P * MUL + ADD;
- P = P * SCREEN;
-
- const float z = XMVectorGetZ(P);
- const float dist = XMVectorGetX(XMVector2Length(C - P));
- if (z >= 0 && z <= 1 && dist <= pressure_radius)
+ const float dist = wi::math::Distance(P, CENTER);
+ if (dist <= pressure_radius)
{
- RecordHistory(true);
+ RecordHistory(hairEntity);
switch (mode)
{
case MODE_HAIRPARTICLE_ADD_TRIANGLE:
- hair->vertex_lengths[j] = 1.0f;
+ hair.vertex_lengths[j] = 1.0f;
break;
case MODE_HAIRPARTICLE_REMOVE_TRIANGLE:
- hair->vertex_lengths[j] = 0;
+ hair.vertex_lengths[j] = 0;
break;
case MODE_HAIRPARTICLE_LENGTH:
- if (hair->vertex_lengths[j] > 0) // don't change distribution
+ if (hair.vertex_lengths[j] > 0) // don't change distribution
{
const float affection = amount * wi::math::SmoothStep(0, smoothness, 1 - dist / pressure_radius);
- hair->vertex_lengths[j] = wi::math::Lerp(hair->vertex_lengths[j], color_float.w, affection);
+ hair.vertex_lengths[j] = wi::math::Lerp(hair.vertex_lengths[j], color_float.w, affection);
// don't let it "remove" the vertex by keeping its length above zero:
// (because if removed, distribution also changes which might be distracting)
- hair->vertex_lengths[j] = wi::math::Clamp(hair->vertex_lengths[j], 1.0f / 255.0f, 1.0f);
+ hair.vertex_lengths[j] = wi::math::Clamp(hair.vertex_lengths[j], 1.0f / 255.0f, 1.0f);
}
break;
}
- hair->_flags |= wi::HairParticleSystem::REBUILD_BUFFERS;
+ hair._flags |= wi::HairParticleSystem::REBUILD_BUFFERS;
}
}
}
@@ -1127,12 +1358,12 @@ void PaintToolWindow::Update(float dt)
}
}
- for (size_t j = 0; j < hair->indices.size() && wireframe; j += 3)
+ for (size_t j = 0; j < hair.indices.size() && wireframe; j += 3)
{
const uint32_t triangle[] = {
- hair->indices[j + 0],
- hair->indices[j + 1],
- hair->indices[j + 2],
+ hair.indices[j + 0],
+ hair.indices[j + 1],
+ hair.indices[j + 2],
};
const XMVECTOR P[arraysize(triangle)] = {
XMVector3Transform(armature == nullptr ? XMLoadFloat3(&mesh->vertex_positions[triangle[0]]) : wi::scene::SkinVertex(*mesh, *armature, triangle[0]), W),
@@ -1149,10 +1380,12 @@ void PaintToolWindow::Update(float dt)
}
}
}
- break;
+ }
+ break;
- }
}
+
+ wi::jobsystem::Wait(ctx);
}
while (strokes.size() > stabilizer)
@@ -1207,11 +1440,18 @@ void PaintToolWindow::DrawBrush(const wi::Canvas& canvas, CommandList cmd) const
LoadShaders();
}
+ const CameraComponent& camera = editor->GetCurrentEditorScene().camera;
+
GraphicsDevice* device = wi::graphics::GetDevice();
device->EventBegin("Paint Tool", cmd);
device->BindPipelineState(&pso, cmd);
+ const float brushRadius = radiusSlider.GetValue();
+ XMFLOAT4X4 brushMatrix = brushIntersect.orientation;
+
+ const XMMATRIX W = XMMatrixRotationY(rot) * XMMatrixScaling(brushRadius, brushRadius, brushRadius) * XMLoadFloat4x4(&brushMatrix);
+
const uint32_t segmentCount = 36;
const uint32_t circle_triangleCount = segmentCount * 2;
uint32_t vertexCount = circle_triangleCount * 3;
@@ -1223,19 +1463,24 @@ void PaintToolWindow::DrawBrush(const wi::Canvas& canvas, CommandList cmd) const
const float angle1 = (float)(i + 1) / (float)segmentCount * XM_2PI;
// circle:
- const float radius = radiusSlider.GetValue();
- const float radius_outer = radius + 8;
+ const float radius = 1;
+ const float radius_outer = radius + 0.1f;
float brightness = i % 2 == 0 ? 1.0f : 0.0f;
XMFLOAT4 color_inner = XMFLOAT4(brightness, brightness, brightness, 1);
XMFLOAT4 color_outer = XMFLOAT4(brightness, brightness, brightness, 0);
- const Vertex verts[] = {
- {XMFLOAT4(std::sin(angle0) * radius, std::cos(angle0) * radius, 0, 1), color_inner},
- {XMFLOAT4(std::sin(angle1) * radius, std::cos(angle1) * radius, 0, 1), color_inner},
- {XMFLOAT4(std::sin(angle0) * radius_outer, std::cos(angle0) * radius_outer, 0, 1), color_outer},
- {XMFLOAT4(std::sin(angle0) * radius_outer, std::cos(angle0) * radius_outer, 0, 1), color_outer},
- {XMFLOAT4(std::sin(angle1) * radius_outer, std::cos(angle1) * radius_outer, 0, 1), color_outer},
- {XMFLOAT4(std::sin(angle1) * radius, std::cos(angle1) * radius, 0, 1), color_inner},
+ Vertex verts[] = {
+ {XMFLOAT4(std::sin(angle0) * radius, 0, std::cos(angle0) * radius, 1), color_inner},
+ {XMFLOAT4(std::sin(angle1) * radius, 0, std::cos(angle1) * radius, 1), color_inner},
+ {XMFLOAT4(std::sin(angle0) * radius_outer, 0, std::cos(angle0) * radius_outer, 1), color_outer},
+ {XMFLOAT4(std::sin(angle0) * radius_outer, 0, std::cos(angle0) * radius_outer, 1), color_outer},
+ {XMFLOAT4(std::sin(angle1) * radius_outer, 0, std::cos(angle1) * radius_outer, 1), color_outer},
+ {XMFLOAT4(std::sin(angle1) * radius, 0, std::cos(angle1) * radius, 1), color_inner},
};
+ for (auto& x : verts)
+ {
+ XMStoreFloat4(&x.position, XMVector3Transform(XMLoadFloat4(&x.position), W));
+ x.position.w = 1;
+ }
std::memcpy(dst, verts, sizeof(verts));
dst += sizeof(verts);
}
@@ -1252,7 +1497,7 @@ void PaintToolWindow::DrawBrush(const wi::Canvas& canvas, CommandList cmd) const
device->BindVertexBuffers(vbs, 0, arraysize(vbs), strides, offsets, cmd);
MiscCB sb;
- XMStoreFloat4x4(&sb.g_xTransform, XMMatrixRotationZ(rot) * XMMatrixTranslation(pos.x, pos.y, 0) * canvas.GetProjection());
+ XMStoreFloat4x4(&sb.g_xTransform, camera.GetViewProjection());
sb.g_xColor = XMFLOAT4(1, 1, 1, 1);
device->BindDynamicConstantBuffer(sb, CBSLOT_RENDERER_MISC, cmd);
device->Draw(vertexCount, 0, cmd);
@@ -1263,185 +1508,184 @@ void PaintToolWindow::DrawBrush(const wi::Canvas& canvas, CommandList cmd) const
PaintToolWindow::MODE PaintToolWindow::GetMode() const
{
- return (MODE)modeComboBox.GetSelected();
+ return (MODE)modeComboBox.GetSelectedUserdata();
}
-void PaintToolWindow::RecordHistory(bool start, CommandList cmd)
+void PaintToolWindow::WriteHistoryData(Entity entity, wi::Archive& archive, CommandList cmd)
{
- if (start)
- {
- if (history_needs_recording_start)
- return;
- history_needs_recording_start = true;
- history_needs_recording_end = true;
-
- // Start history recording (undo)
- currentHistory = &editor->AdvanceHistory();
- wi::Archive& archive = *currentHistory;
- archive << EditorComponent::HISTORYOP_PAINTTOOL;
- archive << (uint32_t)GetMode();
- history_redo_jump_position = archive.WriteUnknownJumpPosition();
- }
- else
- {
- if (!history_needs_recording_end || !strokes.empty())
- return;
- history_needs_recording_end = false;
- history_needs_recording_start = false;
-
- // End history recording (redo)
- }
-
- wi::Archive& archive = *currentHistory;
Scene& scene = editor->GetCurrentScene();
- for (auto& selected : editor->translator.selected)
+ switch (GetMode())
{
- switch (GetMode())
- {
- case PaintToolWindow::MODE_TEXTURE:
- {
- ObjectComponent* object = scene.objects.GetComponent(selected.entity);
- if (object == nullptr || object->meshID == INVALID_ENTITY)
- break;
-
- MeshComponent* mesh = scene.meshes.GetComponent(object->meshID);
- if (mesh == nullptr || (mesh->vertex_uvset_0.empty() && mesh->vertex_uvset_1.empty()))
- break;
+ case PaintToolWindow::MODE_TEXTURE:
+ {
+ MaterialComponent* material = scene.materials.GetComponent(entity);
+ if (material == nullptr)
+ break;
- MaterialComponent* material = selected.subsetIndex >= 0 && selected.subsetIndex < (int)mesh->subsets.size() ? scene.materials.GetComponent(mesh->subsets[selected.subsetIndex].materialID) : nullptr;
- if (material == nullptr)
- break;
+ auto editTexture = GetEditTextureSlot(*material);
- auto editTexture = GetEditTextureSlot(*material);
+ archive << textureSlotComboBox.GetSelected();
- archive << textureSlotComboBox.GetSelected();
+ if (history_textureIndex >= history_textures.size())
+ {
+ history_textures.resize((history_textureIndex + 1) * 2);
+ }
+ history_textures[history_textureIndex] = editTexture;
+ archive << history_textureIndex;
+ history_textureIndex++;
- if (history_textureIndex >= history_textures.size())
+ if (cmd.IsValid())
+ {
+ // Make a copy of texture to edit and replace material resource:
+ GraphicsDevice* device = wi::graphics::GetDevice();
+ TextureSlot newslot;
+ TextureDesc desc = editTexture.texture.GetDesc();
+ desc.format = Format::R8G8B8A8_UNORM; // force format to one that is writable by GPU
+ desc.bind_flags |= BindFlag::SHADER_RESOURCE | BindFlag::UNORDERED_ACCESS;
+ desc.misc_flags = ResourceMiscFlag::TYPED_FORMAT_CASTING;
+ device->CreateTexture(&desc, nullptr, &newslot.texture);
+ for (uint32_t i = 0; i < newslot.texture.GetDesc().mip_levels; ++i)
{
- history_textures.resize((history_textureIndex + 1) * 2);
+ int subresource_index;
+ subresource_index = device->CreateSubresource(&newslot.texture, SubresourceType::SRV, 0, 1, i, 1);
+ assert(subresource_index == i);
+ subresource_index = device->CreateSubresource(&newslot.texture, SubresourceType::UAV, 0, 1, i, 1);
+ assert(subresource_index == i);
}
- history_textures[history_textureIndex] = editTexture;
- archive << history_textureIndex;
- history_textureIndex++;
-
- if (start)
+ // This part must be AFTER mip level subresource creation:
+ int srgb_subresource = -1;
{
- // Make a copy of texture to edit and replace material resource:
- GraphicsDevice* device = wi::graphics::GetDevice();
- TextureSlot newslot;
- TextureDesc desc = editTexture.texture.GetDesc();
- desc.format = Format::R8G8B8A8_UNORM; // force format to one that is writable by GPU
- desc.bind_flags |= BindFlag::SHADER_RESOURCE | BindFlag::UNORDERED_ACCESS;
- desc.misc_flags = ResourceMiscFlag::TYPED_FORMAT_CASTING;
- device->CreateTexture(&desc, nullptr, &newslot.texture);
- for (uint32_t i = 0; i < newslot.texture.GetDesc().mip_levels; ++i)
- {
- int subresource_index;
- subresource_index = device->CreateSubresource(&newslot.texture, SubresourceType::SRV, 0, 1, i, 1);
- assert(subresource_index == i);
- subresource_index = device->CreateSubresource(&newslot.texture, SubresourceType::UAV, 0, 1, i, 1);
- assert(subresource_index == i);
- }
- // This part must be AFTER mip level subresource creation:
- int srgb_subresource = -1;
- {
- Format srgb_format = GetFormatSRGB(desc.format);
- newslot.srgb_subresource = device->CreateSubresource(
- &newslot.texture,
- SubresourceType::SRV,
- 0, -1,
- 0, -1,
- &srgb_format
- );
- }
- assert(cmd.IsValid());
- wi::renderer::CopyTexture2D(
- newslot.texture, 0, 0, 0,
- editTexture.texture, 0, 0, 0,
- cmd
- ); // custom copy with format conversion and decompression capability!
-
- ReplaceEditTextureSlot(*material, newslot);
+ Format srgb_format = GetFormatSRGB(desc.format);
+ newslot.srgb_subresource = device->CreateSubresource(
+ &newslot.texture,
+ SubresourceType::SRV,
+ 0, -1,
+ 0, -1,
+ &srgb_format
+ );
}
+ wi::renderer::CopyTexture2D(
+ newslot.texture, 0, 0, 0,
+ editTexture.texture, 0, 0, 0,
+ cmd
+ ); // custom copy with format conversion and decompression capability!
+ ReplaceEditTextureSlot(*material, newslot);
}
- break;
- case PaintToolWindow::MODE_VERTEXCOLOR:
- {
- ObjectComponent* object = scene.objects.GetComponent(selected.entity);
- if (object == nullptr || object->meshID == INVALID_ENTITY)
- break;
- MeshComponent* mesh = scene.meshes.GetComponent(object->meshID);
- if (mesh == nullptr)
- break;
+ }
+ break;
+ case PaintToolWindow::MODE_VERTEXCOLOR:
+ {
+ MeshComponent* mesh = scene.meshes.GetComponent(entity);
+ if (mesh == nullptr)
+ break;
- archive << mesh->vertex_colors;
- }
- break;
- case PaintToolWindow::MODE_WIND:
- {
- ObjectComponent* object = scene.objects.GetComponent(selected.entity);
- if (object == nullptr || object->meshID == INVALID_ENTITY)
- break;
+ archive << mesh->vertex_colors;
+ }
+ break;
+ case PaintToolWindow::MODE_WIND:
+ {
+ MeshComponent* mesh = scene.meshes.GetComponent(entity);
+ if (mesh == nullptr)
+ break;
- MeshComponent* mesh = scene.meshes.GetComponent(object->meshID);
- if (mesh == nullptr)
- break;
+ archive << mesh->vertex_windweights;
+ }
+ break;
+ case PaintToolWindow::MODE_SCULPTING_ADD:
+ case PaintToolWindow::MODE_SCULPTING_SUBTRACT:
+ {
+ MeshComponent* mesh = scene.meshes.GetComponent(entity);
+ if (mesh == nullptr)
+ break;
- archive << mesh->vertex_windweights;
- }
- break;
- case PaintToolWindow::MODE_SCULPTING_ADD:
- case PaintToolWindow::MODE_SCULPTING_SUBTRACT:
- {
- ObjectComponent* object = scene.objects.GetComponent(selected.entity);
- if (object == nullptr || object->meshID == INVALID_ENTITY)
- break;
+ archive << mesh->vertex_positions;
+ archive << mesh->vertex_normals;
+ }
+ break;
+ case PaintToolWindow::MODE_SOFTBODY_PINNING:
+ case PaintToolWindow::MODE_SOFTBODY_PHYSICS:
+ {
+ SoftBodyPhysicsComponent* softbody = scene.softbodies.GetComponent(entity);
+ if (softbody == nullptr)
+ break;
- MeshComponent* mesh = scene.meshes.GetComponent(object->meshID);
- if (mesh == nullptr)
- break;
+ archive << softbody->weights;
+ }
+ break;
+ case PaintToolWindow::MODE_HAIRPARTICLE_ADD_TRIANGLE:
+ case PaintToolWindow::MODE_HAIRPARTICLE_REMOVE_TRIANGLE:
+ case PaintToolWindow::MODE_HAIRPARTICLE_LENGTH:
+ {
+ wi::HairParticleSystem* hair = scene.hairs.GetComponent(entity);
+ if (hair == nullptr)
+ break;
- archive << mesh->vertex_positions;
- archive << mesh->vertex_normals;
- }
- break;
- case PaintToolWindow::MODE_SOFTBODY_PINNING:
- case PaintToolWindow::MODE_SOFTBODY_PHYSICS:
+ archive << hair->vertex_lengths;
+ }
+ break;
+ case PaintToolWindow::MODE_TERRAIN_MATERIAL:
{
- ObjectComponent* object = scene.objects.GetComponent(selected.entity);
- if (object == nullptr || object->meshID == INVALID_ENTITY)
- break;
-
- SoftBodyPhysicsComponent* softbody = scene.softbodies.GetComponent(object->meshID);
- if (softbody == nullptr)
- break;
-
- archive << softbody->weights;
+ bool found = false;
+ for (size_t i = 0; i < scene.terrains.GetCount() && !found; ++i)
+ {
+ wi::terrain::Terrain& terrain = scene.terrains[i];
+ for (auto& chunk : terrain.chunks)
+ {
+ auto& chunk_data = chunk.second;
+ if (chunk_data.entity == entity)
+ {
+ found = true;
+ archive << terrain_material_layer;
+ archive << chunk_data.blendmap_layers.size();
+ for (size_t l = terrain_material_layer; l < chunk_data.blendmap_layers.size(); ++l)
+ {
+ archive << chunk_data.blendmap_layers[l].pixels;
+ }
+ break;
+ }
+ }
+ }
}
break;
- case PaintToolWindow::MODE_HAIRPARTICLE_ADD_TRIANGLE:
- case PaintToolWindow::MODE_HAIRPARTICLE_REMOVE_TRIANGLE:
- case PaintToolWindow::MODE_HAIRPARTICLE_LENGTH:
- {
- wi::HairParticleSystem* hair = scene.hairs.GetComponent(selected.entity);
- if (hair == nullptr)
- break;
-
- archive << hair->vertex_lengths;
- }
+ default:
+ assert(0);
break;
- default:
- assert(0);
- break;
- }
}
-
+}
+void PaintToolWindow::RecordHistory(Entity entity, CommandList cmd)
+{
+ const bool start = entity != INVALID_ENTITY;
if (start)
{
- archive.PatchUnknownJumpPosition(history_redo_jump_position);
+ if (historyStartDatas.find(entity) != historyStartDatas.end())
+ return; // already saved start data
+ WriteHistoryData(entity, historyStartDatas[entity], cmd);
+ }
+ else if(!historyStartDatas.empty() && strokes.empty())
+ {
+ currentHistory = &editor->AdvanceHistory();
+ wi::Archive& archive = *currentHistory;
+ archive << EditorComponent::HISTORYOP_PAINTTOOL;
+ archive << (uint32_t)GetMode();
+ archive << historyStartDatas.size();
+ for (auto& x : historyStartDatas)
+ {
+ archive << x.first; // archive <- entity
+ }
+ size_t undo_jump = archive.WriteUnknownJumpPosition();
+ for (auto& x : historyStartDatas)
+ {
+ archive << x.second; // archive <- data
+ }
+ archive.PatchUnknownJumpPosition(undo_jump);
+ for (auto& x : historyStartDatas)
+ {
+ WriteHistoryData(x.first, archive);
+ }
+ historyStartDatas.clear();
}
}
void PaintToolWindow::ConsumeHistoryOperation(wi::Archive& archive, bool undo)
@@ -1449,6 +1693,9 @@ void PaintToolWindow::ConsumeHistoryOperation(wi::Archive& archive, bool undo)
MODE historymode;
archive >> (uint32_t&)historymode;
+ wi::vector entities;
+ archive >> entities;
+
uint64_t jump_pos = 0;
archive >> jump_pos;
if (!undo)
@@ -1456,25 +1703,17 @@ void PaintToolWindow::ConsumeHistoryOperation(wi::Archive& archive, bool undo)
archive.Jump(jump_pos);
}
- modeComboBox.SetSelected(historymode);
+ modeComboBox.SetSelectedByUserdata(historymode);
Scene& scene = editor->GetCurrentScene();
- for (auto& selected : editor->translator.selected)
+ for (auto& entity : entities)
{
switch (historymode)
{
case PaintToolWindow::MODE_TEXTURE:
{
- ObjectComponent* object = scene.objects.GetComponent(selected.entity);
- if (object == nullptr || object->meshID == INVALID_ENTITY)
- break;
-
- MeshComponent* mesh = scene.meshes.GetComponent(object->meshID);
- if (mesh == nullptr || (mesh->vertex_uvset_0.empty() && mesh->vertex_uvset_1.empty()))
- break;
-
- MaterialComponent* material = selected.subsetIndex >= 0 && selected.subsetIndex < (int)mesh->subsets.size() ? scene.materials.GetComponent(mesh->subsets[selected.subsetIndex].materialID) : nullptr;
+ MaterialComponent* material = scene.materials.GetComponent(entity);
if (material == nullptr)
break;
@@ -1491,11 +1730,7 @@ void PaintToolWindow::ConsumeHistoryOperation(wi::Archive& archive, bool undo)
break;
case PaintToolWindow::MODE_VERTEXCOLOR:
{
- ObjectComponent* object = scene.objects.GetComponent(selected.entity);
- if (object == nullptr || object->meshID == INVALID_ENTITY)
- break;
-
- MeshComponent* mesh = scene.meshes.GetComponent(object->meshID);
+ MeshComponent* mesh = scene.meshes.GetComponent(entity);
if (mesh == nullptr)
break;
@@ -1509,11 +1744,7 @@ void PaintToolWindow::ConsumeHistoryOperation(wi::Archive& archive, bool undo)
break;
case PaintToolWindow::MODE_WIND:
{
- ObjectComponent* object = scene.objects.GetComponent(selected.entity);
- if (object == nullptr || object->meshID == INVALID_ENTITY)
- break;
-
- MeshComponent* mesh = scene.meshes.GetComponent(object->meshID);
+ MeshComponent* mesh = scene.meshes.GetComponent(entity);
if (mesh == nullptr)
break;
@@ -1528,11 +1759,7 @@ void PaintToolWindow::ConsumeHistoryOperation(wi::Archive& archive, bool undo)
case PaintToolWindow::MODE_SCULPTING_ADD:
case PaintToolWindow::MODE_SCULPTING_SUBTRACT:
{
- ObjectComponent* object = scene.objects.GetComponent(selected.entity);
- if (object == nullptr || object->meshID == INVALID_ENTITY)
- break;
-
- MeshComponent* mesh = scene.meshes.GetComponent(object->meshID);
+ MeshComponent* mesh = scene.meshes.GetComponent(entity);
if (mesh == nullptr)
break;
@@ -1544,16 +1771,37 @@ void PaintToolWindow::ConsumeHistoryOperation(wi::Archive& archive, bool undo)
mesh->vertex_normals = archive_mesh.vertex_normals;
mesh->CreateRenderData();
+
+ if (mesh->bvh.IsValid())
+ {
+ mesh->BuildBVH();
+ }
+
+ // refresh terrain heightmap in case this mesh was a terrain chunk:
+ for (size_t i = 0; i < scene.terrains.GetCount(); ++i)
+ {
+ wi::terrain::Terrain& terrain = scene.terrains[i];
+ for (auto& it : terrain.chunks)
+ {
+ wi::terrain::ChunkData& chunk_data = it.second;
+ if (chunk_data.entity == entity)
+ {
+ for (size_t v = 0; v < chunk_data.heightmap_data.size(); ++v)
+ {
+ chunk_data.heightmap_data[v] = uint16_t(wi::math::InverseLerp(terrain.bottomLevel, terrain.topLevel, mesh->vertex_positions[v].y) * 65535);
+ }
+ chunk_data.heightmap = {};
+ terrain.CreateChunkRegionTexture(chunk_data);
+ break;
+ }
+ }
+ }
}
break;
case PaintToolWindow::MODE_SOFTBODY_PINNING:
case PaintToolWindow::MODE_SOFTBODY_PHYSICS:
{
- ObjectComponent* object = scene.objects.GetComponent(selected.entity);
- if (object == nullptr || object->meshID == INVALID_ENTITY)
- break;
-
- SoftBodyPhysicsComponent* softbody = scene.softbodies.GetComponent(object->meshID);
+ SoftBodyPhysicsComponent* softbody = scene.softbodies.GetComponent(entity);
if (softbody == nullptr)
break;
@@ -1569,7 +1817,7 @@ void PaintToolWindow::ConsumeHistoryOperation(wi::Archive& archive, bool undo)
case PaintToolWindow::MODE_HAIRPARTICLE_REMOVE_TRIANGLE:
case PaintToolWindow::MODE_HAIRPARTICLE_LENGTH:
{
- wi::HairParticleSystem* hair = scene.hairs.GetComponent(selected.entity);
+ wi::HairParticleSystem* hair = scene.hairs.GetComponent(entity);
if (hair == nullptr)
break;
@@ -1581,6 +1829,35 @@ void PaintToolWindow::ConsumeHistoryOperation(wi::Archive& archive, bool undo)
hair->_flags |= wi::HairParticleSystem::REBUILD_BUFFERS;
}
break;
+ case PaintToolWindow::MODE_TERRAIN_MATERIAL:
+ {
+ for (size_t i = 0; i < scene.terrains.GetCount(); ++i)
+ {
+ wi::terrain::Terrain& terrain = scene.terrains[i];
+ for (auto& chunk : terrain.chunks)
+ {
+ auto& chunk_data = chunk.second;
+ if (chunk_data.entity == entity)
+ {
+ archive >> terrain_material_layer;
+ size_t count = 0;
+ archive >> count;
+ chunk_data.blendmap_layers.resize(count);
+ for (size_t l = terrain_material_layer; l < count; ++l)
+ {
+ archive >> chunk_data.blendmap_layers[l].pixels;
+ }
+ chunk_data.blendmap = {};
+ if (chunk_data.vt != nullptr)
+ {
+ chunk_data.vt->invalidate();
+ }
+ break;
+ }
+ }
+ }
+ }
+ break;
default:
assert(0);
break;
@@ -1657,10 +1934,119 @@ void PaintToolWindow::ResizeLayout()
wireCheckBox.SetPos(XMFLOAT2(backfaceCheckBox.GetPos().x - wireCheckBox.GetSize().x - 100, backfaceCheckBox.GetPos().y));
add_right(pressureCheckBox);
alphaCheckBox.SetPos(XMFLOAT2(pressureCheckBox.GetPos().x - alphaCheckBox.GetSize().x - 100, pressureCheckBox.GetPos().y));
+ terrainCheckBox.SetPos(XMFLOAT2(pressureCheckBox.GetPos().x - terrainCheckBox.GetSize().x - 100, pressureCheckBox.GetPos().y));
add(textureSlotComboBox);
add(brushShapeComboBox);
add(axisCombo);
add(saveTextureButton);
add_right(brushTextureButton);
add_right(revealTextureButton);
+
+ if (GetMode() == MODE_TERRAIN_MATERIAL)
+ {
+ const TerrainWindow& terrainWnd = editor->componentsWnd.terrainWnd;
+ if (terrainWnd.terrain != nullptr)
+ {
+ const wi::terrain::Terrain& terrain = *terrainWnd.terrain;
+ Scene& scene = editor->GetCurrentScene();
+
+ wi::gui::Theme theme;
+ theme.image.CopyFrom(sprites[wi::gui::IDLE].params);
+ theme.image.background = false;
+ theme.image.blendFlag = wi::enums::BLENDMODE_ALPHA;
+ theme.font.CopyFrom(font.params);
+ theme.shadow_color = wi::Color::lerp(theme.font.color, wi::Color::Transparent(), 0.25f);
+ theme.tooltipFont.CopyFrom(tooltipFont.params);
+ theme.tooltipImage.CopyFrom(tooltipSprite.params);
+
+ const float preview_size = 100;
+ const float border = 20 * preview_size / 100.0f;
+ int cells = std::max(1, int(GetWidgetAreaSize().x / (preview_size + border)));
+ float offset_y = y + border;
+
+ for (size_t i = 0; i < terrain_material_buttons.size(); ++i)
+ {
+ Entity entity = terrain.materialEntities[i];
+
+ wi::gui::Button& button = terrain_material_buttons[i];
+ button.SetVisible(IsVisible() && !IsCollapsed());
+
+ button.SetTheme(theme);
+ button.SetColor(wi::Color::White());
+ button.SetColor(wi::Color(255, 255, 255, 150), wi::gui::IDLE);
+ button.SetShadowRadius(0);
+
+ if (terrain_material_layer == i)
+ {
+ button.SetColor(wi::Color::White());
+ button.SetShadowRadius(3);
+ }
+
+ MaterialComponent* material = scene.materials.GetComponent(entity);
+ if (material != nullptr)
+ {
+ // find first good texture that we can show:
+ button.SetImage({});
+ for (auto& slot : material->textures)
+ {
+ if (slot.resource.IsValid())
+ {
+ button.SetImage(slot.resource);
+ break;
+ }
+ }
+ }
+
+ const NameComponent* name = scene.names.GetComponent(entity);
+ if (name != nullptr)
+ {
+ button.SetTooltip(name->name);
+ }
+ button.font.params.h_align = wi::font::WIFALIGN_CENTER;
+ button.font.params.v_align = wi::font::WIFALIGN_BOTTOM;
+
+ button.SetSize(XMFLOAT2(preview_size, preview_size));
+ button.SetPos(XMFLOAT2((i % cells) * (preview_size + border) + border, offset_y));
+ if ((i % cells) == (cells - 1))
+ {
+ offset_y += preview_size + border;
+ }
+ }
+ }
+ }
+}
+
+void PaintToolWindow::RecreateTerrainMaterialButtons()
+{
+ if (editor == nullptr)
+ return;
+ const TerrainWindow& terrainWnd = editor->componentsWnd.terrainWnd;
+ if (terrainWnd.terrain == nullptr)
+ return;
+ const wi::terrain::Terrain& terrain = *terrainWnd.terrain;
+
+ const wi::scene::Scene& scene = editor->GetCurrentScene();
+ for (auto& x : terrain_material_buttons)
+ {
+ RemoveWidget(&x);
+ }
+ terrain_material_buttons.resize(terrain.materialEntities.size());
+
+ for (size_t i = 0; i < terrain.materialEntities.size(); ++i)
+ {
+ Entity entity = terrain.materialEntities[i];
+
+ wi::gui::Button& button = terrain_material_buttons[i];
+ button.Create("");
+ AddWidget(&button);
+ button.SetVisible(false);
+ button.SetText("");
+ button.SetTooltip("");
+
+ button.OnClick([i, this](wi::gui::EventArgs args) {
+ terrain_material_layer = i;
+ });
+ }
+
+ ResizeLayout();
}
diff --git a/Editor/PaintToolWindow.h b/Editor/PaintToolWindow.h
index e6438b1349..a31d575fd9 100644
--- a/Editor/PaintToolWindow.h
+++ b/Editor/PaintToolWindow.h
@@ -5,9 +5,6 @@ class PaintToolWindow : public wi::gui::Window
{
float rot = 0;
float stroke_dist = 0;
- bool history_needs_recording_start = false;
- bool history_needs_recording_end = false;
- size_t history_redo_jump_position = 0;
size_t history_textureIndex = 0;
struct TextureSlot
{
@@ -18,6 +15,8 @@ class PaintToolWindow : public wi::gui::Window
TextureSlot GetEditTextureSlot(const wi::scene::MaterialComponent& material, int* uvset = nullptr);
void ReplaceEditTextureSlot(wi::scene::MaterialComponent& material, const TextureSlot& textureslot);
+ wi::unordered_map historyStartDatas;
+
struct SculptingIndex
{
size_t ind;
@@ -53,6 +52,7 @@ class PaintToolWindow : public wi::gui::Window
wi::gui::CheckBox wireCheckBox;
wi::gui::CheckBox pressureCheckBox;
wi::gui::CheckBox alphaCheckBox;
+ wi::gui::CheckBox terrainCheckBox;
wi::gui::ColorPicker colorPicker;
wi::gui::ComboBox textureSlotComboBox;
wi::gui::ComboBox brushShapeComboBox;
@@ -65,6 +65,7 @@ class PaintToolWindow : public wi::gui::Window
void DrawBrush(const wi::Canvas& canvas, wi::graphics::CommandList cmd) const;
XMFLOAT2 pos = XMFLOAT2(0, 0);
+ wi::scene::PickResult brushIntersect;
enum MODE
{
@@ -78,6 +79,7 @@ class PaintToolWindow : public wi::gui::Window
MODE_HAIRPARTICLE_ADD_TRIANGLE,
MODE_HAIRPARTICLE_REMOVE_TRIANGLE,
MODE_HAIRPARTICLE_LENGTH,
+ MODE_TERRAIN_MATERIAL,
MODE_WIND,
};
MODE GetMode() const;
@@ -90,9 +92,19 @@ class PaintToolWindow : public wi::gui::Window
Z
};
+ wi::vector terrain_material_buttons;
+ size_t terrain_material_layer = 0;
+
+ float texture_paint_radius = 50;
+ float vertex_paint_radius = 0.1f;
+ float terrain_paint_radius = 5;
+
wi::Archive* currentHistory = nullptr;
- void RecordHistory(bool start, wi::graphics::CommandList cmd = wi::graphics::CommandList());
+ void WriteHistoryData(wi::ecs::Entity entity, wi::Archive& archive, wi::graphics::CommandList cmd = wi::graphics::CommandList());
+ void RecordHistory(wi::ecs::Entity entity, wi::graphics::CommandList cmd = wi::graphics::CommandList());
void ConsumeHistoryOperation(wi::Archive& archive, bool undo);
void ResizeLayout() override;
+
+ void RecreateTerrainMaterialButtons();
};
diff --git a/Editor/TerrainWindow.cpp b/Editor/TerrainWindow.cpp
index ef6a620fc7..112f9fe4dc 100644
--- a/Editor/TerrainWindow.cpp
+++ b/Editor/TerrainWindow.cpp
@@ -1102,6 +1102,7 @@ void TerrainWindow::Create(EditorComponent* _editor)
{
terrain->materialEntities[i] = INVALID_ENTITY;
}
+ editor->optionsWnd.paintToolWnd.RecreateTerrainMaterialButtons();
});
AddWidget(&materialCombos[i]);
@@ -1254,7 +1255,13 @@ void TerrainWindow::Create(EditorComponent* _editor)
p.x -= aabb._min.x;
p.z -= aabb._min.z;
int coord = int(p.x) + int(p.z) * width;
- dest[coord] = chunk_data.region_weights[i++];
+ dest[coord] = wi::Color(
+ chunk_data.blendmap_layers[0].pixels[i],
+ chunk_data.blendmap_layers[1].pixels[i],
+ chunk_data.blendmap_layers[2].pixels[i],
+ chunk_data.blendmap_layers[3].pixels[i]
+ );
+ i++;
}
}
}
@@ -1413,6 +1420,8 @@ void TerrainWindow::SetupAssets()
// Customize terrain generator before it's initialized:
Scene& currentScene = editor->GetCurrentScene();
+ terrain_preset.materialEntities.clear();
+ terrain_preset.materialEntities.resize(wi::terrain::MATERIAL_COUNT);
for (int i = 0; i < wi::terrain::MATERIAL_COUNT; ++i)
{
terrain_preset.materialEntities[i] = CreateEntity();
@@ -1438,10 +1447,127 @@ void TerrainWindow::SetupAssets()
material_LowAltitude->textures[MaterialComponent::NORMALMAP].name = wi::helper::GetCurrentPath() + "/terrain/low_altitude_nor.jpg";
material_HighAltitude->textures[MaterialComponent::BASECOLORMAP].name = wi::helper::GetCurrentPath() + "/terrain/high_altitude.jpg";
material_HighAltitude->textures[MaterialComponent::NORMALMAP].name = wi::helper::GetCurrentPath() + "/terrain/high_altitude_nor.jpg";
- material_Base->CreateRenderData();
- material_Slope->CreateRenderData();
- material_LowAltitude->CreateRenderData();
- material_HighAltitude->CreateRenderData();
+
+ // Extra material: rock
+ {
+ Entity materialEntity = CreateEntity();
+ MaterialComponent& mat = currentScene.materials.Create(materialEntity);
+ currentScene.names.Create(materialEntity) = "Rock";
+ currentScene.Component_Attach(materialEntity, entity);
+ mat.textures[MaterialComponent::BASECOLORMAP].name = wi::helper::GetCurrentPath() + "/terrain/rock.jpg";
+ mat.textures[MaterialComponent::NORMALMAP].name = wi::helper::GetCurrentPath() + "/terrain/rock_nor.jpg";
+ mat.roughness = 0.9f;
+ terrain_preset.materialEntities.push_back(materialEntity);
+ }
+ // Extra material: ground
+ {
+ Entity materialEntity = CreateEntity();
+ MaterialComponent& mat = currentScene.materials.Create(materialEntity);
+ currentScene.names.Create(materialEntity) = "Ground";
+ currentScene.Component_Attach(materialEntity, entity);
+ mat.textures[MaterialComponent::BASECOLORMAP].name = wi::helper::GetCurrentPath() + "/terrain/ground.jpg";
+ mat.textures[MaterialComponent::NORMALMAP].name = wi::helper::GetCurrentPath() + "/terrain/ground_nor.jpg";
+ mat.roughness = 0.9f;
+ terrain_preset.materialEntities.push_back(materialEntity);
+ }
+ // Extra material: ground2
+ {
+ Entity materialEntity = CreateEntity();
+ MaterialComponent& mat = currentScene.materials.Create(materialEntity);
+ currentScene.names.Create(materialEntity) = "Ground2";
+ currentScene.Component_Attach(materialEntity, entity);
+ mat.textures[MaterialComponent::BASECOLORMAP].name = wi::helper::GetCurrentPath() + "/terrain/ground2.jpg";
+ mat.textures[MaterialComponent::NORMALMAP].name = wi::helper::GetCurrentPath() + "/terrain/ground2_nor.jpg";
+ mat.roughness = 0.9f;
+ terrain_preset.materialEntities.push_back(materialEntity);
+ }
+ // Extra material: bricks
+ {
+ Entity materialEntity = CreateEntity();
+ MaterialComponent& mat = currentScene.materials.Create(materialEntity);
+ currentScene.names.Create(materialEntity) = "Bricks";
+ currentScene.Component_Attach(materialEntity, entity);
+ mat.textures[MaterialComponent::BASECOLORMAP].name = wi::helper::GetCurrentPath() + "/terrain/bricks.jpg";
+ mat.textures[MaterialComponent::NORMALMAP].name = wi::helper::GetCurrentPath() + "/terrain/bricks_nor.jpg";
+ mat.roughness = 0.9f;
+ terrain_preset.materialEntities.push_back(materialEntity);
+ }
+ // Extra material: darkrock
+ {
+ Entity materialEntity = CreateEntity();
+ MaterialComponent& mat = currentScene.materials.Create(materialEntity);
+ currentScene.names.Create(materialEntity) = "Dark Rock";
+ currentScene.Component_Attach(materialEntity, entity);
+ mat.textures[MaterialComponent::BASECOLORMAP].name = wi::helper::GetCurrentPath() + "/terrain/darkrock.jpg";
+ mat.textures[MaterialComponent::NORMALMAP].name = wi::helper::GetCurrentPath() + "/terrain/darkrock_nor.jpg";
+ mat.roughness = 0.8f;
+ terrain_preset.materialEntities.push_back(materialEntity);
+ }
+ // Extra material: metalplate
+ {
+ Entity materialEntity = CreateEntity();
+ MaterialComponent& mat = currentScene.materials.Create(materialEntity);
+ currentScene.names.Create(materialEntity) = "Metal Plate";
+ currentScene.Component_Attach(materialEntity, entity);
+ mat.textures[MaterialComponent::BASECOLORMAP].name = wi::helper::GetCurrentPath() + "/terrain/metalplate.jpg";
+ mat.textures[MaterialComponent::NORMALMAP].name = wi::helper::GetCurrentPath() + "/terrain/metalplate_nor.jpg";
+ mat.metalness = 1;
+ mat.roughness = 0.5f;
+ terrain_preset.materialEntities.push_back(materialEntity);
+ }
+ // Extra material: foil
+ {
+ Entity materialEntity = CreateEntity();
+ MaterialComponent& mat = currentScene.materials.Create(materialEntity);
+ currentScene.names.Create(materialEntity) = "Foil";
+ currentScene.Component_Attach(materialEntity, entity);
+ mat.textures[MaterialComponent::BASECOLORMAP].name = wi::helper::GetCurrentPath() + "/terrain/foil.jpg";
+ mat.textures[MaterialComponent::NORMALMAP].name = wi::helper::GetCurrentPath() + "/terrain/foil_nor.jpg";
+ mat.metalness = 1;
+ mat.roughness = 0.01f;
+ terrain_preset.materialEntities.push_back(materialEntity);
+ }
+ // Extra material: pavingstone
+ {
+ Entity materialEntity = CreateEntity();
+ MaterialComponent& mat = currentScene.materials.Create(materialEntity);
+ currentScene.names.Create(materialEntity) = "Paving Stone";
+ currentScene.Component_Attach(materialEntity, entity);
+ mat.textures[MaterialComponent::BASECOLORMAP].name = wi::helper::GetCurrentPath() + "/terrain/pavingstone.jpg";
+ mat.textures[MaterialComponent::NORMALMAP].name = wi::helper::GetCurrentPath() + "/terrain/pavingstone_nor.jpg";
+ terrain_preset.materialEntities.push_back(materialEntity);
+ }
+ // Extra material: tactilepaving
+ {
+ Entity materialEntity = CreateEntity();
+ MaterialComponent& mat = currentScene.materials.Create(materialEntity);
+ currentScene.names.Create(materialEntity) = "Tactile Paving";
+ currentScene.Component_Attach(materialEntity, entity);
+ mat.textures[MaterialComponent::BASECOLORMAP].name = wi::helper::GetCurrentPath() + "/terrain/tactilepaving.jpg";
+ mat.textures[MaterialComponent::NORMALMAP].name = wi::helper::GetCurrentPath() + "/terrain/tactilepaving_nor.jpg";
+ terrain_preset.materialEntities.push_back(materialEntity);
+ }
+ // Extra material: lava
+ {
+ Entity materialEntity = CreateEntity();
+ MaterialComponent& mat = currentScene.materials.Create(materialEntity);
+ currentScene.names.Create(materialEntity) = "Lava";
+ currentScene.Component_Attach(materialEntity, entity);
+ mat.textures[MaterialComponent::BASECOLORMAP].name = wi::helper::GetCurrentPath() + "/terrain/lava.jpg";
+ mat.textures[MaterialComponent::NORMALMAP].name = wi::helper::GetCurrentPath() + "/terrain/lava_nor.jpg";
+ mat.textures[MaterialComponent::EMISSIVEMAP].name = wi::helper::GetCurrentPath() + "/terrain/lava_emi.jpg";
+ mat.roughness = 0.8f;
+ terrain_preset.materialEntities.push_back(materialEntity);
+ }
+
+ wi::jobsystem::context ctx;
+ wi::jobsystem::Dispatch(ctx, (uint32_t)terrain_preset.materialEntities.size(), 1, [&](wi::jobsystem::JobArgs args) {
+ Entity entity = terrain_preset.materialEntities[args.jobIndex];
+ MaterialComponent* material = currentScene.materials.GetComponent(entity);
+ if (material == nullptr)
+ return;
+ material->CreateRenderData();
+ });
std::string terrain_path = wi::helper::GetCurrentPath() + "/terrain/";
wi::config::File config;
@@ -1536,6 +1662,8 @@ void TerrainWindow::SetupAssets()
scene->Entity_Remove(entity); // The entities will be placed by terrain generator, we don't need the default object that the scene has anymore
}
+ wi::jobsystem::Wait(ctx);
+
for (auto& it : prop_scenes)
{
editor->GetCurrentScene().Merge(it.second);
@@ -1582,6 +1710,8 @@ void TerrainWindow::SetupAssets()
terrain = &terrain_preset;
presetCombo.SetSelected(0);
+
+ editor->optionsWnd.paintToolWnd.RecreateTerrainMaterialButtons();
}
void TerrainWindow::Update(const wi::Canvas& canvas, float dt)
diff --git a/Editor/terrain/bricks.jpg b/Editor/terrain/bricks.jpg
new file mode 100644
index 0000000000..6bb4af4bf7
Binary files /dev/null and b/Editor/terrain/bricks.jpg differ
diff --git a/Editor/terrain/bricks_nor.jpg b/Editor/terrain/bricks_nor.jpg
new file mode 100644
index 0000000000..ee2f2d02e7
Binary files /dev/null and b/Editor/terrain/bricks_nor.jpg differ
diff --git a/Editor/terrain/darkrock.jpg b/Editor/terrain/darkrock.jpg
new file mode 100644
index 0000000000..e4240be296
Binary files /dev/null and b/Editor/terrain/darkrock.jpg differ
diff --git a/Editor/terrain/darkrock_nor.jpg b/Editor/terrain/darkrock_nor.jpg
new file mode 100644
index 0000000000..c741449ba2
Binary files /dev/null and b/Editor/terrain/darkrock_nor.jpg differ
diff --git a/Editor/terrain/foil.jpg b/Editor/terrain/foil.jpg
new file mode 100644
index 0000000000..a57528b2dd
Binary files /dev/null and b/Editor/terrain/foil.jpg differ
diff --git a/Editor/terrain/foil_nor.jpg b/Editor/terrain/foil_nor.jpg
new file mode 100644
index 0000000000..e8e17bc7ea
Binary files /dev/null and b/Editor/terrain/foil_nor.jpg differ
diff --git a/Editor/terrain/ground.jpg b/Editor/terrain/ground.jpg
new file mode 100644
index 0000000000..64ebdb3b74
Binary files /dev/null and b/Editor/terrain/ground.jpg differ
diff --git a/Editor/terrain/ground2.jpg b/Editor/terrain/ground2.jpg
new file mode 100644
index 0000000000..2c0923a49c
Binary files /dev/null and b/Editor/terrain/ground2.jpg differ
diff --git a/Editor/terrain/ground2_nor.jpg b/Editor/terrain/ground2_nor.jpg
new file mode 100644
index 0000000000..8a5afd1433
Binary files /dev/null and b/Editor/terrain/ground2_nor.jpg differ
diff --git a/Editor/terrain/ground_nor.jpg b/Editor/terrain/ground_nor.jpg
new file mode 100644
index 0000000000..ab9b41eaa5
Binary files /dev/null and b/Editor/terrain/ground_nor.jpg differ
diff --git a/Editor/terrain/lava.jpg b/Editor/terrain/lava.jpg
new file mode 100644
index 0000000000..ca7cde331c
Binary files /dev/null and b/Editor/terrain/lava.jpg differ
diff --git a/Editor/terrain/lava_emi.jpg b/Editor/terrain/lava_emi.jpg
new file mode 100644
index 0000000000..66ed4139e5
Binary files /dev/null and b/Editor/terrain/lava_emi.jpg differ
diff --git a/Editor/terrain/lava_nor.jpg b/Editor/terrain/lava_nor.jpg
new file mode 100644
index 0000000000..64e3676168
Binary files /dev/null and b/Editor/terrain/lava_nor.jpg differ
diff --git a/Editor/terrain/licenses.txt b/Editor/terrain/licenses.txt
index 8cbade39e6..7073063f5e 100644
--- a/Editor/terrain/licenses.txt
+++ b/Editor/terrain/licenses.txt
@@ -24,3 +24,33 @@ ambientCG (ambientCG.com)
CC0 1.0 Public Domain Dedication (https://creativecommons.org/publicdomain/zero/1.0/)
Rock048 by ambientCG
https://ambientcg.com/a/Rock048
+
+Rock texture:
+https://ambientcg.com/view?id=Rock051
+
+Ground texture:
+https://ambientcg.com/view?id=Ground068
+
+Ground2 texture:
+https://ambientcg.com/view?id=Ground072
+
+Bricks texture:
+https://ambientcg.com/view?id=Bricks059
+
+Drak rock texture:
+https://ambientcg.com/view?id=Rock035
+
+Metal plate texture:
+https://ambientcg.com/view?id=DiamondPlate008C
+
+Foil texture:
+https://ambientcg.com/view?id=Foil002
+
+Tactile Paving texture:
+https://ambientcg.com/view?id=TactilePaving003
+
+Paving Stones texture:
+https://ambientcg.com/view?id=PavingStones070
+
+Lava texture:
+https://ambientcg.com/view?id=Lava002
\ No newline at end of file
diff --git a/Editor/terrain/metalplate.jpg b/Editor/terrain/metalplate.jpg
new file mode 100644
index 0000000000..2ebc81b9c3
Binary files /dev/null and b/Editor/terrain/metalplate.jpg differ
diff --git a/Editor/terrain/metalplate_nor.jpg b/Editor/terrain/metalplate_nor.jpg
new file mode 100644
index 0000000000..789baf9680
Binary files /dev/null and b/Editor/terrain/metalplate_nor.jpg differ
diff --git a/Editor/terrain/pavingstone.jpg b/Editor/terrain/pavingstone.jpg
new file mode 100644
index 0000000000..cbf66da6aa
Binary files /dev/null and b/Editor/terrain/pavingstone.jpg differ
diff --git a/Editor/terrain/pavingstone_nor.jpg b/Editor/terrain/pavingstone_nor.jpg
new file mode 100644
index 0000000000..75f26113b9
Binary files /dev/null and b/Editor/terrain/pavingstone_nor.jpg differ
diff --git a/Editor/terrain/props.wiscene b/Editor/terrain/props.wiscene
index d4b3b622cb..a20dddf8ec 100644
Binary files a/Editor/terrain/props.wiscene and b/Editor/terrain/props.wiscene differ
diff --git a/Editor/terrain/rock.jpg b/Editor/terrain/rock.jpg
new file mode 100644
index 0000000000..06c905cdf3
Binary files /dev/null and b/Editor/terrain/rock.jpg differ
diff --git a/Editor/terrain/rock_nor.jpg b/Editor/terrain/rock_nor.jpg
new file mode 100644
index 0000000000..68a40c9a01
Binary files /dev/null and b/Editor/terrain/rock_nor.jpg differ
diff --git a/Editor/terrain/tactilepaving.jpg b/Editor/terrain/tactilepaving.jpg
new file mode 100644
index 0000000000..6eba028f08
Binary files /dev/null and b/Editor/terrain/tactilepaving.jpg differ
diff --git a/Editor/terrain/tactilepaving_nor.jpg b/Editor/terrain/tactilepaving_nor.jpg
new file mode 100644
index 0000000000..02c5a80157
Binary files /dev/null and b/Editor/terrain/tactilepaving_nor.jpg differ
diff --git a/WickedEngine/offlineshadercompiler.cpp b/WickedEngine/offlineshadercompiler.cpp
index 200f1802c7..0f331f24d3 100644
--- a/WickedEngine/offlineshadercompiler.cpp
+++ b/WickedEngine/offlineshadercompiler.cpp
@@ -218,6 +218,7 @@ wi::vector shaders = {
{"terrainVirtualTextureUpdateCS", wi::graphics::ShaderStage::CS },
{"terrainVirtualTextureUpdateCS_normalmap", wi::graphics::ShaderStage::CS },
{"terrainVirtualTextureUpdateCS_surfacemap", wi::graphics::ShaderStage::CS },
+ {"terrainVirtualTextureUpdateCS_emissivemap", wi::graphics::ShaderStage::CS },
{"meshlet_prepareCS", wi::graphics::ShaderStage::CS },
{"impostor_prepareCS", wi::graphics::ShaderStage::CS },
{"virtualTextureTileRequestsCS", wi::graphics::ShaderStage::CS },
diff --git a/WickedEngine/shaders/ShaderInterop_Renderer.h b/WickedEngine/shaders/ShaderInterop_Renderer.h
index 80331c88de..8d859b9ddd 100644
--- a/WickedEngine/shaders/ShaderInterop_Renderer.h
+++ b/WickedEngine/shaders/ShaderInterop_Renderer.h
@@ -3,6 +3,7 @@
#include "ShaderInterop.h"
#include "ShaderInterop_Weather.h"
#include "ShaderInterop_VXGI.h"
+#include "ShaderInterop_Terrain.h"
struct ShaderScene
{
@@ -59,6 +60,8 @@ struct ShaderScene
float smooth_backface;
};
DDGI ddgi;
+
+ ShaderTerrain terrain;
};
static const uint SHADERMATERIAL_OPTION_BIT_USE_VERTEXCOLORS = 1 << 0;
@@ -340,7 +343,7 @@ struct ShaderMaterial
float anisotropy_strength;
float anisotropy_rotation_sin;
float anisotropy_rotation_cos;
- float padding0;
+ float blend_with_terrain_height_rcp;
int sampler_descriptor;
uint options;
@@ -420,7 +423,7 @@ struct ShaderTypeBin
uint4 padding; // 32-byte alignment
#endif // __SCE__ || __PSSL__
};
-static const uint SHADERTYPE_BIN_COUNT = 10;
+static const uint SHADERTYPE_BIN_COUNT = 11;
struct VisibilityTile
{
@@ -1361,10 +1364,10 @@ struct TerrainVirtualTexturePush
{
int2 offset;
uint2 write_offset;
+
uint write_size;
float resolution_rcp;
- int region_weights_textureRO;
- int padding;
+ uint blendmap_buffer_offset;
};
struct VirtualTextureResidencyUpdateCB
{
diff --git a/WickedEngine/shaders/ShaderInterop_Terrain.h b/WickedEngine/shaders/ShaderInterop_Terrain.h
new file mode 100644
index 0000000000..cf88015258
--- /dev/null
+++ b/WickedEngine/shaders/ShaderInterop_Terrain.h
@@ -0,0 +1,36 @@
+#ifndef WI_SHADERINTEROP_TERRAIN_H
+#define WI_SHADERINTEROP_TERRAIN_H
+#include "ShaderInterop.h"
+#include "ShaderInterop_Renderer.h"
+
+struct ShaderTerrainChunk
+{
+ int heightmap;
+ uint materialID;
+
+ void init()
+ {
+ heightmap = -1;
+ materialID = 0;
+ }
+};
+struct ShaderTerrain
+{
+ float3 center_chunk_pos;
+ float chunk_size;
+
+ int chunk_buffer;
+ int chunk_buffer_range;
+ float min_height;
+ float max_height;
+
+ void init()
+ {
+ center_chunk_pos = float3(0, 0, 0);
+ chunk_size = 0;
+ chunk_buffer = -1;
+ chunk_buffer_range = 0;
+ }
+};
+
+#endif // WI_SHADERINTEROP_TERRAIN_H
diff --git a/WickedEngine/shaders/Shaders_SOURCE.vcxitems b/WickedEngine/shaders/Shaders_SOURCE.vcxitems
index 6375a3aa09..d7648dcc28 100644
--- a/WickedEngine/shaders/Shaders_SOURCE.vcxitems
+++ b/WickedEngine/shaders/Shaders_SOURCE.vcxitems
@@ -1011,6 +1011,10 @@
Compute
4.0
+
+ Compute
+ 4.0
+
Compute
4.0
@@ -2731,5 +2735,6 @@
+
\ No newline at end of file
diff --git a/WickedEngine/shaders/Shaders_SOURCE.vcxitems.filters b/WickedEngine/shaders/Shaders_SOURCE.vcxitems.filters
index 2bad342ce6..2b71ed3495 100644
--- a/WickedEngine/shaders/Shaders_SOURCE.vcxitems.filters
+++ b/WickedEngine/shaders/Shaders_SOURCE.vcxitems.filters
@@ -1151,6 +1151,9 @@
CS
+
+ CS
+
@@ -1300,5 +1303,8 @@
ffx-shadows-dnsr
+
+ interop
+
\ No newline at end of file
diff --git a/WickedEngine/shaders/globals.hlsli b/WickedEngine/shaders/globals.hlsli
index 054e155253..880c00a8b5 100644
--- a/WickedEngine/shaders/globals.hlsli
+++ b/WickedEngine/shaders/globals.hlsli
@@ -65,7 +65,8 @@
"SRV(t0, space = 37, offset = 0, numDescriptors = unbounded, flags = DESCRIPTORS_VOLATILE | DATA_VOLATILE)," \
"SRV(t0, space = 38, offset = 0, numDescriptors = unbounded, flags = DESCRIPTORS_VOLATILE | DATA_VOLATILE)," \
"SRV(t0, space = 39, offset = 0, numDescriptors = unbounded, flags = DESCRIPTORS_VOLATILE | DATA_VOLATILE)," \
- "SRV(t0, space = 40, offset = 0, numDescriptors = unbounded, flags = DESCRIPTORS_VOLATILE | DATA_VOLATILE)" \
+ "SRV(t0, space = 40, offset = 0, numDescriptors = unbounded, flags = DESCRIPTORS_VOLATILE | DATA_VOLATILE)," \
+ "SRV(t0, space = 41, offset = 0, numDescriptors = unbounded, flags = DESCRIPTORS_VOLATILE | DATA_VOLATILE)" \
"), " \
"StaticSampler(s100, addressU = TEXTURE_ADDRESS_CLAMP, addressV = TEXTURE_ADDRESS_CLAMP, addressW = TEXTURE_ADDRESS_CLAMP, filter = FILTER_MIN_MAG_MIP_LINEAR)," \
"StaticSampler(s101, addressU = TEXTURE_ADDRESS_WRAP, addressV = TEXTURE_ADDRESS_WRAP, addressW = TEXTURE_ADDRESS_WRAP, filter = FILTER_MIN_MAG_MIP_LINEAR)," \
@@ -228,6 +229,7 @@ static const BindlessResource> bindless_structu
static const BindlessResource> bindless_structured_entity;
static const BindlessResource> bindless_structured_matrix;
static const BindlessResource> bindless_structured_uint;
+static const BindlessResource> bindless_structured_terrain_chunks;
#elif defined(__spirv__)
[[vk::binding(0, DESCRIPTOR_SET_BINDLESS_STORAGE_BUFFER)]] StructuredBuffer bindless_structured_meshinstance[];
[[vk::binding(0, DESCRIPTOR_SET_BINDLESS_STORAGE_BUFFER)]] StructuredBuffer bindless_structured_geometry[];
@@ -236,6 +238,7 @@ static const BindlessResource> bindless_structured_uint;
[[vk::binding(0, DESCRIPTOR_SET_BINDLESS_STORAGE_BUFFER)]] StructuredBuffer bindless_structured_entity[];
[[vk::binding(0, DESCRIPTOR_SET_BINDLESS_STORAGE_BUFFER)]] StructuredBuffer bindless_structured_matrix[];
[[vk::binding(0, DESCRIPTOR_SET_BINDLESS_STORAGE_BUFFER)]] StructuredBuffer bindless_structured_uint[];
+[[vk::binding(0, DESCRIPTOR_SET_BINDLESS_STORAGE_BUFFER)]] StructuredBuffer bindless_structured_terrain_chunks[];
#else
StructuredBuffer bindless_structured_meshinstance[] : register(space34);
StructuredBuffer bindless_structured_geometry[] : register(space35);
@@ -244,6 +247,7 @@ StructuredBuffer bindless_structured_material[] : register(space
StructuredBuffer bindless_structured_entity[] : register(space38);
StructuredBuffer bindless_structured_matrix[] : register(space39);
StructuredBuffer bindless_structured_uint[] : register(space40);
+StructuredBuffer bindless_structured_terrain_chunks[] : register(space41);
#endif // __spirv__
inline FrameCB GetFrame()
diff --git a/WickedEngine/shaders/objectHF.hlsli b/WickedEngine/shaders/objectHF.hlsli
index 813f8720d3..325895de57 100644
--- a/WickedEngine/shaders/objectHF.hlsli
+++ b/WickedEngine/shaders/objectHF.hlsli
@@ -31,6 +31,9 @@
#define SVT_FEEDBACK
#endif // EARLY_DEPTH_STENCIL
+#ifdef TERRAINBLENDED
+#define TEXTURE_SLOT_NONUNIFORM
+#endif // TERRAINBLENDED
#include "globals.hlsli"
#include "brdf.hlsli"
@@ -601,6 +604,77 @@ float4 main(PixelInput input, in bool is_frontface : SV_IsFrontFace) : SV_Target
}
#endif // OBJECTSHADER_USE_UVSETS
+#ifdef OBJECTSHADER_USE_EMISSIVE
+ // Emissive map:
+ surface.emissiveColor = GetMaterial().GetEmissive();
+
+#ifdef OBJECTSHADER_USE_UVSETS
+ [branch]
+ if (any(surface.emissiveColor) && GetMaterial().textures[EMISSIVEMAP].IsValid())
+ {
+ float4 emissiveMap = GetMaterial().textures[EMISSIVEMAP].Sample(sampler_objectshader, uvsets);
+ surface.emissiveColor *= emissiveMap.rgb * emissiveMap.a;
+ }
+#endif // OBJECTSHADER_USE_UVSETS
+
+ surface.emissiveColor *= Unpack_R11G11B10_FLOAT(meshinstance.emissive);
+#endif // OBJECTSHADER_USE_EMISSIVE
+
+#ifdef OBJECTSHADER_USE_UVSETS
+#ifdef TERRAINBLENDED
+ [branch]
+ if (GetMaterial().blend_with_terrain_height_rcp > 0)
+ {
+ // Blend object into terrain material:
+ ShaderTerrain terrain = GetScene().terrain;
+ [branch]
+ if(terrain.chunk_buffer >= 0)
+ {
+ int2 chunk_coord = floor((surface.P.xz - terrain.center_chunk_pos.xz) / terrain.chunk_size);
+ if(chunk_coord.x >= -terrain.chunk_buffer_range && chunk_coord.x <= terrain.chunk_buffer_range && chunk_coord.y >= -terrain.chunk_buffer_range && chunk_coord.y <= terrain.chunk_buffer_range)
+ {
+ uint chunk_idx = flatten2D(chunk_coord + terrain.chunk_buffer_range, terrain.chunk_buffer_range * 2 + 1);
+ ShaderTerrainChunk chunk = bindless_structured_terrain_chunks[terrain.chunk_buffer][chunk_idx];
+
+ [branch]
+ if(chunk.heightmap >= 0)
+ {
+ Texture2D terrain_heightmap = bindless_textures[NonUniformResourceIndex(chunk.heightmap)];
+ float2 chunk_min = terrain.center_chunk_pos.xz + chunk_coord * terrain.chunk_size;
+ float2 chunk_max = terrain.center_chunk_pos.xz + terrain.chunk_size + chunk_coord * terrain.chunk_size;
+ float2 terrain_uv = saturate(inverse_lerp(chunk_min, chunk_max, surface.P.xz));
+ float terrain_height0 = terrain_heightmap.SampleLevel(sampler_linear_clamp, terrain_uv, 0).r;
+ float terrain_height1 = terrain_heightmap.SampleLevel(sampler_linear_clamp, terrain_uv, 0, int2(1, 0)).r;
+ float terrain_height2 = terrain_heightmap.SampleLevel(sampler_linear_clamp, terrain_uv, 0, int2(0, 1)).r;
+ float3 P0 = float3(0, terrain_height0, 0);
+ float3 P1 = float3(1, terrain_height1, 0);
+ float3 P2 = float3(0, terrain_height2, 1);
+ float3 terrain_normal = normalize(cross(P2 - P0, P1 - P0));
+ float terrain_height = lerp(terrain.min_height, terrain.max_height, terrain_height0);
+ float object_height = surface.P.y;
+ float diff = (object_height - terrain_height) * GetMaterial().blend_with_terrain_height_rcp;
+ float blend = 1 - pow(saturate(diff), 2);
+ //blend *= lerp(1, saturate((noise_gradient_3D(surface.P * 2) * 0.5 + 0.5) * 2), saturate(diff));
+ //terrain_uv = lerp(saturate(inverse_lerp(chunk_min, chunk_max, surface.P.xz - surface.N.xz * diff)), terrain_uv, saturate(surface.N.y)); // uv stretching improvement: stretch in normal direction if normal gets horizontal
+ ShaderMaterial terrain_material = load_material(chunk.materialID);
+ terrain_uv = mad(terrain_uv, terrain_material.texMulAdd.xy, terrain_material.texMulAdd.zw);
+ float4 terrain_baseColor = terrain_material.textures[BASECOLORMAP].Sample(sampler_objectshader, terrain_uv.xyxy);
+ float4 terrain_bumpColor = terrain_material.textures[NORMALMAP].Sample(sampler_objectshader, terrain_uv.xyxy);
+ float4 terrain_surfaceMap = terrain_material.textures[SURFACEMAP].Sample(sampler_objectshader, terrain_uv.xyxy);
+ float3 terrain_emissiveMap = terrain_material.textures[EMISSIVEMAP].Sample(sampler_objectshader, terrain_uv.xyxy).rgb;
+ surface.baseColor = lerp(surface.baseColor, terrain_baseColor, blend);
+ surface.bumpColor = lerp(surface.bumpColor, terrain_bumpColor.rgb * 2 - 1, blend);
+ surfaceMap = lerp(surfaceMap, terrain_surfaceMap, blend);
+ surface.emissiveColor += terrain_emissiveMap * terrain_material.GetEmissive() * blend;
+ input.nor = lerp(input.nor, terrain_normal, blend);
+ TBN[2] = input.nor;
+ surface.N = normalize(input.nor);
+ }
+ }
+ }
+ }
+#endif // TERRAINBLENDED
+#endif // OBJECTSHADER_USE_UVSETS
[branch]
if (!GetMaterial().IsUsingSpecularGlossinessWorkflow())
@@ -652,25 +726,7 @@ float4 main(PixelInput input, in bool is_frontface : SV_IsFrontFace) : SV_Target
surface.create(GetMaterial(), surface.baseColor, surfaceMap, specularMap);
-
-
-
-
-#ifdef OBJECTSHADER_USE_EMISSIVE
- // Emissive map:
- surface.emissiveColor = GetMaterial().GetEmissive();
-
-#ifdef OBJECTSHADER_USE_UVSETS
- [branch]
- if (any(surface.emissiveColor) && GetMaterial().textures[EMISSIVEMAP].IsValid())
- {
- float4 emissiveMap = GetMaterial().textures[EMISSIVEMAP].Sample(sampler_objectshader, uvsets);
- surface.emissiveColor *= emissiveMap.rgb * emissiveMap.a;
- }
-#endif // OBJECTSHADER_USE_UVSETS
-
- surface.emissiveColor *= Unpack_R11G11B10_FLOAT(meshinstance.emissive);
-#endif // OBJECTSHADER_USE_EMISSIVE
+
#ifdef OBJECTSHADER_USE_UVSETS
diff --git a/WickedEngine/shaders/objectPS_paintradius.hlsl b/WickedEngine/shaders/objectPS_paintradius.hlsl
index 1fc56d864d..40b7301007 100644
--- a/WickedEngine/shaders/objectPS_paintradius.hlsl
+++ b/WickedEngine/shaders/objectPS_paintradius.hlsl
@@ -7,13 +7,13 @@
float4 main(PixelInput input) : SV_TARGET
{
float4 uvsets = input.GetUVSets();
- const float2 pixel = (xPaintRadUVSET == 0 ? uvsets.xy : uvsets.zw) * xPaintRadResolution;
+ const float2 pixel = frac((xPaintRadUVSET == 0 ? uvsets.xy : uvsets.zw)) * xPaintRadResolution;
const float2x2 rot = float2x2(
cos(xPaintRadBrushRotation), -sin(xPaintRadBrushRotation),
sin(xPaintRadBrushRotation), cos(xPaintRadBrushRotation)
);
- const float2 diff = mul(xPaintRadCenter - pixel, rot);
+ const float2 diff = mul((xPaintRadCenter % xPaintRadResolution) - pixel, rot);
float dist = 0;
switch (xPaintRadBrushShape)
diff --git a/WickedEngine/shaders/paint_textureCS.hlsl b/WickedEngine/shaders/paint_textureCS.hlsl
index f9ff65e4ca..a674320d30 100644
--- a/WickedEngine/shaders/paint_textureCS.hlsl
+++ b/WickedEngine/shaders/paint_textureCS.hlsl
@@ -4,8 +4,11 @@
PUSHCONSTANT(push, PaintTexturePushConstants);
[numthreads(PAINT_TEXTURE_BLOCKSIZE, PAINT_TEXTURE_BLOCKSIZE, 1)]
-void main( uint3 DTid : SV_DispatchThreadID )
+void main(uint groupIndex : SV_GroupIndex, uint2 Gid : SV_GroupID)
{
+ const uint2 GTid = remap_lane_8x8(groupIndex);
+ const uint2 DTid = Gid * PAINT_TEXTURE_BLOCKSIZE + GTid;
+
RWTexture2D texture_output = bindless_rwtextures[push.texture_output];
float2 dim;
diff --git a/WickedEngine/shaders/surfaceHF.hlsli b/WickedEngine/shaders/surfaceHF.hlsli
index a84c1888f7..3c17081b5e 100644
--- a/WickedEngine/shaders/surfaceHF.hlsli
+++ b/WickedEngine/shaders/surfaceHF.hlsli
@@ -222,7 +222,7 @@ struct Surface
// Metallic-roughness workflow:
if (material.IsOcclusionEnabled_Primary())
{
- occlusion = surfaceMap.r;
+ occlusion *= surfaceMap.r;
}
roughness = surfaceMap.g;
const float metalness = surfaceMap.b;
@@ -623,6 +623,92 @@ struct Surface
surfaceMap = surfacemap_simple;
}
+ emissiveColor = material.GetEmissive() * Unpack_R11G11B10_FLOAT(inst.emissive);
+ if (is_emittedparticle)
+ {
+ emissiveColor *= baseColor.rgb * baseColor.a;
+ }
+ else
+ {
+ [branch]
+ if (material.textures[EMISSIVEMAP].IsValid())
+ {
+#ifdef SURFACE_LOAD_QUAD_DERIVATIVES
+ float4 emissiveMap = material.textures[EMISSIVEMAP].SampleGrad(sam, uvsets, uvsets_dx, uvsets_dy);
+#else
+ float lod = 0;
+#ifdef SURFACE_LOAD_MIPCONE
+ lod = compute_texture_lod(material.textures[EMISSIVEMAP].GetTexture(), material.textures[EMISSIVEMAP].GetUVSet() == 0 ? lod_constant0 : lod_constant1, ray_direction, surf_normal, cone_width);
+#endif // SURFACE_LOAD_MIPCONE
+ float4 emissiveMap = material.textures[EMISSIVEMAP].SampleLevel(sam, uvsets, lod);
+#endif // SURFACE_LOAD_QUAD_DERIVATIVES
+ emissiveColor *= emissiveMap.rgb * emissiveMap.a;
+ }
+ }
+
+ if (material.options & SHADERMATERIAL_OPTION_BIT_ADDITIVE)
+ {
+ emissiveColor += baseColor.rgb * baseColor.a;
+ }
+
+#ifdef SURFACE_LOAD_QUAD_DERIVATIVES
+#ifdef TERRAINBLENDED
+ [branch]
+ if (material.blend_with_terrain_height_rcp > 0)
+ {
+ // Blend object into terrain material:
+ ShaderTerrain terrain = GetScene().terrain;
+ [branch]
+ if(terrain.chunk_buffer >= 0)
+ {
+ int2 chunk_coord = floor((P.xz - terrain.center_chunk_pos.xz) / terrain.chunk_size);
+ if(chunk_coord.x >= -terrain.chunk_buffer_range && chunk_coord.x <= terrain.chunk_buffer_range && chunk_coord.y >= -terrain.chunk_buffer_range && chunk_coord.y <= terrain.chunk_buffer_range)
+ {
+ uint chunk_idx = flatten2D(chunk_coord + terrain.chunk_buffer_range, terrain.chunk_buffer_range * 2 + 1);
+ ShaderTerrainChunk chunk = bindless_structured_terrain_chunks[terrain.chunk_buffer][chunk_idx];
+
+ [branch]
+ if(chunk.heightmap >= 0)
+ {
+ Texture2D terrain_heightmap = bindless_textures[NonUniformResourceIndex(chunk.heightmap)];
+ float2 chunk_min = terrain.center_chunk_pos.xz + chunk_coord * terrain.chunk_size;
+ float2 chunk_max = terrain.center_chunk_pos.xz + terrain.chunk_size + chunk_coord * terrain.chunk_size;
+ float2 terrain_uv = saturate(inverse_lerp(chunk_min, chunk_max, P.xz));
+ float terrain_height0 = terrain_heightmap.SampleLevel(sampler_linear_clamp, terrain_uv, 0).r;
+ float terrain_height1 = terrain_heightmap.SampleLevel(sampler_linear_clamp, terrain_uv, 0, int2(1, 0)).r;
+ float terrain_height2 = terrain_heightmap.SampleLevel(sampler_linear_clamp, terrain_uv, 0, int2(0, 1)).r;
+ float3 P0 = float3(0, terrain_height0, 0);
+ float3 P1 = float3(1, terrain_height1, 0);
+ float3 P2 = float3(0, terrain_height2, 1);
+ float3 terrain_normal = normalize(cross(P2 - P0, P1 - P0));
+ float terrain_height = lerp(terrain.min_height, terrain.max_height, terrain_height0);
+ float object_height = P.y;
+ float diff = (object_height - terrain_height) * material.blend_with_terrain_height_rcp;
+ float blend = 1 - pow(saturate(diff), 2);
+ //blend *= lerp(1, saturate((noise_gradient_3D(P * 2) * 0.5 + 0.5) * 2), saturate(diff));
+ //terrain_uv = lerp(saturate(inverse_lerp(chunk_min, chunk_max, P.xz - N.xz * diff)), terrain_uv, saturate(N.y)); // uv stretching improvement: stretch in normal direction if normal gets horizontal
+ ShaderMaterial terrain_material = load_material(chunk.materialID);
+ terrain_uv = mad(terrain_uv, terrain_material.texMulAdd.xy, terrain_material.texMulAdd.zw);
+ float2 terrain_uv_dx = terrain_uv - saturate(inverse_lerp(chunk_min, chunk_max, (P - P_dx).xz));
+ float2 terrain_uv_dy = terrain_uv - saturate(inverse_lerp(chunk_min, chunk_max, (P - P_dy).xz));
+ float4 terrain_baseColor = terrain_material.textures[BASECOLORMAP].SampleGrad(sam, terrain_uv.xyxy, terrain_uv_dx.xyxy, terrain_uv_dy.xyxy);
+ float4 terrain_bumpColor = terrain_material.textures[NORMALMAP].SampleGrad(sam, terrain_uv.xyxy, terrain_uv_dx.xyxy, terrain_uv_dy.xyxy);
+ float4 terrain_surfaceMap = terrain_material.textures[SURFACEMAP].SampleGrad(sam, terrain_uv.xyxy, terrain_uv_dx.xyxy, terrain_uv_dy.xyxy);
+ float3 terrain_emissiveMap = terrain_material.textures[EMISSIVEMAP].SampleGrad(sam, terrain_uv.xyxy, terrain_uv_dx.xyxy, terrain_uv_dy.xyxy).rgb;
+ baseColor = lerp(baseColor, terrain_baseColor, blend);
+ bumpColor = lerp(bumpColor, terrain_bumpColor.rgb * 2 - 1, blend);
+ surfaceMap = lerp(surfaceMap, terrain_surfaceMap, blend);
+ emissiveColor += terrain_emissiveMap * terrain_material.GetEmissive() * blend;
+ Nunnormalized = lerp(Nunnormalized, terrain_normal, blend);
+ TBN[2] = Nunnormalized;
+ N = normalize(Nunnormalized);
+ }
+ }
+ }
+ }
+#endif // TERRAINBLENDED
+#endif // SURFACE_LOAD_QUAD_DERIVATIVES
+
#ifdef SURFACE_LOAD_QUAD_DERIVATIVES
// I need to copy the decal code here because include resolve issues:
#ifndef DISABLE_DECALS
@@ -781,34 +867,6 @@ struct Surface
create(material, baseColor, surfaceMap, specularMap);
- emissiveColor = material.GetEmissive() * Unpack_R11G11B10_FLOAT(inst.emissive);
- if (is_emittedparticle)
- {
- emissiveColor *= baseColor.rgb * baseColor.a;
- }
- else
- {
- [branch]
- if (material.textures[EMISSIVEMAP].IsValid())
- {
-#ifdef SURFACE_LOAD_QUAD_DERIVATIVES
- float4 emissiveMap = material.textures[EMISSIVEMAP].SampleGrad(sam, uvsets, uvsets_dx, uvsets_dy);
-#else
- float lod = 0;
-#ifdef SURFACE_LOAD_MIPCONE
- lod = compute_texture_lod(material.textures[EMISSIVEMAP].GetTexture(), material.textures[EMISSIVEMAP].GetUVSet() == 0 ? lod_constant0 : lod_constant1, ray_direction, surf_normal, cone_width);
-#endif // SURFACE_LOAD_MIPCONE
- float4 emissiveMap = material.textures[EMISSIVEMAP].SampleLevel(sam, uvsets, lod);
-#endif // SURFACE_LOAD_QUAD_DERIVATIVES
- emissiveColor *= emissiveMap.rgb * emissiveMap.a;
- }
- }
-
- if (material.options & SHADERMATERIAL_OPTION_BIT_ADDITIVE)
- {
- emissiveColor += baseColor.rgb * baseColor.a;
- }
-
transmission = material.transmission;
[branch]
if (material.textures[TRANSMISSIONMAP].IsValid())
diff --git a/WickedEngine/shaders/terrainVirtualTextureUpdateCS.hlsl b/WickedEngine/shaders/terrainVirtualTextureUpdateCS.hlsl
index d1d82d647a..2d45a0bfad 100644
--- a/WickedEngine/shaders/terrainVirtualTextureUpdateCS.hlsl
+++ b/WickedEngine/shaders/terrainVirtualTextureUpdateCS.hlsl
@@ -2,22 +2,20 @@
#include "BlockCompress.hlsli"
#include "ColorSpaceUtility.hlsli"
-static const uint region_count = 4;
-
PUSHCONSTANT(push, TerrainVirtualTexturePush);
-struct Terrain
-{
- ShaderMaterial materials[region_count];
-};
-ConstantBuffer terrain : register(b0);
+Texture2DArray blendmap : register(t0);
+ByteAddressBuffer blendmap_buffer : register(t1);
-#if !defined(UPDATE_NORMALMAP) && !defined(UPDATE_SURFACEMAP)
+#if !defined(UPDATE_NORMALMAP) && !defined(UPDATE_SURFACEMAP) && !defined(UPDATE_EMISSIVEMAP)
#define UPDATE_BASECOLORMAP
+#endif
+
+#if defined(UPDATE_BASECOLORMAP) || defined(UPDATE_EMISSIVEMAP)
RWTexture2D output : register(u0);
#else
RWTexture2D output : register(u0);
-#endif // UPDATE_NORMALMAP
+#endif
static const uint2 block_offsets[16] = {
uint2(0, 0), uint2(1, 0), uint2(2, 0), uint2(3, 0),
@@ -32,11 +30,17 @@ void main(uint3 DTid : SV_DispatchThreadID)
if (DTid.x >= push.write_size || DTid.y >= push.write_size)
return;
- Texture2D region_weights_texture = bindless_textures[push.region_weights_textureRO];
-
+ uint2 dim;
+ uint array_size;
+ blendmap.GetDimensions(dim.x, dim.y, array_size);
+
#ifdef UPDATE_BASECOLORMAP
float3 block_rgb[16];
#endif // UPDATE_BASECOLORMAP
+
+#ifdef UPDATE_EMISSIVEMAP
+ float3 block_rgb[16];
+#endif // UPDATE_EMISSIVEMAP
#ifdef UPDATE_NORMALMAP
float block_x[16];
@@ -53,16 +57,19 @@ void main(uint3 DTid : SV_DispatchThreadID)
const uint2 block_offset = block_offsets[idx];
const int2 pixel = push.offset + DTid.xy * 4 + block_offset;
const float2 uv = (pixel.xy + 0.5f) * push.resolution_rcp;
-
- float4 region_weights = region_weights_texture.SampleLevel(sampler_linear_clamp, uv, 0);
-
- float weight_sum = 0;
+
float4 total_color = 0;
- for (uint i = 0; i < region_count; ++i)
+ float accumulation = 0;
+
+ // Note: blending is front-to back with early exit like decals
+ for(int blendmap_index = array_size - 1; blendmap_index >= 0; blendmap_index--)
{
- float weight = region_weights[i];
+ float weight = blendmap.SampleLevel(sampler_linear_clamp, float3(uv, blendmap_index), 0);
+ if(weight == 0)
+ continue;
- ShaderMaterial material = terrain.materials[i];
+ uint materialIndex = blendmap_buffer.Load(push.blendmap_buffer_offset + blendmap_index * sizeof(uint));
+ ShaderMaterial material = load_material(materialIndex);
#ifdef UPDATE_BASECOLORMAP
float4 baseColor = material.baseColor;
@@ -78,13 +85,13 @@ void main(uint3 DTid : SV_DispatchThreadID)
float4 baseColorMap = tex.SampleLevel(sampler_linear_wrap, uv / overscale, lod);
baseColor *= baseColorMap;
}
- total_color += baseColor * weight;
- //if (DTid.x < 2 || DTid.y < 2)
- // total_color = 0;
+ total_color = mad(1 - accumulation, weight * baseColor, total_color);
+ accumulation = mad(1 - weight, accumulation, weight);
+ if(accumulation >= 1)
+ break;
#endif // UPDATE_BASECOLORMAP
#ifdef UPDATE_NORMALMAP
- float2 normal = float2(0.5, 0.5);
[branch]
if (material.textures[NORMALMAP].IsValid())
{
@@ -95,9 +102,11 @@ void main(uint3 DTid : SV_DispatchThreadID)
float lod = log2(max(diff.x, diff.y));
float2 overscale = lod < 0 ? diff : 1;
float2 normalMap = tex.SampleLevel(sampler_linear_wrap, uv / overscale, lod).rg;
- normal = normalMap;
+ total_color.rg = mad(1 - accumulation, weight * normalMap, total_color.rg);
+ accumulation = mad(1 - weight, accumulation, weight);
+ if(accumulation >= 1)
+ break;
}
- total_color += float4(normal.rg, 1, 1) * weight;
#endif // UPDATE_NORMALMAP
#ifdef UPDATE_SURFACEMAP
@@ -114,12 +123,33 @@ void main(uint3 DTid : SV_DispatchThreadID)
float4 surfaceMap = tex.SampleLevel(sampler_linear_wrap, uv / overscale, lod);
surface *= surfaceMap;
}
- total_color += surface * weight;
+ total_color = mad(1 - accumulation, weight * surface, total_color);
+ accumulation = mad(1 - weight, accumulation, weight);
+ if(accumulation >= 1)
+ break;
#endif // UPDATE_SURFACEMAP
- weight_sum += weight;
+#ifdef UPDATE_EMISSIVEMAP
+ float4 emissiveColor = 0;
+ [branch]
+ if (material.textures[EMISSIVEMAP].IsValid())
+ {
+ Texture2D tex = bindless_textures[material.textures[EMISSIVEMAP].texture_descriptor];
+ float2 dim = 0;
+ tex.GetDimensions(dim.x, dim.y);
+ float2 diff = dim * push.resolution_rcp;
+ float lod = log2(max(diff.x, diff.y));
+ float2 overscale = lod < 0 ? diff : 1;
+ float4 emissiveMap = tex.SampleLevel(sampler_linear_wrap, uv / overscale, lod);
+ emissiveColor.rgb = emissiveMap.rgb * emissiveMap.a;
+ emissiveColor.a = emissiveMap.a;
+ }
+ total_color = mad(1 - accumulation, weight * emissiveColor, total_color);
+ accumulation = mad(1 - weight, accumulation, weight);
+ if(accumulation >= 1)
+ break;
+#endif // UPDATE_EMISSIVEMAP
}
- total_color /= weight_sum;
#ifdef UPDATE_BASECOLORMAP
block_rgb[idx] = ApplySRGBCurve_Fast(total_color.rgb);
@@ -134,6 +164,10 @@ void main(uint3 DTid : SV_DispatchThreadID)
block_rgb[idx] = total_color.rgb;
block_a[idx] = total_color.a;
#endif // UPDATE_SURFACEMAP
+
+#ifdef UPDATE_EMISSIVEMAP
+ block_rgb[idx] = ApplySRGBCurve_Fast(total_color.rgb);
+#endif // UPDATE_EMISSIVEMAP
}
const uint2 write_coord = push.write_offset + DTid.xy;
@@ -149,5 +183,9 @@ void main(uint3 DTid : SV_DispatchThreadID)
#ifdef UPDATE_SURFACEMAP
output[write_coord] = CompressBC3Block(block_rgb, block_a);
#endif // UPDATE_SURFACEMAP
+
+#ifdef UPDATE_EMISSIVEMAP
+ output[write_coord] = CompressBC1Block(block_rgb);
+#endif // UPDATE_EMISSIVEMAP
}
diff --git a/WickedEngine/shaders/terrainVirtualTextureUpdateCS_emissivemap.hlsl b/WickedEngine/shaders/terrainVirtualTextureUpdateCS_emissivemap.hlsl
new file mode 100644
index 0000000000..9b10cd497b
--- /dev/null
+++ b/WickedEngine/shaders/terrainVirtualTextureUpdateCS_emissivemap.hlsl
@@ -0,0 +1,2 @@
+#define UPDATE_EMISSIVEMAP
+#include "terrainVirtualTextureUpdateCS.hlsl"
diff --git a/WickedEngine/wiArchive.h b/WickedEngine/wiArchive.h
index 6f2248b168..a021a50702 100644
--- a/WickedEngine/wiArchive.h
+++ b/WickedEngine/wiArchive.h
@@ -27,7 +27,7 @@ namespace wi
void CreateEmpty(); // creates new archive in write mode
public:
- // Create empty arhive for writing
+ // Create empty archive for writing
Archive();
Archive(const Archive&) = default;
Archive(Archive&&) = default;
@@ -107,11 +107,21 @@ namespace wi
_write((int8_t)data);
return *this;
}
+ inline Archive& operator<<(short data)
+ {
+ _write((int16_t)data);
+ return *this;
+ }
inline Archive& operator<<(unsigned char data)
{
_write((uint8_t)data);
return *this;
}
+ inline Archive& operator<<(unsigned short data)
+ {
+ _write((uint16_t)data);
+ return *this;
+ }
inline Archive& operator<<(int data)
{
_write((int64_t)data);
@@ -222,6 +232,16 @@ namespace wi
}
return *this;
}
+ inline Archive& operator<<(const wi::Archive& other)
+ {
+ // Here we will use the << operator so that non-specified types will have compile error!
+ // Note: version is skipped, only data is appended
+ for (size_t i = sizeof(version); i < other.pos; ++i)
+ {
+ (*this) << other.data_ptr[i];
+ }
+ return *this;
+ }
// Read operations
inline Archive& operator>>(bool& data)
@@ -238,6 +258,13 @@ namespace wi
data = (char)temp;
return *this;
}
+ inline Archive& operator>>(short& data)
+ {
+ int16_t temp;
+ _read(temp);
+ data = (short)temp;
+ return *this;
+ }
inline Archive& operator>>(unsigned char& data)
{
uint8_t temp;
@@ -245,6 +272,13 @@ namespace wi
data = (unsigned char)temp;
return *this;
}
+ inline Archive& operator>>(unsigned short& data)
+ {
+ uint16_t temp;
+ _read(temp);
+ data = (unsigned short)temp;
+ return *this;
+ }
inline Archive& operator>>(int& data)
{
int64_t temp;
diff --git a/WickedEngine/wiEnums.h b/WickedEngine/wiEnums.h
index 4524c05069..4e4c99a384 100644
--- a/WickedEngine/wiEnums.h
+++ b/WickedEngine/wiEnums.h
@@ -26,10 +26,11 @@ namespace wi::enums
FILTER_TRANSPARENT = 1 << 1,
FILTER_WATER = 1 << 2,
FILTER_NAVIGATION_MESH = 1 << 3,
- FILTER_OBJECT_ALL = FILTER_OPAQUE | FILTER_TRANSPARENT | FILTER_WATER | FILTER_NAVIGATION_MESH,
+ FILTER_TERRAIN = 1 << 4,
+ FILTER_OBJECT_ALL = FILTER_OPAQUE | FILTER_TRANSPARENT | FILTER_WATER | FILTER_NAVIGATION_MESH | FILTER_TERRAIN,
// Other filtering types:
- FILTER_COLLIDER = 1 << 4,
+ FILTER_COLLIDER = 1 << 5,
// Include everything:
FILTER_ALL = ~0,
@@ -399,6 +400,7 @@ namespace wi::enums
CSTYPE_TERRAIN_VIRTUALTEXTURE_UPDATE_BASECOLORMAP,
CSTYPE_TERRAIN_VIRTUALTEXTURE_UPDATE_NORMALMAP,
CSTYPE_TERRAIN_VIRTUALTEXTURE_UPDATE_SURFACEMAP,
+ CSTYPE_TERRAIN_VIRTUALTEXTURE_UPDATE_EMISSIVEMAP,
CSTYPE_MESHLET_PREPARE,
CSTYPE_IMPOSTOR_PREPARE,
CSTYPE_VIRTUALTEXTURE_TILEREQUESTS,
diff --git a/WickedEngine/wiGUI.cpp b/WickedEngine/wiGUI.cpp
index 833407ce48..002d707705 100644
--- a/WickedEngine/wiGUI.cpp
+++ b/WickedEngine/wiGUI.cpp
@@ -2707,6 +2707,10 @@ namespace wi::gui
{
return selected;
}
+ uint64_t ComboBox::GetSelectedUserdata() const
+ {
+ return GetItemUserData(GetSelected());
+ }
void ComboBox::SetColor(wi::Color color, int id)
{
Widget::SetColor(color, id);
diff --git a/WickedEngine/wiGUI.h b/WickedEngine/wiGUI.h
index 8abedb6213..3b9627a162 100644
--- a/WickedEngine/wiGUI.h
+++ b/WickedEngine/wiGUI.h
@@ -588,6 +588,7 @@ namespace wi::gui
void SetSelectedByUserdata(uint64_t userdata);
void SetSelectedByUserdataWithoutCallback(uint64_t userdata); // SetSelectedByUserdata() but the OnSelect callback will not be executed
int GetSelected() const;
+ uint64_t GetSelectedUserdata() const;
void SetItemText(int index, const std::string& text);
void SetItemUserdata(int index, uint64_t userdata);
std::string GetItemText(int index) const;
diff --git a/WickedEngine/wiPhysics_Bullet.cpp b/WickedEngine/wiPhysics_Bullet.cpp
index ee19913fb8..0b690fbe1d 100644
--- a/WickedEngine/wiPhysics_Bullet.cpp
+++ b/WickedEngine/wiPhysics_Bullet.cpp
@@ -1435,7 +1435,7 @@ namespace wi::physics
}
// Update tangent vectors:
- if (!mesh.vertex_uvset_0.empty() && !mesh.vertex_normals.empty())
+ if (!mesh.vertex_uvset_0.empty() && !physicscomponent->vertex_normals_simulation.empty())
{
uint32_t first_subset = 0;
uint32_t last_subset = 0;
diff --git a/WickedEngine/wiPrimitive.cpp b/WickedEngine/wiPrimitive.cpp
index a55cc57bfe..a709113ee8 100644
--- a/WickedEngine/wiPrimitive.cpp
+++ b/WickedEngine/wiPrimitive.cpp
@@ -236,6 +236,18 @@ namespace wi::primitive
+ bool Sphere::intersects(const XMVECTOR& P) const
+ {
+ float distsq = wi::math::DistanceSquared(XMLoadFloat3(¢er), P);
+ float radiussq = radius * radius;
+ return distsq < radiussq;
+ }
+ bool Sphere::intersects(const XMFLOAT3& P) const
+ {
+ float distsq = wi::math::DistanceSquared(center, P);
+ float radiussq = radius * radius;
+ return distsq < radiussq;
+ }
bool Sphere::intersects(const AABB& b) const
{
if (!b.IsValid())
diff --git a/WickedEngine/wiPrimitive.h b/WickedEngine/wiPrimitive.h
index 38a9fd2bfd..7fc65aa027 100644
--- a/WickedEngine/wiPrimitive.h
+++ b/WickedEngine/wiPrimitive.h
@@ -87,6 +87,8 @@ namespace wi::primitive
{
assert(radius >= 0);
}
+ bool intersects(const XMVECTOR& P) const;
+ bool intersects(const XMFLOAT3& P) const;
bool intersects(const AABB& b) const;
bool intersects(const Sphere& b) const;
bool intersects(const Sphere& b, float& dist) const;
diff --git a/WickedEngine/wiRenderer.cpp b/WickedEngine/wiRenderer.cpp
index e6fa9b245b..14fd5ac5f0 100644
--- a/WickedEngine/wiRenderer.cpp
+++ b/WickedEngine/wiRenderer.cpp
@@ -1107,6 +1107,7 @@ void LoadShaders()
wi::jobsystem::Execute(ctx, [](wi::jobsystem::JobArgs args) { LoadShader(ShaderStage::CS, shaders[CSTYPE_TERRAIN_VIRTUALTEXTURE_UPDATE_BASECOLORMAP], "terrainVirtualTextureUpdateCS.cso"); });
wi::jobsystem::Execute(ctx, [](wi::jobsystem::JobArgs args) { LoadShader(ShaderStage::CS, shaders[CSTYPE_TERRAIN_VIRTUALTEXTURE_UPDATE_NORMALMAP], "terrainVirtualTextureUpdateCS_normalmap.cso"); });
wi::jobsystem::Execute(ctx, [](wi::jobsystem::JobArgs args) { LoadShader(ShaderStage::CS, shaders[CSTYPE_TERRAIN_VIRTUALTEXTURE_UPDATE_SURFACEMAP], "terrainVirtualTextureUpdateCS_surfacemap.cso"); });
+ wi::jobsystem::Execute(ctx, [](wi::jobsystem::JobArgs args) { LoadShader(ShaderStage::CS, shaders[CSTYPE_TERRAIN_VIRTUALTEXTURE_UPDATE_EMISSIVEMAP], "terrainVirtualTextureUpdateCS_emissivemap.cso"); });
wi::jobsystem::Execute(ctx, [](wi::jobsystem::JobArgs args) { LoadShader(ShaderStage::CS, shaders[CSTYPE_MESHLET_PREPARE], "meshlet_prepareCS.cso"); });
wi::jobsystem::Execute(ctx, [](wi::jobsystem::JobArgs args) { LoadShader(ShaderStage::CS, shaders[CSTYPE_IMPOSTOR_PREPARE], "impostor_prepareCS.cso"); });
wi::jobsystem::Execute(ctx, [](wi::jobsystem::JobArgs args) { LoadShader(ShaderStage::CS, shaders[CSTYPE_VIRTUALTEXTURE_TILEREQUESTS], "virtualTextureTileRequestsCS.cso"); });
diff --git a/WickedEngine/wiScene.cpp b/WickedEngine/wiScene.cpp
index 0b4959ad21..772171de87 100644
--- a/WickedEngine/wiScene.cpp
+++ b/WickedEngine/wiScene.cpp
@@ -909,6 +909,12 @@ namespace wi::scene
shaderscene.ddgi.cell_size_rcp.y = 1.0f / shaderscene.ddgi.cell_size.y;
shaderscene.ddgi.cell_size_rcp.z = 1.0f / shaderscene.ddgi.cell_size.z;
shaderscene.ddgi.max_distance = std::max(shaderscene.ddgi.cell_size.x, std::max(shaderscene.ddgi.cell_size.y, shaderscene.ddgi.cell_size.z)) * 1.5f;
+
+ shaderscene.terrain.init();
+ if (terrains.GetCount() > 0)
+ {
+ shaderscene.terrain = terrains[0].GetShaderTerrain();
+ }
}
void Scene::Clear()
{
@@ -4105,7 +4111,7 @@ namespace wi::scene
uint32_t doublesided : 1; // bool
uint32_t tessellation : 1; // bool
uint32_t alphatest : 1; // bool
- uint32_t customshader : 10;
+ uint32_t customshader : 8;
uint32_t sort_priority : 4;
} bits;
uint32_t value;
@@ -5130,7 +5136,7 @@ namespace wi::scene
if (distance < result.distance && distance >= ray.TMin && distance <= ray.TMax)
{
XMVECTOR nor;
- if (mesh->vertex_normals.empty())
+ if (softbody != nullptr || mesh->vertex_normals.empty()) // Note: for soft body we compute it instead of loading the simulated normals
{
nor = XMVector3Cross(p2 - p1, p1 - p0);
}
diff --git a/WickedEngine/wiScene.h b/WickedEngine/wiScene.h
index d8d8f689d3..e5bea3379d 100644
--- a/WickedEngine/wiScene.h
+++ b/WickedEngine/wiScene.h
@@ -32,7 +32,7 @@ namespace wi::scene
wi::ecs::ComponentManager& layers = componentLibrary.Register("wi::scene::Scene::layers");
wi::ecs::ComponentManager& transforms = componentLibrary.Register("wi::scene::Scene::transforms");
wi::ecs::ComponentManager& hierarchy = componentLibrary.Register("wi::scene::Scene::hierarchy");
- wi::ecs::ComponentManager& materials = componentLibrary.Register("wi::scene::Scene::materials", 3); // version = 3
+ wi::ecs::ComponentManager& materials = componentLibrary.Register("wi::scene::Scene::materials", 4); // version = 4
wi::ecs::ComponentManager& meshes = componentLibrary.Register("wi::scene::Scene::meshes", 2); // version = 2
wi::ecs::ComponentManager& impostors = componentLibrary.Register("wi::scene::Scene::impostors");
wi::ecs::ComponentManager& objects = componentLibrary.Register("wi::scene::Scene::objects", 3); // version = 3
@@ -57,7 +57,7 @@ namespace wi::scene
wi::ecs::ComponentManager& scripts = componentLibrary.Register("wi::scene::Scene::scripts");
wi::ecs::ComponentManager& expressions = componentLibrary.Register("wi::scene::Scene::expressions");
wi::ecs::ComponentManager& humanoids = componentLibrary.Register("wi::scene::Scene::humanoids", 1); // version = 1
- wi::ecs::ComponentManager& terrains = componentLibrary.Register("wi::scene::Scene::terrains", 4); // version = 4
+ wi::ecs::ComponentManager& terrains = componentLibrary.Register("wi::scene::Scene::terrains", 5); // version = 5
wi::ecs::ComponentManager& sprites = componentLibrary.Register("wi::scene::Scene::sprites");
wi::ecs::ComponentManager& fonts = componentLibrary.Register("wi::scene::Scene::fonts");
wi::ecs::ComponentManager& voxel_grids = componentLibrary.Register("wi::scene::Scene::voxel_grids");
diff --git a/WickedEngine/wiScene_BindLua.cpp b/WickedEngine/wiScene_BindLua.cpp
index 30b778990c..d515faebf5 100644
--- a/WickedEngine/wiScene_BindLua.cpp
+++ b/WickedEngine/wiScene_BindLua.cpp
@@ -319,8 +319,9 @@ FILTER_OPAQUE = 1 << 0
FILTER_TRANSPARENT = 1 << 1
FILTER_WATER = 1 << 2
FILTER_NAVIGATION_MESH = 1 << 3
-FILTER_OBJECT_ALL = FILTER_OPAQUE | FILTER_TRANSPARENT | FILTER_WATER | FILTER_NAVIGATION_MESH
-FILTER_COLLIDER = 1 << 4
+FILTER_TERRAIN = 1 << 4
+FILTER_OBJECT_ALL = FILTER_OPAQUE | FILTER_TRANSPARENT | FILTER_WATER | FILTER_NAVIGATION_MESH | FILTER_TERRAIN
+FILTER_COLLIDER = 1 << 5
FILTER_ALL = ~0
PICK_VOID = FILTER_NONE
diff --git a/WickedEngine/wiScene_Components.cpp b/WickedEngine/wiScene_Components.cpp
index 703c280ce2..1b2e04ff39 100644
--- a/WickedEngine/wiScene_Components.cpp
+++ b/WickedEngine/wiScene_Components.cpp
@@ -310,6 +310,14 @@ namespace wi::scene
material.anisotropy_rotation_sin = 0;
material.anisotropy_rotation_cos = 0;
}
+ if (blend_with_terrain_height > 0)
+ {
+ material.blend_with_terrain_height_rcp = 1.0f / blend_with_terrain_height;
+ }
+ else
+ {
+ material.blend_with_terrain_height_rcp = 0;
+ }
material.stencilRef = wi::renderer::CombineStencilrefs(engineStencilRef, userStencilRef);
material.shaderType = (uint)shaderType;
material.userdata = userdata;
diff --git a/WickedEngine/wiScene_Components.h b/WickedEngine/wiScene_Components.h
index e63df21d26..3eedf0e759 100644
--- a/WickedEngine/wiScene_Components.h
+++ b/WickedEngine/wiScene_Components.h
@@ -144,6 +144,7 @@ namespace wi::scene
SHADERTYPE_PBR_CLOTH,
SHADERTYPE_PBR_CLEARCOAT,
SHADERTYPE_PBR_CLOTH_CLEARCOAT,
+ SHADERTYPE_PBR_TERRAINBLENDED,
SHADERTYPE_COUNT
} shaderType = SHADERTYPE_PBR;
static_assert(SHADERTYPE_COUNT == SHADERTYPE_BIN_COUNT, "These values must match!");
@@ -159,6 +160,7 @@ namespace wi::scene
{"SHEEN"}, // SHADERTYPE_PBR_CLOTH,
{"CLEARCOAT"}, // SHADERTYPE_PBR_CLEARCOAT,
{"SHEEN", "CLEARCOAT"}, // SHADERTYPE_PBR_CLOTH_CLEARCOAT,
+ {"TERRAINBLENDED"}, //SHADERTYPE_PBR_TERRAINBLENDED
};
static_assert(SHADERTYPE_COUNT == arraysize(shaderTypeDefines), "These values must match!");
@@ -182,6 +184,7 @@ namespace wi::scene
float alphaRef = 1.0f;
float anisotropy_strength = 0;
float anisotropy_rotation = 0; //radians, counter-clockwise
+ float blend_with_terrain_height = 0;
XMFLOAT4 sheenColor = XMFLOAT4(1, 1, 1, 1);
float sheenRoughness = 0;
diff --git a/WickedEngine/wiScene_Serializers.cpp b/WickedEngine/wiScene_Serializers.cpp
index 4068d8a763..833b90be4e 100644
--- a/WickedEngine/wiScene_Serializers.cpp
+++ b/WickedEngine/wiScene_Serializers.cpp
@@ -237,6 +237,11 @@ namespace wi::scene
archive >> textures[TRANSPARENCYMAP].uvset;
}
+ if (seri.GetVersion() >= 4)
+ {
+ archive >> blend_with_terrain_height;
+ }
+
for (auto& x : textures)
{
if (!x.name.empty())
@@ -381,6 +386,11 @@ namespace wi::scene
archive << wi::helper::GetPathRelative(dir, textures[TRANSPARENCYMAP].name);
archive << textures[TRANSPARENCYMAP].uvset;
}
+
+ if (seri.GetVersion() >= 4)
+ {
+ archive << blend_with_terrain_height;
+ }
}
}
void MeshComponent::Serialize(wi::Archive& archive, EntitySerializer& seri)
diff --git a/WickedEngine/wiTerrain.cpp b/WickedEngine/wiTerrain.cpp
index fa1d4a52e6..7cf0447b9e 100644
--- a/WickedEngine/wiTerrain.cpp
+++ b/WickedEngine/wiTerrain.cpp
@@ -13,7 +13,6 @@
#include
#include
#include
-#include
using namespace wi::ecs;
using namespace wi::scene;
@@ -187,6 +186,14 @@ namespace wi::terrain
static std::mutex locker;
+ void weight_norm(XMFLOAT4& weights)
+ {
+ const float norm = 1.0f / (weights.x + weights.y + weights.z + weights.w);
+ weights.x *= norm;
+ weights.y *= norm;
+ weights.z *= norm;
+ weights.w *= norm;
+ };
void VirtualTextureAtlas::Residency::init(uint32_t resolution)
{
@@ -398,6 +405,8 @@ namespace wi::terrain
grass_properties.viewDistance = chunk_width;
generator = std::make_shared();
+
+ materialEntities.resize(MATERIAL_COUNT);
}
Terrain::~Terrain()
{
@@ -411,8 +420,8 @@ namespace wi::terrain
generator->scene.Clear();
// save material parameters:
- wi::scene::MaterialComponent materials[MATERIAL_COUNT];
- for (int i = 0; i < MATERIAL_COUNT; ++i)
+ materials.resize(materialEntities.size());
+ for (int i = 0; i < materialEntities.size(); ++i)
{
MaterialComponent* material = scene->materials.GetComponent(materialEntities[i]);
if (material == nullptr)
@@ -488,7 +497,7 @@ namespace wi::terrain
// Restore surface source materials:
{
- for (int i = 0; i < MATERIAL_COUNT; ++i)
+ for (size_t i = 0; i < materialEntities.size(); ++i)
{
if (materialEntities[i] == INVALID_ENTITY)
{
@@ -499,7 +508,7 @@ namespace wi::terrain
{
scene->materials.Create(materialEntities[i]);
}
- if (!scene->names.Contains(materialEntities[i]))
+ if (i < MATERIAL_COUNT && !scene->names.Contains(materialEntities[i]))
{
NameComponent& name = scene->names.Create(materialEntities[i]);
switch (i)
@@ -662,7 +671,6 @@ namespace wi::terrain
// Check whether there are any materials that would write to virtual textures:
bool virtual_texture_any = false;
- bool virtual_texture_available[TEXTURESLOT_COUNT] = {};
if (scene->materials.GetCount() > 0)
{
@@ -672,17 +680,16 @@ namespace wi::terrain
if (material == nullptr)
continue;
- for (int i = 0; i < TEXTURESLOT_COUNT; ++i)
+ for (int i = 0; i < TEXTURESLOT_COUNT && !virtual_texture_any; ++i)
{
- virtual_texture_available[i] = false;
switch (i)
{
case MaterialComponent::BASECOLORMAP:
case MaterialComponent::NORMALMAP:
case MaterialComponent::SURFACEMAP:
+ case MaterialComponent::EMISSIVEMAP:
if (material->textures[i].resource.IsValid())
{
- virtual_texture_available[i] = true;
virtual_texture_any = true;
}
break;
@@ -690,8 +697,10 @@ namespace wi::terrain
break;
}
}
+
+ if (virtual_texture_any)
+ break;
}
- virtual_texture_available[MaterialComponent::SURFACEMAP] = true; // this is always needed to bake individual material properties
if (grassEntity != INVALID_ENTITY)
{
@@ -862,6 +871,20 @@ namespace wi::terrain
UpdateVirtualTexturesCPU();
}
+ const uint64_t required_chunk_buffer_size = sizeof(ShaderTerrainChunk) * (chunk_buffer_range * 2 + 1) * (chunk_buffer_range * 2 + 1);
+ if (chunk_buffer.desc.size < required_chunk_buffer_size)
+ {
+ GPUBufferDesc desc;
+ desc.usage = Usage::DEFAULT;
+ desc.size = required_chunk_buffer_size;
+ desc.bind_flags = BindFlag::SHADER_RESOURCE;
+ desc.misc_flags = ResourceMiscFlag::BUFFER_STRUCTURED;
+ desc.stride = sizeof(ShaderTerrainChunk);
+ bool success = device->CreateBuffer(&desc, nullptr, &chunk_buffer);
+ assert(success);
+ device->SetName(&chunk_buffer, "wi::terrain::Terrain::chunk_buffer");
+ }
+
// Start the generation on a background thread and keep it running until the next frame
wi::jobsystem::Execute(generator->workload, [=](wi::jobsystem::JobArgs args) {
@@ -883,6 +906,7 @@ namespace wi::terrain
ObjectComponent& object = *generator->scene.objects.GetComponent(chunk_data.entity);
object.lod_distance_multiplier = lod_multiplier;
object.filterMask |= wi::enums::FILTER_NAVIGATION_MESH;
+ object.filterMask |= wi::enums::FILTER_TERRAIN;
generator->scene.Component_Attach(chunk_data.entity, chunkGroupEntity);
TransformComponent& transform = *generator->scene.transforms.GetComponent(chunk_data.entity);
@@ -897,6 +921,7 @@ namespace wi::terrain
material.SetRoughness(1);
material.SetMetalness(1);
material.SetReflectance(1);
+ material.SetEmissiveStrength(100);
MeshComponent& mesh = generator->scene.meshes.Create(chunk_data.entity);
mesh.SetQuantizedPositionsDisabled(true); // connecting meshes quantization is not correct because mismatching AABBs
@@ -914,10 +939,17 @@ namespace wi::terrain
mesh.vertex_normals.resize(vertexCount);
mesh.vertex_tangents.resize(vertexCount);
mesh.vertex_uvset_0.resize(vertexCount);
- chunk_data.region_weights.resize(vertexCount);
+
+ chunk_data.blendmap_layers.reserve(4);
+ chunk_data.blendmap_layers.emplace_back().pixels.resize(vertexCount);
+ chunk_data.blendmap_layers.emplace_back().pixels.resize(vertexCount);
+ chunk_data.blendmap_layers.emplace_back().pixels.resize(vertexCount);
+ chunk_data.blendmap_layers.emplace_back().pixels.resize(vertexCount);
chunk_data.mesh_vertex_positions = mesh.vertex_positions.data();
+ chunk_data.heightmap_data.resize(vertexCount);
+
wi::HairParticleSystem grass = grass_properties;
grass.vertex_lengths.resize(vertexCount);
std::atomic grass_valid_vertex_count{ 0 };
@@ -946,12 +978,16 @@ namespace wi::terrain
{
modifier->Apply(world_pos, height);
}
+ if (i == 0)
+ {
+ chunk_data.heightmap_data[index] = uint16_t(height * 65535);
+ }
height = wi::math::Lerp(bottomLevel, topLevel, height);
corners[i] = XMVectorSet(world_pos.x, height, world_pos.y, 0);
}
const float height = XMVectorGetY(corners[0]);
- const XMVECTOR T = XMVectorSubtract(corners[2], corners[1]);
- const XMVECTOR B = XMVectorSubtract(corners[1], corners[0]);
+ const XMVECTOR T = XMVectorSubtract(corners[1], corners[2]);
+ const XMVECTOR B = XMVectorSubtract(corners[0], corners[1]);
const XMVECTOR N = XMVector3Normalize(XMVector3Cross(T, B));
XMFLOAT3 normal;
XMStoreFloat3(&normal, N);
@@ -965,17 +1001,15 @@ namespace wi::terrain
const float region_low_altitude = bottomLevel == 0 ? 0 : std::pow(wi::math::saturate(wi::math::InverseLerp(0, bottomLevel, height)), region2);
const float region_high_altitude = topLevel == 0 ? 0 : std::pow(wi::math::saturate(wi::math::InverseLerp(0, topLevel, height)), region3);
- XMFLOAT4 materialBlendWeights(region_base, 0, 0, 0);
- materialBlendWeights = wi::math::Lerp(materialBlendWeights, XMFLOAT4(0, 1, 0, 0), region_slope);
- materialBlendWeights = wi::math::Lerp(materialBlendWeights, XMFLOAT4(0, 0, 1, 0), region_low_altitude);
- materialBlendWeights = wi::math::Lerp(materialBlendWeights, XMFLOAT4(0, 0, 0, 1), region_high_altitude);
- const float weight_norm = 1.0f / (materialBlendWeights.x + materialBlendWeights.y + materialBlendWeights.z + materialBlendWeights.w);
- materialBlendWeights.x *= weight_norm;
- materialBlendWeights.y *= weight_norm;
- materialBlendWeights.z *= weight_norm;
- materialBlendWeights.w *= weight_norm;
+ XMFLOAT4 materialBlendWeights(region_base, region_slope, region_low_altitude, region_high_altitude);
+
+ chunk_data.blendmap_layers[0].pixels[index] = uint8_t(materialBlendWeights.x * 255);
+ chunk_data.blendmap_layers[1].pixels[index] = uint8_t(materialBlendWeights.y * 255);
+ chunk_data.blendmap_layers[2].pixels[index] = uint8_t(materialBlendWeights.z * 255);
+ chunk_data.blendmap_layers[3].pixels[index] = uint8_t(materialBlendWeights.w * 255);
- chunk_data.region_weights[index] = wi::Color::fromFloat4(materialBlendWeights);
+ // Normalize after store, blending shader wants unnormalized!
+ weight_norm(materialBlendWeights);
mesh.vertex_positions[index] = XMFLOAT3(x, height, z);
mesh.vertex_normals[index] = normal;
@@ -1000,7 +1034,7 @@ namespace wi::terrain
}
});
wi::jobsystem::Wait(ctx); // wait until chunk's vertex buffer is fully generated
-
+
object.SetCastShadow(slope_cast_shadow.load());
mesh.SetDoubleSidedShadow(slope_cast_shadow.load());
@@ -1023,10 +1057,11 @@ namespace wi::terrain
chunk_data.grass.CreateFromMesh(mesh);
}
+ wi::jobsystem::Wait(ctx); // wait until mesh.CreateRenderData() async task finishes
+
// Create the textures for virtual texture update:
CreateChunkRegionTexture(chunk_data);
- wi::jobsystem::Wait(ctx); // wait until mesh.CreateRenderData() async task finishes
generated_something = true;
}
@@ -1092,9 +1127,12 @@ namespace wi::terrain
const XMFLOAT3& pos0 = chunk_data.mesh_vertex_positions[ind0];
const XMFLOAT3& pos1 = chunk_data.mesh_vertex_positions[ind1];
const XMFLOAT3& pos2 = chunk_data.mesh_vertex_positions[ind2];
- const XMFLOAT4 region0 = chunk_data.region_weights[ind0];
- const XMFLOAT4 region1 = chunk_data.region_weights[ind1];
- const XMFLOAT4 region2 = chunk_data.region_weights[ind2];
+ XMFLOAT4 region0 = wi::Color(chunk_data.blendmap_layers[0].pixels[ind0], chunk_data.blendmap_layers[1].pixels[ind0], chunk_data.blendmap_layers[2].pixels[ind0], chunk_data.blendmap_layers[3].pixels[ind0]);
+ XMFLOAT4 region1 = wi::Color(chunk_data.blendmap_layers[0].pixels[ind1], chunk_data.blendmap_layers[1].pixels[ind1], chunk_data.blendmap_layers[2].pixels[ind1], chunk_data.blendmap_layers[3].pixels[ind1]);
+ XMFLOAT4 region2 = wi::Color(chunk_data.blendmap_layers[0].pixels[ind2], chunk_data.blendmap_layers[1].pixels[ind2], chunk_data.blendmap_layers[2].pixels[ind2], chunk_data.blendmap_layers[3].pixels[ind2]);
+ weight_norm(region0);
+ weight_norm(region1);
+ weight_norm(region2);
// random barycentric coords on the triangle:
float f = rng.next_float();
float g = rng.next_float();
@@ -1214,19 +1252,44 @@ namespace wi::terrain
void Terrain::CreateChunkRegionTexture(ChunkData& chunk_data)
{
- if (!chunk_data.region_weights_texture.IsValid())
+ GraphicsDevice* device = GetDevice();
+
+ if (!chunk_data.heightmap.IsValid())
{
- GraphicsDevice* device = GetDevice();
TextureDesc desc;
desc.width = (uint32_t)chunk_width;
desc.height = (uint32_t)chunk_width;
- desc.format = Format::R8G8B8A8_UNORM;
+ desc.format = Format::R16_UNORM;
desc.bind_flags = BindFlag::SHADER_RESOURCE;
+
SubresourceData data;
- data.data_ptr = chunk_data.region_weights.data();
- data.row_pitch = chunk_width * sizeof(chunk_data.region_weights[0]);
- bool success = device->CreateTexture(&desc, &data, &chunk_data.region_weights_texture);
+ data.data_ptr = chunk_data.heightmap_data.data();
+ data.row_pitch = chunk_width * sizeof(uint16_t);
+
+ bool success = device->CreateTexture(&desc, &data, &chunk_data.heightmap);
+ assert(success);
+ device->SetName(&chunk_data.heightmap, "wi::terrain::ChunkData::heightmap");
+ }
+
+ if (!chunk_data.blendmap.IsValid() || chunk_data.blendmap.desc.array_size != (uint32_t)chunk_data.blendmap_layers.size())
+ {
+ TextureDesc desc;
+ desc.width = (uint32_t)chunk_width;
+ desc.height = (uint32_t)chunk_width;
+ desc.array_size = (uint32_t)chunk_data.blendmap_layers.size();
+ desc.format = Format::R8_UNORM;
+ desc.bind_flags = BindFlag::SHADER_RESOURCE;
+
+ wi::vector data(chunk_data.blendmap_layers.size());
+ for (size_t i = 0; i < chunk_data.blendmap_layers.size(); ++i)
+ {
+ data[i].data_ptr = chunk_data.blendmap_layers[i].pixels.data();
+ data[i].row_pitch = chunk_width * sizeof(uint8_t);
+ }
+
+ bool success = device->CreateTexture(&desc, data.data(), &chunk_data.blendmap);
assert(success);
+ device->SetName(&chunk_data.blendmap, "wi::terrain::ChunkData::blendmap");
}
}
@@ -1292,6 +1355,8 @@ namespace wi::terrain
switch (map_type)
{
default:
+ case MaterialComponent::BASECOLORMAP:
+ case MaterialComponent::EMISSIVEMAP:
desc.format = Format::BC1_UNORM_SRGB;
desc_raw_block.format = Format::R32G32_UINT;
break;
@@ -1427,7 +1492,7 @@ namespace wi::terrain
}
material->textures[map_type].lod_clamp = (float)vt.lod_count - 2;
}
- vt.region_weights_texture = chunk_data.region_weights_texture;
+ vt.blendmap = chunk_data.blendmap;
}
virtual_textures_in_use.push_back(&vt);
@@ -1543,6 +1608,37 @@ namespace wi::terrain
device->EventBegin("Terrain - UpdateVirtualTexturesGPU", cmd);
auto range = wi::profiler::BeginRangeGPU("Terrain - UpdateVirtualTexturesGPU", cmd);
+ if (chunk_buffer.IsValid())
+ {
+ device->EventBegin("Update chunk buffer", cmd);
+ auto mem = device->AllocateGPU(chunk_buffer.desc.size, cmd);
+ ShaderTerrainChunk* shader_chunks = (ShaderTerrainChunk*)mem.data;
+ for (int y = -chunk_buffer_range; y <= chunk_buffer_range; ++y)
+ {
+ for (int x = -chunk_buffer_range; x <= chunk_buffer_range; ++x)
+ {
+ ShaderTerrainChunk shader_chunk;
+ shader_chunk.init();
+
+ auto chunk = center_chunk;
+ chunk.x += x;
+ chunk.z += y;
+ auto it = chunks.find(chunk);
+ if (it != chunks.end())
+ {
+ const auto& chunk_data = it->second;
+ shader_chunk.heightmap = device->GetDescriptorIndex(&chunk_data.heightmap, SubresourceType::SRV);
+ shader_chunk.materialID = (uint)scene->materials.GetIndex(chunk_data.entity);
+ }
+
+ const uint idx = (x + chunk_buffer_range) + (y + chunk_buffer_range) * (chunk_buffer_range * 2 + 1);
+ std::memcpy(shader_chunks + idx, &shader_chunk, sizeof(shader_chunk));
+ }
+ }
+ device->CopyBuffer(&chunk_buffer, 0, &mem.buffer, mem.offset, chunk_buffer.desc.size, cmd);
+ device->EventEnd(cmd);
+ }
+
device->Barrier(virtual_texture_barriers_before_update.data(), (uint32_t)virtual_texture_barriers_before_update.size(), cmd);
device->EventBegin("Clear Metadata", cmd);
@@ -1590,40 +1686,46 @@ namespace wi::terrain
device->EventEnd(cmd);
device->EventBegin("Render Tile Regions", cmd);
+ wi::renderer::BindCommonResources(cmd);
- ShaderMaterial materials[MATERIAL_COUNT];
- for (size_t i = 0; i < MATERIAL_COUNT && scene->materials.GetCount() > 0; ++i)
- {
- const MaterialComponent* material = scene->materials.GetComponent(materialEntities[i]);
- if (material == nullptr)
- continue;
- material->WriteShaderMaterial(&materials[i]);
- }
- device->BindDynamicConstantBuffer(materials, 0, cmd);
-
- for (uint32_t map_type = 0; map_type < 3; map_type++)
+ for (const VirtualTexture* vt : virtual_textures_in_use)
{
- switch (map_type)
+ for (uint32_t map_type = 0; map_type < arraysize(atlas.maps); map_type++)
{
- case MaterialComponent::BASECOLORMAP:
- device->BindComputeShader(wi::renderer::GetShader(wi::enums::CSTYPE_TERRAIN_VIRTUALTEXTURE_UPDATE_BASECOLORMAP), cmd);
- device->BindUAV(&atlas.maps[MaterialComponent::BASECOLORMAP].texture_raw_block, 0, cmd);
- break;
- case MaterialComponent::NORMALMAP:
- device->BindComputeShader(wi::renderer::GetShader(wi::enums::CSTYPE_TERRAIN_VIRTUALTEXTURE_UPDATE_NORMALMAP), cmd);
- device->BindUAV(&atlas.maps[MaterialComponent::NORMALMAP].texture_raw_block, 0, cmd);
- break;
- case MaterialComponent::SURFACEMAP:
- device->BindComputeShader(wi::renderer::GetShader(wi::enums::CSTYPE_TERRAIN_VIRTUALTEXTURE_UPDATE_SURFACEMAP), cmd);
- device->BindUAV(&atlas.maps[MaterialComponent::SURFACEMAP].texture_raw_block, 0, cmd);
- break;
- default:
- assert(0);
- break;
- }
+ switch (map_type)
+ {
+ case MaterialComponent::BASECOLORMAP:
+ device->BindComputeShader(wi::renderer::GetShader(wi::enums::CSTYPE_TERRAIN_VIRTUALTEXTURE_UPDATE_BASECOLORMAP), cmd);
+ device->BindUAV(&atlas.maps[MaterialComponent::BASECOLORMAP].texture_raw_block, 0, cmd);
+ break;
+ case MaterialComponent::NORMALMAP:
+ device->BindComputeShader(wi::renderer::GetShader(wi::enums::CSTYPE_TERRAIN_VIRTUALTEXTURE_UPDATE_NORMALMAP), cmd);
+ device->BindUAV(&atlas.maps[MaterialComponent::NORMALMAP].texture_raw_block, 0, cmd);
+ break;
+ case MaterialComponent::SURFACEMAP:
+ device->BindComputeShader(wi::renderer::GetShader(wi::enums::CSTYPE_TERRAIN_VIRTUALTEXTURE_UPDATE_SURFACEMAP), cmd);
+ device->BindUAV(&atlas.maps[MaterialComponent::SURFACEMAP].texture_raw_block, 0, cmd);
+ break;
+ case MaterialComponent::EMISSIVEMAP:
+ device->BindComputeShader(wi::renderer::GetShader(wi::enums::CSTYPE_TERRAIN_VIRTUALTEXTURE_UPDATE_EMISSIVEMAP), cmd);
+ device->BindUAV(&atlas.maps[MaterialComponent::EMISSIVEMAP].texture_raw_block, 0, cmd);
+ break;
+ default:
+ assert(0);
+ break;
+ }
+
+ auto mem = device->AllocateGPU(sizeof(uint) * vt->blendmap.desc.array_size, cmd);
+ const uint blendcount = std::min(vt->blendmap.desc.array_size, uint(materialEntities.size()));
+ for (uint i = 0; i < blendcount; ++i)
+ {
+ const uint material_index = (uint)scene->materials.GetIndex(materialEntities[i]);
+ std::memcpy((uint*)mem.data + i, &material_index, sizeof(uint)); // force memcpy to avoid uncached write into GPU pointer!
+ }
+ const uint blendmap_buffer_offset = (uint)mem.offset;
+ device->BindResource(&vt->blendmap, 0, cmd);
+ device->BindResource(&mem.buffer, 1, cmd);
- for (const VirtualTexture* vt : virtual_textures_in_use)
- {
for (auto& request : vt->update_requests)
{
uint request_lod_resolution = std::max(1u, vt->resolution >> request.lod);
@@ -1637,7 +1739,7 @@ namespace wi::terrain
int(request.x * SVT_TILE_SIZE) - int(SVT_TILE_BORDER),
int(request.y * SVT_TILE_SIZE) - int(SVT_TILE_BORDER)
);
- push.region_weights_textureRO = device->GetDescriptorIndex(&vt->region_weights_texture, SubresourceType::SRV);
+ push.blendmap_buffer_offset = blendmap_buffer_offset;
if (request_lod_resolution < SVT_TILE_SIZE)
{
@@ -1789,6 +1891,30 @@ namespace wi::terrain
device->EventEnd(cmd);
}
+ ShaderTerrain Terrain::GetShaderTerrain() const
+ {
+ GraphicsDevice* device = GetDevice();
+
+ ShaderTerrain shader_terrain;
+ shader_terrain.init();
+
+ auto it = chunks.find(center_chunk);
+ if (it != chunks.end())
+ {
+ const auto& chunk_data = it->second;
+ shader_terrain.chunk_size = chunk_half_width * 2 * chunk_scale;
+ shader_terrain.center_chunk_pos.x = center_chunk.x * shader_terrain.chunk_size - shader_terrain.chunk_size * 0.5f;
+ shader_terrain.center_chunk_pos.y = 0;
+ shader_terrain.center_chunk_pos.z = center_chunk.z * shader_terrain.chunk_size - shader_terrain.chunk_size * 0.5f;
+ shader_terrain.chunk_buffer = device->GetDescriptorIndex(&chunk_buffer, SubresourceType::SRV);
+ shader_terrain.chunk_buffer_range = chunk_buffer_range;
+ shader_terrain.min_height = bottomLevel;
+ shader_terrain.max_height = topLevel;
+ }
+
+ return shader_terrain;
+ }
+
void Terrain::Serialize(wi::Archive& archive, wi::ecs::EntitySerializer& seri)
{
Generation_Cancel();
@@ -1925,7 +2051,34 @@ namespace wi::terrain
seri.version = terrain_version;
archive >> chunk_data.grass_density_current;
- archive >> chunk_data.region_weights;
+ if (terrain_version >= 5)
+ {
+ size_t blendmapCount = 0;
+ archive >> blendmapCount;
+ chunk_data.blendmap_layers.resize(blendmapCount);
+ for (size_t i = 0; i < chunk_data.blendmap_layers.size(); ++i)
+ {
+ archive >> chunk_data.blendmap_layers[i].pixels;
+ }
+ archive >> chunk_data.heightmap_data;
+ }
+ else
+ {
+ wi::vector blendmap4;
+ archive >> blendmap4;
+ chunk_data.blendmap_layers.resize(4);
+ for (size_t i = 0; i < 4; ++i)
+ {
+ chunk_data.blendmap_layers[i].pixels.resize(blendmap4.size());
+ }
+ for (size_t i = 0; i < blendmap4.size(); ++i)
+ {
+ chunk_data.blendmap_layers[0].pixels[i] = blendmap4[i].getR();
+ chunk_data.blendmap_layers[1].pixels[i] = blendmap4[i].getG();
+ chunk_data.blendmap_layers[2].pixels[i] = blendmap4[i].getB();
+ chunk_data.blendmap_layers[3].pixels[i] = blendmap4[i].getA();
+ }
+ }
archive >> chunk_data.sphere.center;
archive >> chunk_data.sphere.radius;
archive >> chunk_data.position;
@@ -2052,7 +2205,12 @@ namespace wi::terrain
seri.version = terrain_version;
archive << chunk_data.grass_density_current;
- archive << chunk_data.region_weights;
+ archive << chunk_data.blendmap_layers.size();
+ for (size_t i = 0; i < chunk_data.blendmap_layers.size(); ++i)
+ {
+ archive << chunk_data.blendmap_layers[i].pixels;
+ }
+ archive << chunk_data.heightmap_data;
archive << chunk_data.sphere.center;
archive << chunk_data.sphere.radius;
archive << chunk_data.position;
@@ -2094,7 +2252,32 @@ namespace wi::terrain
// Caution: seri.version changes must be handled carefully!
- if (terrain_version >= 4)
+ if (terrain_version >= 5)
+ {
+ if (archive.IsReadMode())
+ {
+ SerializeEntity(archive, chunkGroupEntity, seri);
+ size_t materialCount = 0;
+ archive >> materialCount;
+ materialEntities.resize(materialCount);
+ for (size_t i = 0; i < materialEntities.size(); ++i)
+ {
+ SerializeEntity(archive, materialEntities[i], seri);
+ }
+ SerializeEntity(archive, grassEntity, seri);
+ }
+ else
+ {
+ SerializeEntity(archive, chunkGroupEntity, seri);
+ archive << materialEntities.size();
+ for (size_t i = 0; i < materialEntities.size(); ++i)
+ {
+ SerializeEntity(archive, materialEntities[i], seri);
+ }
+ SerializeEntity(archive, grassEntity, seri);
+ }
+ }
+ else if (terrain_version >= 4)
{
SerializeEntity(archive, chunkGroupEntity, seri);
for (size_t i = 0; i < MATERIAL_COUNT; ++i)
diff --git a/WickedEngine/wiTerrain.h b/WickedEngine/wiTerrain.h
index 7a3d9abc46..d1260c6493 100644
--- a/WickedEngine/wiTerrain.h
+++ b/WickedEngine/wiTerrain.h
@@ -6,6 +6,7 @@
#include "wiECS.h"
#include "wiColor.h"
#include "wiHairParticle.h"
+#include "wiVector.h"
#include
@@ -59,7 +60,7 @@ namespace wi::terrain
wi::graphics::Texture texture;
wi::graphics::Texture texture_raw_block;
};
- Map maps[3];
+ Map maps[4];
wi::graphics::GPUBuffer tile_pool;
struct Tile
@@ -151,6 +152,11 @@ namespace wi::terrain
atlas.free_residency(residency);
}
+ void invalidate()
+ {
+ resolution = 0;
+ }
+
// Attach this data to Virtual Texture because we will record these by separate CPU thread:
struct UpdateRequest
{
@@ -161,7 +167,12 @@ namespace wi::terrain
uint8_t tile_y = 0;
};
mutable wi::vector update_requests;
- wi::graphics::Texture region_weights_texture;
+ wi::graphics::Texture blendmap;
+ };
+
+ struct BlendmapLayer
+ {
+ wi::vector pixels;
};
struct ChunkData
@@ -173,12 +184,23 @@ namespace wi::terrain
float prop_density_current = 1;
wi::HairParticleSystem grass;
float grass_density_current = 1;
- wi::vector region_weights;
- wi::graphics::Texture region_weights_texture;
+ wi::vector blendmap_layers;
+ wi::graphics::Texture blendmap;
wi::primitive::Sphere sphere;
XMFLOAT3 position = XMFLOAT3(0, 0, 0);
bool visible = true;
std::shared_ptr vt;
+ wi::vector heightmap_data;
+ wi::graphics::Texture heightmap;
+
+ void enable_blendmap_layer(size_t materialIndex)
+ {
+ while (blendmap_layers.size() < materialIndex + 1)
+ {
+ blendmap_layers.emplace_back().pixels.resize(vertexCount);
+ std::fill(blendmap_layers.back().pixels.begin(), blendmap_layers.back().pixels.end(), 0);
+ }
+ }
};
struct Prop
@@ -217,7 +239,7 @@ namespace wi::terrain
wi::ecs::Entity terrainEntity = wi::ecs::INVALID_ENTITY;
wi::ecs::Entity chunkGroupEntity = wi::ecs::INVALID_ENTITY;
wi::scene::Scene* scene = nullptr;
- wi::ecs::Entity materialEntities[MATERIAL_COUNT] = {};
+ wi::vector materialEntities = {};
wi::ecs::Entity grassEntity = wi::ecs::INVALID_ENTITY;
wi::scene::WeatherComponent weather;
wi::HairParticleSystem grass_properties;
@@ -239,6 +261,9 @@ namespace wi::terrain
wi::graphics::Sampler sampler;
VirtualTextureAtlas atlas;
+ int chunk_buffer_range = 3; // how many chunks to upload to GPU in X and Z directions
+ wi::graphics::GPUBuffer chunk_buffer;
+
constexpr bool IsCenterToCamEnabled() const { return _flags & CENTER_TO_CAM; }
constexpr bool IsRemovalEnabled() const { return _flags & REMOVAL; }
constexpr bool IsGrassEnabled() const { return _flags & GRASS; }
@@ -289,7 +314,12 @@ namespace wi::terrain
void AllocateVirtualTextureTileRequestsGPU(wi::graphics::CommandList cmd) const;
void WritebackTileRequestsGPU(wi::graphics::CommandList cmd) const;
+ ShaderTerrain GetShaderTerrain() const;
+
void Serialize(wi::Archive& archive, wi::ecs::EntitySerializer& seri);
+
+ private:
+ wi::vector materials; // temp storage allocation
};
struct Modifier
diff --git a/WickedEngine/wiVersion.cpp b/WickedEngine/wiVersion.cpp
index 150d0a92ca..ee47c6042d 100644
--- a/WickedEngine/wiVersion.cpp
+++ b/WickedEngine/wiVersion.cpp
@@ -9,7 +9,7 @@ namespace wi::version
// minor features, major updates, breaking compatibility changes
const int minor = 71;
// minor bug fixes, alterations, refactors, updates
- const int revision = 432;
+ const int revision = 433;
const std::string version_string = std::to_string(major) + "." + std::to_string(minor) + "." + std::to_string(revision);