Vertex AO (#795)
This commit is contained in:
+230
-1
@@ -257,7 +257,7 @@ void ObjectWindow::Create(EditorComponent* _editor)
|
||||
editor = _editor;
|
||||
|
||||
wi::gui::Window::Create(ICON_OBJECT " Object", wi::gui::Window::WindowControls::COLLAPSE | wi::gui::Window::WindowControls::CLOSE);
|
||||
SetSize(XMFLOAT2(670, 740));
|
||||
SetSize(XMFLOAT2(670, 820));
|
||||
|
||||
closeButton.SetTooltip("Delete ObjectComponent");
|
||||
OnClose([=](wi::gui::EventArgs args) {
|
||||
@@ -648,6 +648,216 @@ void ObjectWindow::Create(EditorComponent* _editor)
|
||||
});
|
||||
AddWidget(&clearLightmapButton);
|
||||
|
||||
|
||||
y += step;
|
||||
|
||||
vertexAOButton.Create("Vertex AO");
|
||||
vertexAOButton.SetTooltip("Create or delete per vertex Ambient Occlusion.");
|
||||
vertexAOButton.SetPos(XMFLOAT2(x, y += step));
|
||||
vertexAOButton.SetSize(XMFLOAT2(wid, hei));
|
||||
vertexAOButton.SetLocalizationEnabled(false);
|
||||
vertexAOButton.OnClick([&](wi::gui::EventArgs args) {
|
||||
const Scene& scene = editor->GetCurrentScene();
|
||||
|
||||
// Build BVHs for everything selected:
|
||||
if (!deleteAOMode)
|
||||
{
|
||||
wi::Timer timer;
|
||||
for (auto& x : this->editor->translator.selected)
|
||||
{
|
||||
ObjectComponent* objectcomponent = scene.objects.GetComponent(x.entity);
|
||||
if (objectcomponent != nullptr)
|
||||
{
|
||||
const size_t objectcomponentIndex = scene.objects.GetIndex(x.entity);
|
||||
MeshComponent* meshcomponent = scene.meshes.GetComponent(objectcomponent->meshID);
|
||||
if (meshcomponent == nullptr)
|
||||
continue;
|
||||
if (!meshcomponent->bvh.IsValid())
|
||||
{
|
||||
meshcomponent->BuildBVH();
|
||||
}
|
||||
}
|
||||
}
|
||||
wi::backlog::post("Building BVHs for vertex AO took " + wi::helper::GetTimerDurationText((float)timer.elapsed_seconds()));
|
||||
}
|
||||
|
||||
for (auto& x : this->editor->translator.selected)
|
||||
{
|
||||
ObjectComponent* objectcomponent = scene.objects.GetComponent(x.entity);
|
||||
if (objectcomponent != nullptr)
|
||||
{
|
||||
if (deleteAOMode)
|
||||
{
|
||||
objectcomponent->vertex_ao.clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
const MeshComponent* meshcomponent = scene.meshes.GetComponent(objectcomponent->meshID);
|
||||
if (meshcomponent == nullptr)
|
||||
continue;
|
||||
if (meshcomponent->vertex_normals.size() != meshcomponent->vertex_positions.size())
|
||||
continue;
|
||||
wi::Timer timer;
|
||||
using namespace wi::primitive;
|
||||
objectcomponent->vertex_ao.resize(meshcomponent->vertex_positions.size());
|
||||
const size_t objectcomponentIndex = scene.objects.GetIndex(x.entity);
|
||||
|
||||
uint32_t groupSizePerCore = wi::jobsystem::DispatchGroupCount((uint32_t)objectcomponent->vertex_ao.size(), wi::jobsystem::GetThreadCount());
|
||||
|
||||
wi::jobsystem::context ctx;
|
||||
wi::jobsystem::Dispatch(ctx, (uint32_t)objectcomponent->vertex_ao.size(), groupSizePerCore, [&](wi::jobsystem::JobArgs args) {
|
||||
XMFLOAT3 position = meshcomponent->vertex_positions[args.jobIndex];
|
||||
XMFLOAT3 normal = meshcomponent->vertex_normals[args.jobIndex];
|
||||
const XMMATRIX W = XMLoadFloat4x4(&scene.matrix_objects[objectcomponentIndex]);
|
||||
XMStoreFloat3(&position, XMVector3Transform(XMLoadFloat3(&position), W));
|
||||
XMStoreFloat3(&normal, XMVector3Normalize(XMVector3TransformNormal(XMLoadFloat3(&normal), XMMatrixTranspose(XMMatrixInverse(nullptr, W)))));
|
||||
const XMMATRIX TBN = wi::math::GetTangentSpace(normal);
|
||||
float accum = 0;
|
||||
const uint32_t samplecount = (uint32_t)vertexAORayCountSlider.GetValue();
|
||||
const float raylength = vertexAORayLengthSlider.GetValue();
|
||||
const XMVECTOR rayOrigin = XMLoadFloat3(&position);
|
||||
for (uint32_t sam = 0; sam < samplecount; ++sam)
|
||||
{
|
||||
XMFLOAT2 hamm = wi::math::Hammersley2D(sam, samplecount);
|
||||
XMFLOAT3 hemi = wi::math::HemispherePoint_Cos(hamm.x, hamm.y);
|
||||
XMVECTOR rayDirection = XMLoadFloat3(&hemi);
|
||||
rayDirection = XMVector3TransformNormal(rayDirection, TBN);
|
||||
rayDirection = XMVector3Normalize(rayDirection);
|
||||
XMStoreFloat3(&hemi, rayDirection);
|
||||
|
||||
wi::primitive::Ray ray(position, hemi, 0.0001f, raylength);
|
||||
|
||||
bool hit = false;
|
||||
for (size_t objectIndex = 0; (objectIndex < scene.aabb_objects.size()) && !hit; ++objectIndex)
|
||||
{
|
||||
const AABB& aabb = scene.aabb_objects[objectIndex];
|
||||
if (!ray.intersects(aabb))
|
||||
continue;
|
||||
|
||||
const ObjectComponent& object = scene.objects[objectIndex];
|
||||
if (object.meshID == INVALID_ENTITY)
|
||||
continue;
|
||||
|
||||
const MeshComponent* mesh = scene.meshes.GetComponent(object.meshID);
|
||||
if (mesh == nullptr)
|
||||
continue;
|
||||
|
||||
const Entity entity = scene.objects.GetEntity(objectIndex);
|
||||
const XMMATRIX objectMat = XMLoadFloat4x4(&scene.matrix_objects[objectIndex]);
|
||||
const XMMATRIX objectMatPrev = XMLoadFloat4x4(&scene.matrix_objects_prev[objectIndex]);
|
||||
const XMMATRIX objectMat_Inverse = XMMatrixInverse(nullptr, objectMat);
|
||||
const XMVECTOR rayOrigin_local = XMVector3Transform(rayOrigin, objectMat_Inverse);
|
||||
const XMVECTOR rayDirection_local = XMVector3Normalize(XMVector3TransformNormal(rayDirection, objectMat_Inverse));
|
||||
|
||||
auto intersect_triangle = [&](uint32_t subsetIndex, uint32_t indexOffset, uint32_t triangleIndex)
|
||||
{
|
||||
const uint32_t i0 = mesh->indices[indexOffset + triangleIndex * 3 + 0];
|
||||
const uint32_t i1 = mesh->indices[indexOffset + triangleIndex * 3 + 1];
|
||||
const uint32_t i2 = mesh->indices[indexOffset + triangleIndex * 3 + 2];
|
||||
|
||||
const XMVECTOR p0 = XMLoadFloat3(&mesh->vertex_positions[i0]);
|
||||
const XMVECTOR p1 = XMLoadFloat3(&mesh->vertex_positions[i1]);
|
||||
const XMVECTOR p2 = XMLoadFloat3(&mesh->vertex_positions[i2]);
|
||||
|
||||
float distance;
|
||||
XMFLOAT2 bary;
|
||||
if (wi::math::RayTriangleIntersects(rayOrigin_local, rayDirection_local, p0, p1, p2, distance, bary, ray.TMin, ray.TMax))
|
||||
return true;
|
||||
return false;
|
||||
};
|
||||
|
||||
if (mesh->bvh.IsValid())
|
||||
{
|
||||
Ray ray_local = Ray(rayOrigin_local, rayDirection_local);
|
||||
|
||||
hit = mesh->bvh.IntersectsFirst(ray_local, [&](uint32_t index) {
|
||||
const uint32_t userdata = mesh->bvh_leaf_aabbs[index].userdata;
|
||||
const uint32_t triangleIndex = userdata & 0xFFFFFF;
|
||||
const uint32_t subsetIndex = userdata >> 24u;
|
||||
const MeshComponent::MeshSubset& subset = mesh->subsets[subsetIndex];
|
||||
if (subset.indexCount == 0)
|
||||
return false;
|
||||
const MaterialComponent* material = scene.materials.GetComponent(subset.materialID);
|
||||
if (material != nullptr && material->GetBlendMode() != wi::enums::BLENDMODE_OPAQUE)
|
||||
return false;
|
||||
const uint32_t indexOffset = subset.indexOffset;
|
||||
if (intersect_triangle(subsetIndex, indexOffset, triangleIndex))
|
||||
return true;
|
||||
return false;
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
// Brute-force intersection test:
|
||||
uint32_t first_subset = 0;
|
||||
uint32_t last_subset = 0;
|
||||
mesh->GetLODSubsetRange(0, first_subset, last_subset);
|
||||
for (uint32_t subsetIndex = first_subset; (subsetIndex < last_subset) && !hit; ++subsetIndex)
|
||||
{
|
||||
const MeshComponent::MeshSubset& subset = mesh->subsets[subsetIndex];
|
||||
if (subset.indexCount == 0)
|
||||
continue;
|
||||
const MaterialComponent* material = scene.materials.GetComponent(subset.materialID);
|
||||
if (material != nullptr && material->GetBlendMode() != wi::enums::BLENDMODE_OPAQUE)
|
||||
continue;
|
||||
const uint32_t indexOffset = subset.indexOffset;
|
||||
const uint32_t triangleCount = subset.indexCount / 3;
|
||||
|
||||
for (uint32_t triangleIndex = 0; (triangleIndex < triangleCount) && !hit; ++triangleIndex)
|
||||
{
|
||||
hit |= intersect_triangle(subsetIndex, indexOffset, triangleIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!hit)
|
||||
accum += 1.0f;
|
||||
}
|
||||
accum /= float(samplecount);
|
||||
objectcomponent->vertex_ao[args.jobIndex] = uint8_t(accum * 255);
|
||||
});
|
||||
wi::jobsystem::Wait(ctx);
|
||||
wi::backlog::post("Vertex AO baking took " + wi::helper::GetTimerDurationText((float)timer.elapsed_seconds()));
|
||||
}
|
||||
objectcomponent->CreateRenderData();
|
||||
}
|
||||
}
|
||||
|
||||
// Delete BVHs that are not really needed any more:
|
||||
for (auto& x : this->editor->translator.selected)
|
||||
{
|
||||
ObjectComponent* objectcomponent = scene.objects.GetComponent(x.entity);
|
||||
if (objectcomponent != nullptr)
|
||||
{
|
||||
const size_t objectcomponentIndex = scene.objects.GetIndex(x.entity);
|
||||
MeshComponent* meshcomponent = scene.meshes.GetComponent(objectcomponent->meshID);
|
||||
if (meshcomponent == nullptr)
|
||||
continue;
|
||||
if (!meshcomponent->IsBVHEnabled())
|
||||
{
|
||||
meshcomponent->bvh = {};
|
||||
meshcomponent->bvh_leaf_aabbs = {};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SetEntity(entity);
|
||||
});
|
||||
AddWidget(&vertexAOButton);
|
||||
|
||||
vertexAORayCountSlider.Create(8, 1024, 256, 1024 - 8, "Ray count: ");
|
||||
vertexAORayCountSlider.SetTooltip("Set the ray count per vertex for vertex AO baking.\nThe larger this value is, the longer the baking will take, and the better the quality will get.");
|
||||
vertexAORayCountSlider.SetSize(XMFLOAT2(wid, hei));
|
||||
vertexAORayCountSlider.SetPos(XMFLOAT2(x, y += step));
|
||||
AddWidget(&vertexAORayCountSlider);
|
||||
|
||||
vertexAORayLengthSlider.Create(0, 1000, 100, 1000, "Ray length: ");
|
||||
vertexAORayLengthSlider.SetTooltip("Set the ray length for vertex AO baking.\nSmaller ray length can reduce ambient occlusion from larger features.");
|
||||
vertexAORayLengthSlider.SetSize(XMFLOAT2(wid, hei));
|
||||
vertexAORayLengthSlider.SetPos(XMFLOAT2(x, y += step));
|
||||
AddWidget(&vertexAORayLengthSlider);
|
||||
|
||||
y += step;
|
||||
|
||||
colorComboBox.Create("Color picker mode: ");
|
||||
@@ -698,6 +908,20 @@ void ObjectWindow::SetEntity(Entity entity)
|
||||
if (object == nullptr)
|
||||
entity = INVALID_ENTITY;
|
||||
|
||||
if (object != nullptr)
|
||||
{
|
||||
if (object->vertex_ao.empty())
|
||||
{
|
||||
vertexAOButton.SetText("Compute Vertex AO");
|
||||
deleteAOMode = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
vertexAOButton.SetText("Delete Vertex AO");
|
||||
deleteAOMode = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (this->entity == entity)
|
||||
return;
|
||||
|
||||
@@ -827,4 +1051,9 @@ void ObjectWindow::ResizeLayout()
|
||||
add(stopLightmapGenButton);
|
||||
add(clearLightmapButton);
|
||||
|
||||
y += jump;
|
||||
add_fullwidth(vertexAOButton);
|
||||
add(vertexAORayCountSlider);
|
||||
add(vertexAORayLengthSlider);
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user