Vertex AO (#795)

This commit is contained in:
Turánszki János
2024-01-27 13:06:47 +01:00
committed by GitHub
parent 481cc85e3b
commit fcca0dfe37
22 changed files with 475 additions and 18 deletions
+14 -1
View File
@@ -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, 1340));
SetSize(XMFLOAT2(300, 1360));
closeButton.SetTooltip("Delete MaterialComponent");
OnClose([=](wi::gui::EventArgs args) {
@@ -97,6 +97,17 @@ void MaterialWindow::Create(EditorComponent* _editor)
});
AddWidget(&occlusionSecondaryCheckBox);
vertexAOCheckBox.Create("Vertex AO: ");
vertexAOCheckBox.SetTooltip("If enabled, vertex ambient occlusion will be enabled (if it exists)");
vertexAOCheckBox.SetPos(XMFLOAT2(x, y += step));
vertexAOCheckBox.SetSize(XMFLOAT2(hei, hei));
vertexAOCheckBox.OnClick([&](wi::gui::EventArgs args) {
MaterialComponent* material = editor->GetCurrentScene().materials.GetComponent(entity);
if (material != nullptr)
material->SetVertexAODisabled(!args.bValue);
});
AddWidget(&vertexAOCheckBox);
windCheckBox.Create("Wind: ");
windCheckBox.SetTooltip("If enabled, vertex wind weights will affect how much wind offset affects the subset.");
windCheckBox.SetPos(XMFLOAT2(x, y += step));
@@ -772,6 +783,7 @@ void MaterialWindow::SetEntity(Entity entity)
specularGlossinessCheckBox.SetCheck(material->IsUsingSpecularGlossinessWorkflow());
occlusionPrimaryCheckBox.SetCheck(material->IsOcclusionEnabled_Primary());
occlusionSecondaryCheckBox.SetCheck(material->IsOcclusionEnabled_Secondary());
vertexAOCheckBox.SetCheck(!material->IsVertexAODisabled());
windCheckBox.SetCheck(material->IsUsingWind());
doubleSidedCheckBox.SetCheck(material->IsDoubleSided());
outlineCheckBox.SetCheck(material->IsOutlineEnabled());
@@ -942,6 +954,7 @@ void MaterialWindow::ResizeLayout()
add_right(specularGlossinessCheckBox);
add_right(occlusionPrimaryCheckBox);
add_right(occlusionSecondaryCheckBox);
add_right(vertexAOCheckBox);
add_right(windCheckBox);
add_right(doubleSidedCheckBox);
add_right(outlineCheckBox);
+1
View File
@@ -17,6 +17,7 @@ public:
wi::gui::CheckBox specularGlossinessCheckBox;
wi::gui::CheckBox occlusionPrimaryCheckBox;
wi::gui::CheckBox occlusionSecondaryCheckBox;
wi::gui::CheckBox vertexAOCheckBox;
wi::gui::CheckBox windCheckBox;
wi::gui::CheckBox doubleSidedCheckBox;
wi::gui::CheckBox outlineCheckBox;
-1
View File
@@ -649,7 +649,6 @@ void MeshWindow::Create(EditorComponent* _editor)
AddWidget(&exportHeaderButton);
subsetMaterialComboBox.Create("Material: ");
subsetMaterialComboBox.SetSize(XMFLOAT2(wid, hei));
subsetMaterialComboBox.SetPos(XMFLOAT2(x, y += step));
+230 -1
View File
@@ -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);
}
+5
View File
@@ -33,6 +33,11 @@ public:
wi::gui::Button stopLightmapGenButton;
wi::gui::Button clearLightmapButton;
wi::gui::Button vertexAOButton;
bool deleteAOMode = false;
wi::gui::Slider vertexAORayCountSlider;
wi::gui::Slider vertexAORayLengthSlider;
void ResizeLayout() override;
};
@@ -72,6 +72,7 @@ static const uint SHADERMATERIAL_OPTION_BIT_DOUBLE_SIDED = 1 << 7;
static const uint SHADERMATERIAL_OPTION_BIT_TRANSPARENT = 1 << 8;
static const uint SHADERMATERIAL_OPTION_BIT_ADDITIVE = 1 << 9;
static const uint SHADERMATERIAL_OPTION_BIT_UNLIT = 1 << 10;
static const uint SHADERMATERIAL_OPTION_BIT_USE_VERTEXAO = 1 << 11;
// Same as MaterialComponent::TEXTURESLOT
enum TEXTURESLOT
@@ -397,6 +398,7 @@ struct ShaderMaterial
#endif // __cplusplus
inline bool IsUsingVertexColors() { return options & SHADERMATERIAL_OPTION_BIT_USE_VERTEXCOLORS; }
inline bool IsUsingVertexAO() { return options & SHADERMATERIAL_OPTION_BIT_USE_VERTEXAO; }
inline bool IsUsingSpecularGlossinessWorkflow() { return options & SHADERMATERIAL_OPTION_BIT_SPECULARGLOSSINESS_WORKFLOW; }
inline bool IsOcclusionEnabled_Primary() { return options & SHADERMATERIAL_OPTION_BIT_OCCLUSION_PRIMARY; }
inline bool IsOcclusionEnabled_Secondary() { return options & SHADERMATERIAL_OPTION_BIT_OCCLUSION_SECONDARY; }
@@ -477,12 +479,10 @@ struct ShaderGeometry
ib = -1;
vb_pos_wind = -1;
vb_uvs = -1;
vb_nor = -1;
vb_tan = -1;
vb_col = -1;
vb_atl = -1;
vb_pre = -1;
materialIndex = 0;
meshletOffset = 0;
@@ -568,6 +568,11 @@ struct ShaderMeshInstance
float3 center;
float radius;
int vb_ao;
int padding0;
int padding1;
int padding2;
ShaderTransform transform;
ShaderTransform transformInverseTranspose; // This correctly handles non uniform scaling for normals
ShaderTransform transformPrev;
@@ -588,6 +593,7 @@ struct ShaderMeshInstance
fadeDistance = 0;
center = float3(0, 0, 0);
radius = 0;
vb_ao = -1;
transform.init();
transformInverseTranspose.init();
transformPrev.init();
@@ -10,6 +10,7 @@ struct GSInput
min16float4 color : COLOR;
min16float4 tan : TANGENT;
min16float3 nor : NORMAL;
min16float ao : AMBIENT_OCCLUSION;
min16float2 atl : ATLAS;
float3 pos3D : WORLDPOSITION;
uint RTIndex : RTINDEX;
@@ -23,6 +24,7 @@ struct GSOutput
min16float4 color : COLOR;
min16float4 tan : TANGENT;
min16float3 nor : NORMAL;
min16float ao : AMBIENT_OCCLUSION;
min16float2 atl : ATLAS;
float3 pos3D : WORLDPOSITION;
uint RTIndex : SV_RenderTargetArrayIndex;
@@ -43,6 +45,7 @@ void main(
element.uvsets = input[i].uvsets;
element.atl = input[i].atl;
element.nor = input[i].nor;
element.ao = input[i].ao;
element.tan = input[i].tan;
element.pos3D = input[i].pos3D;
element.RTIndex = input[i].RTIndex;
+1
View File
@@ -9,6 +9,7 @@
#define OBJECTSHADER_USE_COLOR
#define OBJECTSHADER_USE_NORMAL
#define OBJECTSHADER_USE_TANGENT
#define OBJECTSHADER_USE_AO
#define OBJECTSHADER_USE_POSITION3D
#define OBJECTSHADER_USE_EMISSIVE
#define OBJECTSHADER_USE_INSTANCEINDEX
+1
View File
@@ -9,6 +9,7 @@
#define OBJECTSHADER_USE_COLOR
#define OBJECTSHADER_USE_NORMAL
#define OBJECTSHADER_USE_TANGENT
#define OBJECTSHADER_USE_AO
#define OBJECTSHADER_USE_POSITION3D
#define OBJECTSHADER_USE_EMISSIVE
#define OBJECTSHADER_USE_INSTANCEINDEX
+33
View File
@@ -76,6 +76,7 @@ inline ShaderMaterial GetMaterial()
//#define OBJECTSHADER_USE_UVSETS - shader will sample textures with uv sets
//#define OBJECTSHADER_USE_ATLAS - shader will use atlas
//#define OBJECTSHADER_USE_NORMAL - shader will use normals
//#define OBJECTSHADER_USE_AO - shader will use ambient occlusion
//#define OBJECTSHADER_USE_TANGENT - shader will use tangents, normal mapping
//#define OBJECTSHADER_USE_POSITION3D - shader will use world space positions
//#define OBJECTSHADER_USE_EMISSIVE - shader will use emissive
@@ -110,6 +111,7 @@ inline ShaderMaterial GetMaterial()
#define OBJECTSHADER_USE_ATLAS
#define OBJECTSHADER_USE_COLOR
#define OBJECTSHADER_USE_NORMAL
#define OBJECTSHADER_USE_AO
#define OBJECTSHADER_USE_TANGENT
#define OBJECTSHADER_USE_POSITION3D
#define OBJECTSHADER_USE_EMISSIVE
@@ -185,6 +187,14 @@ struct VertexInput
inst.init();
return inst;
}
min16float GetVertexAO()
{
[branch]
if (GetInstance().vb_ao < 0)
return 1;
return (min16float)bindless_buffers_float[GetInstance().vb_ao][vertexID];
}
};
@@ -196,6 +206,7 @@ struct VertexSurface
min16float4 color;
min16float3 normal;
min16float4 tangent;
min16float ao;
inline void create(in ShaderMaterial material, in VertexInput input)
{
@@ -211,6 +222,16 @@ struct VertexSurface
color *= input.GetVertexColor();
}
[branch]
if (material.IsUsingVertexAO())
{
ao = input.GetVertexAO();
}
else
{
ao = 1;
}
normal = mul((min16float3x3)input.GetInstance().transformInverseTranspose.GetMatrix(), normal);
tangent = input.GetTangent();
@@ -264,6 +285,10 @@ struct PixelInput
min16float3 nor : NORMAL;
#endif // OBJECTSHADER_USE_NORMAL
#ifdef OBJECTSHADER_USE_AO
min16float ao : AMBIENT_OCCLUSION;
#endif // OBJECTSHADER_USE_AO
#ifdef OBJECTSHADER_USE_ATLAS
min16float2 atl : ATLAS;
#endif // OBJECTSHADER_USE_ATLAS
@@ -366,6 +391,10 @@ PixelInput main(VertexInput input)
Out.nor = surface.normal;
#endif // OBJECTSHADER_USE_NORMAL
#ifdef OBJECTSHADER_USE_AO
Out.ao = surface.ao;
#endif // OBJECTSHADER_USE_AO
#ifdef OBJECTSHADER_USE_TANGENT
Out.tan = surface.tangent;
#endif // OBJECTSHADER_USE_TANGENT
@@ -457,6 +486,10 @@ float4 main(PixelInput input, in bool is_frontface : SV_IsFrontFace) : SV_Target
surface.N = normalize(input.nor);
#endif // OBJECTSHADER_USE_NORMAL
#ifdef OBJECTSHADER_USE_AO
surface.occlusion = input.ao;
#endif // OBJECTSHADER_USE_AO
#ifdef OBJECTSHADER_USE_POSITION3D
surface.P = input.pos3D;
surface.V = GetCamera().position - surface.P;
@@ -134,6 +134,10 @@ PixelInput main(ConstantOutput input, float3 uvw : SV_DomainLocation, const Outp
output.nor = min16float3(normalize(w * patch[0].nor + u * patch[1].nor + v * patch[2].nor));
#endif // OBJECTSHADER_USE_NORMAL
#ifdef OBJECTSHADER_USE_AO
output.ao = min16float(w * patch[0].ao + u * patch[1].ao + v * patch[2].ao);
#endif // OBJECTSHADER_USE_NORMAL
#ifdef OBJECTSHADER_USE_TANGENT
output.tan = min16float4(normalize(w * patch[0].tan + u * patch[1].tan + v * patch[2].tan));
#endif // OBJECTSHADER_USE_TANGENT
+11
View File
@@ -540,6 +540,17 @@ struct Surface
baseColor *= vertexColor;
}
[branch]
if (inst.vb_ao >= 0 && material.IsUsingVertexAO())
{
Buffer<float> buf = bindless_buffers_float[NonUniformResourceIndex(inst.vb_ao)];
const float ao0 = buf[i0];
const float ao1 = buf[i1];
const float ao2 = buf[i2];
float ao = attribute_at_bary(ao0, ao1, ao2, bary);
occlusion = ao;
}
[branch]
if (inst.lightmap >= 0 && geometry.vb_atl >= 0)
{
+33
View File
@@ -140,5 +140,38 @@ namespace wi
Intersects(primitive, node.left + 1, callback);
}
}
// Returning true from callback will immediately exit the whole search
template <typename T>
bool IntersectsFirst(
const T& primitive,
const std::function<bool(uint32_t index)>& callback
) const
{
uint32_t stack[64];
uint32_t count = 0;
stack[count++] = 0; // push node 0
while (count > 0)
{
const uint32_t nodeIndex = stack[--count];
Node& node = nodes[nodeIndex];
if (!node.aabb.intersects(primitive))
continue;
if (node.isLeaf())
{
for (uint32_t i = 0; i < node.count; ++i)
{
if (callback(leaf_indices[node.offset + i]))
return true;
}
}
else
{
stack[count++] = node.left;
stack[count++] = node.left + 1;
}
}
return false;
}
};
}
+23
View File
@@ -1702,4 +1702,27 @@ namespace wi::helper
}
return ss.str();
}
std::string GetTimerDurationText(float timerSeconds)
{
std::stringstream ss;
ss << std::fixed << std::setprecision(1);
if (timerSeconds < 1)
{
ss << timerSeconds * 1000 << " ms";
}
else if (timerSeconds < 60)
{
ss << timerSeconds << " sec";
}
else if (timerSeconds < 60 * 60)
{
ss << timerSeconds * 60 << " min";
}
else
{
ss << timerSeconds * 60 * 60 << " hours";
}
return ss.str();
}
}
+3
View File
@@ -170,4 +170,7 @@ namespace wi::helper
// Returns a good looking memory size string as either bytes, KB, MB or GB
std::string GetMemorySizeText(size_t sizeInBytes);
// Returns a good looking timer duration text as either milliseconds, seconds, minutes or hours
std::string GetTimerDurationText(float timerSeconds);
};
+40
View File
@@ -274,6 +274,46 @@ namespace wi::math
return ++x;
}
// A uniform 2D random generator for hemisphere sampling: http://holger.dammertz.org/stuff/notes_HammersleyOnHemisphere.html
// idx : iteration index
// num : number of iterations in total
constexpr XMFLOAT2 Hammersley2D(uint32_t idx, uint32_t num) {
uint32_t bits = idx;
bits = (bits << 16u) | (bits >> 16u);
bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u);
bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u);
bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u);
bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u);
const float radicalInverse_VdC = float(bits) * 2.3283064365386963e-10f; // / 0x100000000
return XMFLOAT2(float(idx) / float(num), radicalInverse_VdC);
}
inline XMMATRIX GetTangentSpace(const XMFLOAT3& N)
{
// Choose a helper vector for the cross product
XMVECTOR helper = std::abs(N.x) > 0.99 ? XMVectorSet(0, 0, 1, 0) : XMVectorSet(1, 0, 0, 0);
// Generate vectors
XMVECTOR normal = XMLoadFloat3(&N);
XMVECTOR tangent = XMVector3Normalize(XMVector3Cross(normal, helper));
XMVECTOR binormal = XMVector3Normalize(XMVector3Cross(normal, tangent));
return XMMATRIX(tangent, binormal, normal, XMVectorSet(0,0,0,1));
}
inline XMFLOAT3 HemispherePoint_Uniform(float u, float v)
{
float phi = v * 2 * PI;
float cosTheta = 1 - u;
float sinTheta = std::sqrt(1 - cosTheta * cosTheta);
return XMFLOAT3(std::cos(phi) * sinTheta, std::sin(phi) * sinTheta, cosTheta);
}
inline XMFLOAT3 HemispherePoint_Cos(float u, float v)
{
float phi = v * 2 * PI;
float cosTheta = std::sqrt(1 - u);
float sinTheta = std::sqrt(1 - cosTheta * cosTheta);
return XMFLOAT3(std::cos(phi) * sinTheta, std::sin(phi) * sinTheta, cosTheta);
}
// A, B, C: trangle vertices
float TriangleArea(const XMVECTOR& A, const XMVECTOR& B, const XMVECTOR& C);
// a, b, c: trangle side lengths
+4 -9
View File
@@ -3977,6 +3977,7 @@ namespace wi::scene
inst.fadeDistance = object.fadeDistance;
inst.center = object.center;
inst.radius = object.radius;
inst.vb_ao = object.vb_ao_srv;
inst.SetUserStencilRef(object.userStencilRef);
std::memcpy(instanceArrayMapped + args.jobIndex, &inst, sizeof(inst)); // memcpy whole structure into mapped pointer to avoid read from uncached memory
@@ -4854,8 +4855,6 @@ namespace wi::scene
const XMVECTOR rayOrigin_local = XMVector3Transform(rayOrigin, objectMat_Inverse);
const XMVECTOR rayDirection_local = XMVector3Normalize(XMVector3TransformNormal(rayDirection, objectMat_Inverse));
const ArmatureComponent* armature = mesh->IsSkinned() ? armatures.GetComponent(mesh->armatureID) : nullptr;
const XMVECTOR aabb_min = XMLoadFloat3(&mesh->aabb._min);
const XMVECTOR aabb_max = XMLoadFloat3(&mesh->aabb._max);
auto intersect_triangle = [&](uint32_t subsetIndex, uint32_t indexOffset, uint32_t triangleIndex)
{
@@ -4950,7 +4949,7 @@ namespace wi::scene
}
else
{
// Brute-force interection test:
// Brute-force intersection test:
uint32_t first_subset = 0;
uint32_t last_subset = 0;
mesh->GetLODSubsetRange(lod, first_subset, last_subset);
@@ -5050,8 +5049,6 @@ namespace wi::scene
const XMMATRIX objectMatPrev = XMLoadFloat4x4(&matrix_objects_prev[objectIndex]);
const XMMATRIX objectMatInverse = XMMatrixInverse(nullptr, objectMat);
const ArmatureComponent* armature = mesh->IsSkinned() ? armatures.GetComponent(mesh->armatureID) : nullptr;
const XMVECTOR aabb_min = XMLoadFloat3(&mesh->aabb._min);
const XMVECTOR aabb_max = XMLoadFloat3(&mesh->aabb._max);
auto intersect_triangle = [&](uint32_t subsetIndex, uint32_t indexOffset, uint32_t triangleIndex)
{
@@ -5227,7 +5224,7 @@ namespace wi::scene
}
else
{
// Brute-force interection test:
// Brute-force intersection test:
uint32_t first_subset = 0;
uint32_t last_subset = 0;
mesh->GetLODSubsetRange(lod, first_subset, last_subset);
@@ -5334,8 +5331,6 @@ namespace wi::scene
const XMMATRIX objectMatPrev = XMLoadFloat4x4(&matrix_objects_prev[objectIndex]);
const ArmatureComponent* armature = mesh->IsSkinned() ? armatures.GetComponent(mesh->armatureID) : nullptr;
const XMMATRIX objectMat_Inverse = XMMatrixInverse(nullptr, objectMat);
const XMVECTOR aabb_min = XMLoadFloat3(&mesh->aabb._min);
const XMVECTOR aabb_max = XMLoadFloat3(&mesh->aabb._max);
auto intersect_triangle = [&](uint32_t subsetIndex, uint32_t indexOffset, uint32_t triangleIndex)
{
@@ -5625,7 +5620,7 @@ namespace wi::scene
}
else
{
// Brute-force interection test:
// Brute-force intersection test:
uint32_t first_subset = 0;
uint32_t last_subset = 0;
mesh->GetLODSubsetRange(lod, first_subset, last_subset);
+1 -1
View File
@@ -33,7 +33,7 @@ namespace wi::scene
wi::ecs::ComponentManager<MaterialComponent>& materials = componentLibrary.Register<MaterialComponent>("wi::scene::Scene::materials", 2); // version = 2
wi::ecs::ComponentManager<MeshComponent>& meshes = componentLibrary.Register<MeshComponent>("wi::scene::Scene::meshes", 2); // version = 2
wi::ecs::ComponentManager<ImpostorComponent>& impostors = componentLibrary.Register<ImpostorComponent>("wi::scene::Scene::impostors");
wi::ecs::ComponentManager<ObjectComponent>& objects = componentLibrary.Register<ObjectComponent>("wi::scene::Scene::objects", 2); // version = 2
wi::ecs::ComponentManager<ObjectComponent>& objects = componentLibrary.Register<ObjectComponent>("wi::scene::Scene::objects", 3); // version = 3
wi::ecs::ComponentManager<RigidBodyPhysicsComponent>& rigidbodies = componentLibrary.Register<RigidBodyPhysicsComponent>("wi::scene::Scene::rigidbodies", 1); // version = 1
wi::ecs::ComponentManager<SoftBodyPhysicsComponent>& softbodies = componentLibrary.Register<SoftBodyPhysicsComponent>("wi::scene::Scene::softbodies");
wi::ecs::ComponentManager<ArmatureComponent>& armatures = componentLibrary.Register<ArmatureComponent>("wi::scene::Scene::armatures");
+33 -1
View File
@@ -330,6 +330,10 @@ namespace wi::scene
{
material.options |= SHADERMATERIAL_OPTION_BIT_UNLIT;
}
if (!IsVertexAODisabled())
{
material.options |= SHADERMATERIAL_OPTION_BIT_USE_VERTEXAO;
}
GraphicsDevice* device = wi::graphics::GetDevice();
for (int i = 0; i < TEXTURESLOT_COUNT; ++i)
@@ -1599,7 +1603,6 @@ namespace wi::scene
lightmapTextureData.clear();
SetLightmapRenderRequest(false);
}
void ObjectComponent::SaveLightmap()
{
if (lightmap.IsValid() && has_flag(lightmap.desc.bind_flags, BindFlag::RENDER_TARGET))
@@ -1702,6 +1705,35 @@ namespace wi::scene
lightmap.desc = desc;
}
}
void ObjectComponent::DeleteRenderData()
{
vb_ao = {};
vb_ao_srv = -1;
}
void ObjectComponent::CreateRenderData()
{
DeleteRenderData();
GraphicsDevice* device = wi::graphics::GetDevice();
if (!vertex_ao.empty())
{
GPUBufferDesc desc;
desc.bind_flags = BindFlag::SHADER_RESOURCE;
desc.size = sizeof(Vertex_AO) * vertex_ao.size();
desc.format = Vertex_AO::FORMAT;
auto fill_ao = [&](void* data) {
std::memcpy(data, vertex_ao.data(), vertex_ao.size());
};
bool success = device->CreateBuffer2(&desc, fill_ao, &vb_ao);
assert(success);
device->SetName(&vb_ao, "ObjectComponent::vb_ao");
vb_ao_srv = device->GetDescriptorIndex(&vb_ao, SubresourceType::SRV);
}
}
void EnvironmentProbeComponent::CreateRenderData()
{
+14
View File
@@ -122,6 +122,7 @@ namespace wi::scene
DOUBLE_SIDED = 1 << 11,
OUTLINE = 1 << 12,
PREFER_UNCOMPRESSED_TEXTURES = 1 << 13,
DISABLE_VERTEXAO = 1 << 14,
};
uint32_t _flags = CAST_SHADOW;
@@ -267,6 +268,7 @@ namespace wi::scene
inline bool IsDoubleSided() const { return _flags & DOUBLE_SIDED; }
inline bool IsOutlineEnabled() const { return _flags & OUTLINE; }
inline bool IsPreferUncompressedTexturesEnabled() const { return _flags & PREFER_UNCOMPRESSED_TEXTURES; }
inline bool IsVertexAODisabled() const { return _flags & DISABLE_VERTEXAO; }
inline void SetBaseColor(const XMFLOAT4& value) { SetDirty(); baseColor = value; }
inline void SetSpecularColor(const XMFLOAT4& value) { SetDirty(); specularColor = value; }
@@ -306,6 +308,7 @@ namespace wi::scene
inline void SetDoubleSided(bool value = true) { if (value) { _flags |= DOUBLE_SIDED; } else { _flags &= ~DOUBLE_SIDED; } }
inline void SetOutlineEnabled(bool value = true) { if (value) { _flags |= OUTLINE; } else { _flags &= ~OUTLINE; } }
inline void SetPreferUncompressedTexturesEnabled(bool value = true) { if (value) { _flags |= PREFER_UNCOMPRESSED_TEXTURES; } else { _flags &= ~PREFER_UNCOMPRESSED_TEXTURES; } CreateRenderData(true); }
inline void SetVertexAODisabled(bool value = true) { if (value) { _flags |= DISABLE_VERTEXAO; } else { _flags &= ~DISABLE_VERTEXAO; } }
// The MaterialComponent will be written to ShaderMaterial (a struct that is optimized for GPU use)
void WriteShaderMaterial(ShaderMaterial* dest) const;
@@ -809,12 +812,15 @@ namespace wi::scene
uint32_t lightmapHeight = 0;
wi::vector<uint8_t> lightmapTextureData;
uint32_t sort_priority = 0; // increase to draw earlier (currently 4 bits will be used)
wi::vector<uint8_t> vertex_ao;
// Non-serialized attributes:
uint32_t filterMaskDynamic = 0;
wi::graphics::Texture lightmap;
mutable uint32_t lightmapIterationCount = 0;
wi::graphics::GPUBuffer vb_ao;
int vb_ao_srv = -1;
XMFLOAT3 center = XMFLOAT3(0, 0, 0);
float radius = 0;
@@ -865,6 +871,14 @@ namespace wi::scene
void CompressLightmap(); // not thread safe if LIGHTMAP_BLOCK_COMPRESSION is enabled!
void Serialize(wi::Archive& archive, wi::ecs::EntitySerializer& seri);
void CreateRenderData();
void DeleteRenderData();
struct Vertex_AO
{
uint8_t value = 0;
static constexpr wi::graphics::Format FORMAT = wi::graphics::Format::R8_UNORM;
};
};
struct RigidBodyPhysicsComponent
+12 -1
View File
@@ -373,7 +373,6 @@ namespace wi::scene
}
void MeshComponent::Serialize(wi::Archive& archive, EntitySerializer& seri)
{
if (archive.IsReadMode())
{
archive >> _flags;
@@ -594,6 +593,14 @@ namespace wi::scene
{
archive >> sort_priority;
}
if (seri.GetVersion() >= 3)
{
archive >> vertex_ao;
}
wi::jobsystem::Execute(seri.ctx, [&](wi::jobsystem::JobArgs args) {
CreateRenderData();
});
}
else
{
@@ -632,6 +639,10 @@ namespace wi::scene
{
archive << sort_priority;
}
if (seri.GetVersion() >= 3)
{
archive << vertex_ao;
}
}
}
void RigidBodyPhysicsComponent::Serialize(wi::Archive& archive, EntitySerializer& seri)
+1 -1
View File
@@ -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 = 362;
const int revision = 363;
const std::string version_string = std::to_string(major) + "." + std::to_string(minor) + "." + std::to_string(revision);