fix recompute smooth normals, system update

This commit is contained in:
turanszkij
2018-03-29 15:08:42 +01:00
parent 54c20de3f8
commit 02f681d92d
5 changed files with 187 additions and 68 deletions
+12 -12
View File
@@ -20,9 +20,10 @@ MeshWindow::MeshWindow(wiGUI* gui) : GUI(gui)
float x = 200;
float y = 0;
float step = 35;
meshInfoLabel = new wiLabel("Mesh Info");
meshInfoLabel->SetPos(XMFLOAT2(x, y += 30));
meshInfoLabel->SetPos(XMFLOAT2(x, y += step));
meshInfoLabel->SetSize(XMFLOAT2(400, 150));
meshWindow->AddWidget(meshInfoLabel);
@@ -30,7 +31,7 @@ MeshWindow::MeshWindow(wiGUI* gui) : GUI(gui)
doubleSidedCheckBox = new wiCheckBox("Double Sided: ");
doubleSidedCheckBox->SetTooltip("If enabled, the inside of the mesh will be visible.");
doubleSidedCheckBox->SetPos(XMFLOAT2(x, y += 30));
doubleSidedCheckBox->SetPos(XMFLOAT2(x, y += step));
doubleSidedCheckBox->OnClick([&](wiEventArgs args) {
if (mesh != nullptr)
{
@@ -42,7 +43,7 @@ MeshWindow::MeshWindow(wiGUI* gui) : GUI(gui)
massSlider = new wiSlider(0, 5000, 0, 100000, "Mass: ");
massSlider->SetTooltip("Set the mass amount for the physics engine.");
massSlider->SetSize(XMFLOAT2(100, 30));
massSlider->SetPos(XMFLOAT2(x, y += 30));
massSlider->SetPos(XMFLOAT2(x, y += step));
massSlider->OnSlide([&](wiEventArgs args) {
if (mesh != nullptr)
{
@@ -54,7 +55,7 @@ MeshWindow::MeshWindow(wiGUI* gui) : GUI(gui)
frictionSlider = new wiSlider(0, 5000, 0, 100000, "Friction: ");
frictionSlider->SetTooltip("Set the friction amount for the physics engine.");
frictionSlider->SetSize(XMFLOAT2(100, 30));
frictionSlider->SetPos(XMFLOAT2(x, y += 30));
frictionSlider->SetPos(XMFLOAT2(x, y += step));
frictionSlider->OnSlide([&](wiEventArgs args) {
if (mesh != nullptr)
{
@@ -66,7 +67,7 @@ MeshWindow::MeshWindow(wiGUI* gui) : GUI(gui)
impostorCreateButton = new wiButton("Create Impostor");
impostorCreateButton->SetTooltip("Create an impostor image of the mesh. The mesh will be replaced by this image when far away, to render faster.");
impostorCreateButton->SetSize(XMFLOAT2(240, 30));
impostorCreateButton->SetPos(XMFLOAT2(x - 50, y += 30));
impostorCreateButton->SetPos(XMFLOAT2(x - 50, y += step));
impostorCreateButton->OnClick([&](wiEventArgs args) {
if (mesh != nullptr)
{
@@ -78,7 +79,7 @@ MeshWindow::MeshWindow(wiGUI* gui) : GUI(gui)
impostorDistanceSlider = new wiSlider(0, 1000, 100, 10000, "Impostor Distance: ");
impostorDistanceSlider->SetTooltip("Assign the distance where the mesh geometry should be switched to the impostor image.");
impostorDistanceSlider->SetSize(XMFLOAT2(100, 30));
impostorDistanceSlider->SetPos(XMFLOAT2(x, y += 30));
impostorDistanceSlider->SetPos(XMFLOAT2(x, y += step));
impostorDistanceSlider->OnSlide([&](wiEventArgs args) {
if (mesh != nullptr)
{
@@ -90,7 +91,7 @@ MeshWindow::MeshWindow(wiGUI* gui) : GUI(gui)
tessellationFactorSlider = new wiSlider(0, 16, 0, 10000, "Tessellation Factor: ");
tessellationFactorSlider->SetTooltip("Set the dynamic tessellation amount. Tessellation should be enabled in the Renderer window and your GPU must support it!");
tessellationFactorSlider->SetSize(XMFLOAT2(100, 30));
tessellationFactorSlider->SetPos(XMFLOAT2(x, y += 30));
tessellationFactorSlider->SetPos(XMFLOAT2(x, y += step));
tessellationFactorSlider->OnSlide([&](wiEventArgs args) {
if (mesh != nullptr)
{
@@ -102,11 +103,12 @@ MeshWindow::MeshWindow(wiGUI* gui) : GUI(gui)
computeNormalsSmoothButton = new wiButton("Compute Normals [SMOOTH]");
computeNormalsSmoothButton->SetTooltip("Compute surface normals of the mesh. Resulting normals will be unique per vertex.");
computeNormalsSmoothButton->SetSize(XMFLOAT2(240, 30));
computeNormalsSmoothButton->SetPos(XMFLOAT2(x - 50, y += 30));
computeNormalsSmoothButton->SetPos(XMFLOAT2(x - 50, y += step));
computeNormalsSmoothButton->OnClick([&](wiEventArgs args) {
if (mesh != nullptr)
{
mesh->ComputeNormals(true);
SetMesh(mesh);
}
});
meshWindow->AddWidget(computeNormalsSmoothButton);
@@ -114,11 +116,12 @@ MeshWindow::MeshWindow(wiGUI* gui) : GUI(gui)
computeNormalsHardButton = new wiButton("Compute Normals [HARD]");
computeNormalsHardButton->SetTooltip("Compute surface normals of the mesh. Resulting normals will be unique per face.");
computeNormalsHardButton->SetSize(XMFLOAT2(240, 30));
computeNormalsHardButton->SetPos(XMFLOAT2(x - 50, y += 30));
computeNormalsHardButton->SetPos(XMFLOAT2(x - 50, y += step));
computeNormalsHardButton->OnClick([&](wiEventArgs args) {
if (mesh != nullptr)
{
mesh->ComputeNormals(false);
SetMesh(mesh);
}
});
meshWindow->AddWidget(computeNormalsHardButton);
@@ -142,9 +145,6 @@ MeshWindow::~MeshWindow()
void MeshWindow::SetMesh(Mesh* mesh)
{
if (this->mesh == mesh)
return;
this->mesh = mesh;
if (mesh != nullptr)
{
+149 -55
View File
@@ -1864,24 +1864,6 @@ void Mesh::CreateBuffers()
wiRenderer::GetDevice()->CreateBuffer(&bd, &InitData, &vertexBuffer_TEX);
//PHYSICALMAPPING
if (!physicsverts.empty() && physicalmapGP.empty())
{
for (unsigned int i = 0; i < vertices_POS.size(); ++i) {
for (unsigned int j = 0; j < physicsverts.size(); ++j) {
if (fabs(vertices_POS[i].pos.x - physicsverts[j].x) < FLT_EPSILON
&& fabs(vertices_POS[i].pos.y - physicsverts[j].y) < FLT_EPSILON
&& fabs(vertices_POS[i].pos.z - physicsverts[j].z) < FLT_EPSILON
)
{
physicalmapGP.push_back(j);
break;
}
}
}
}
// Remap index buffer to be continuous across subsets and create gpu buffer data:
uint32_t counter = 0;
uint8_t stride;
@@ -2132,6 +2114,7 @@ void Mesh::CreateVertexArrays()
return;
}
// We can call this function anytime to recreate data, so clean up first:
vertices_POS.clear();
vertices_TEX.clear();
vertices_BON.clear();
@@ -2139,7 +2122,7 @@ void Mesh::CreateVertexArrays()
// De-interleave vertex arrays:
vertices_POS.resize(vertices_FULL.size());
vertices_TEX.resize(vertices_FULL.size());
// do not resize vertices_BON just yet!!
// do not resize vertices_BON just yet, not every mesh will need bone vertex data!
for (size_t i = 0; i < vertices_FULL.size(); ++i)
{
// Normalize normals:
@@ -2161,6 +2144,7 @@ void Mesh::CreateVertexArrays()
if (vertices_BON.empty())
{
// Allocate full bone vertex data when we find a correct bone weight.
vertices_BON.resize(vertices_FULL.size());
}
vertices_BON[i] = Vertex_BON(vertices_FULL[i]);
@@ -2173,7 +2157,7 @@ void Mesh::CreateVertexArrays()
// Save original vertices. This will be input for CPU skinning / soft bodies
vertices_Transformed_POS = vertices_POS;
vertices_Transformed_PRE = vertices_POS; // pre <- pos!!
vertices_Transformed_PRE = vertices_POS; // pre <- pos!! (previous positions will have the current positions initially)
// Map subset indices:
for (auto& subset : subsets)
@@ -2197,6 +2181,8 @@ void Mesh::CreateVertexArrays()
}
}
// Goal positions, normals are controlling blending between animation and physics states for soft body rendering:
goalPositions.clear();
goalNormals.clear();
if (goalVG >= 0)
@@ -2205,49 +2191,159 @@ void Mesh::CreateVertexArrays()
goalNormals.resize(vertexGroups[goalVG].vertices.size());
}
// Mapping render vertices to physics vertex representation:
// the physics vertices contain unique position, not duplicated by texcoord or normals
// this way we can map several renderable vertices to one physics vertex
// but the mapping function will actually be indexed by renderable vertex index for efficient retrieval.
if (!physicsverts.empty() && physicalmapGP.empty())
{
for (size_t i = 0; i < vertices_POS.size(); ++i)
{
for (size_t j = 0; j < physicsverts.size(); ++j)
{
if (fabs(vertices_POS[i].pos.x - physicsverts[j].x) < FLT_EPSILON
&& fabs(vertices_POS[i].pos.y - physicsverts[j].y) < FLT_EPSILON
&& fabs(vertices_POS[i].pos.z - physicsverts[j].z) < FLT_EPSILON
)
{
physicalmapGP.push_back(static_cast<int>(j));
break;
}
}
}
}
arraysComplete = true;
}
void Mesh::ComputeNormals(bool smooth)
{
// Start recalculating normals:
vector<uint32_t> newIndexBuffer;
vector<Vertex_FULL> newVertexBuffer;
for (size_t face = 0; face < indices.size() / 3; face++)
if (smooth)
{
uint32_t i0 = indices[face * 3 + 0];
uint32_t i1 = indices[face * 3 + 1];
uint32_t i2 = indices[face * 3 + 2];
// Compute smooth surface normals:
Vertex_FULL& v0 = vertices_FULL[i0];
Vertex_FULL& v1 = vertices_FULL[i1];
Vertex_FULL& v2 = vertices_FULL[i2];
XMVECTOR U = XMLoadFloat4(&v2.pos) - XMLoadFloat4(&v0.pos);
XMVECTOR V = XMLoadFloat4(&v1.pos) - XMLoadFloat4(&v0.pos);
XMVECTOR N = XMVector3Cross(U, V);
N = XMVector3Normalize(N);
XMFLOAT4 normal;
XMStoreFloat4(&normal, N);
if (smooth)
// 1.) Zero normals, they will be averaged later
for (size_t i = 0; i < vertices_FULL.size() - 1; i++)
{
v0.nor.x += normal.x;
v0.nor.y += normal.y;
v0.nor.z += normal.z;
v1.nor.x += normal.x;
v1.nor.y += normal.y;
v1.nor.z += normal.z;
v2.nor.x += normal.x;
v2.nor.y += normal.y;
v2.nor.z += normal.z;
vertices_FULL[i].nor = XMFLOAT4(0, 0, 0, 0);
}
else
// 2.) Find identical vertices by POSITION, accumulate face normals
for (size_t i = 0; i < vertices_FULL.size() - 1; i++)
{
Vertex_FULL& v_search = vertices_FULL[i];
for (size_t ind = 0; ind < indices.size() / 3; ++ind)
{
uint32_t i0 = indices[ind * 3 + 0];
uint32_t i1 = indices[ind * 3 + 1];
uint32_t i2 = indices[ind * 3 + 2];
Vertex_FULL& v0 = vertices_FULL[i0];
Vertex_FULL& v1 = vertices_FULL[i1];
Vertex_FULL& v2 = vertices_FULL[i2];
bool match_pos0 =
fabs(v_search.pos.x - v0.pos.x) < FLT_EPSILON &&
fabs(v_search.pos.y - v0.pos.y) < FLT_EPSILON &&
fabs(v_search.pos.z - v0.pos.z) < FLT_EPSILON;
bool match_pos1 =
fabs(v_search.pos.x - v1.pos.x) < FLT_EPSILON &&
fabs(v_search.pos.y - v1.pos.y) < FLT_EPSILON &&
fabs(v_search.pos.z - v1.pos.z) < FLT_EPSILON;
bool match_pos2 =
fabs(v_search.pos.x - v2.pos.x) < FLT_EPSILON &&
fabs(v_search.pos.y - v2.pos.y) < FLT_EPSILON &&
fabs(v_search.pos.z - v2.pos.z) < FLT_EPSILON;
if (match_pos0 || match_pos1 || match_pos2)
{
XMVECTOR U = XMLoadFloat4(&v2.pos) - XMLoadFloat4(&v0.pos);
XMVECTOR V = XMLoadFloat4(&v1.pos) - XMLoadFloat4(&v0.pos);
XMVECTOR N = XMVector3Cross(U, V);
N = XMVector3Normalize(N);
XMFLOAT3 normal;
XMStoreFloat3(&normal, N);
v_search.nor.x += normal.x;
v_search.nor.y += normal.y;
v_search.nor.z += normal.z;
}
}
}
// 3.) Find unique vertices by POSITION and TEXCOORD and MATERIAL and remove duplicates
for (size_t i = 0; i < vertices_FULL.size() - 1; i++)
{
const Vertex_FULL& v0 = vertices_FULL[i];
for (size_t j = i + 1; j < vertices_FULL.size(); j++)
{
const Vertex_FULL& v1 = vertices_FULL[j];
bool unique_pos =
fabs(v0.pos.x - v1.pos.x) < FLT_EPSILON &&
fabs(v0.pos.y - v1.pos.y) < FLT_EPSILON &&
fabs(v0.pos.z - v1.pos.z) < FLT_EPSILON;
bool unique_tex =
fabs(v0.tex.x - v1.tex.x) < FLT_EPSILON &&
fabs(v0.tex.y - v1.tex.y) < FLT_EPSILON &&
(int)v0.tex.z == (int)v1.tex.z;
if (unique_pos && unique_tex)
{
for (size_t ind = 0; ind < indices.size(); ++ind)
{
if (indices[ind] == j)
{
indices[ind] = static_cast<uint32_t>(i);
}
else if (indices[ind] > j && indices[ind] > 0)
{
indices[ind]--;
}
}
vertices_FULL.erase(vertices_FULL.begin() + j);
}
}
}
}
else
{
// Compute hard surface normals:
vector<uint32_t> newIndexBuffer;
vector<Vertex_FULL> newVertexBuffer;
for (size_t face = 0; face < indices.size() / 3; face++)
{
uint32_t i0 = indices[face * 3 + 0];
uint32_t i1 = indices[face * 3 + 1];
uint32_t i2 = indices[face * 3 + 2];
Vertex_FULL& v0 = vertices_FULL[i0];
Vertex_FULL& v1 = vertices_FULL[i1];
Vertex_FULL& v2 = vertices_FULL[i2];
XMVECTOR U = XMLoadFloat4(&v2.pos) - XMLoadFloat4(&v0.pos);
XMVECTOR V = XMLoadFloat4(&v1.pos) - XMLoadFloat4(&v0.pos);
XMVECTOR N = XMVector3Cross(U, V);
N = XMVector3Normalize(N);
XMFLOAT3 normal;
XMStoreFloat3(&normal, N);
v0.nor.x = normal.x;
v0.nor.y = normal.y;
v0.nor.z = normal.z;
@@ -2268,10 +2364,8 @@ void Mesh::ComputeNormals(bool smooth)
newIndexBuffer.push_back(static_cast<uint32_t>(newIndexBuffer.size()));
newIndexBuffer.push_back(static_cast<uint32_t>(newIndexBuffer.size()));
}
}
if (!smooth)
{
// For hard surface normals, we created a new mesh in the previous loop through faces, so swap data:
vertices_FULL = newVertexBuffer;
indices = newIndexBuffer;
}
+20
View File
@@ -90,6 +90,26 @@ namespace wiMath
return ++x;
}
float TriangleArea(const XMVECTOR& A, const XMVECTOR& B, const XMVECTOR& C)
{
// Heron's formula:
XMVECTOR a = XMVector3Length(B - A);
XMVECTOR b = XMVector3Length(C - A);
XMVECTOR c = XMVector3Length(C - B);
XMVECTOR p = (a + b + c) * 0.5f;
XMVECTOR areaSq = p * (p - a) * (p - b) * (p - c);
float area;
XMStoreFloat(&area, areaSq);
area = sqrtf(area);
return area;
}
float TriangleArea(float a, float b, float c)
{
// Heron's formula:
float p = (a + b + c) * 0.5f;
return sqrtf(p * (p - a) * (p - b) * (p - c));
}
float InverseLerp(float value1, float value2, float pos)
{
+5
View File
@@ -27,6 +27,11 @@ namespace wiMath
UINT GetNextPowerOfTwo(UINT x);
float SmoothStep(float value1, float value2, float amount);
// A, B, C: trangle vertices
float TriangleArea(const XMVECTOR& A, const XMVECTOR& B, const XMVECTOR& C);
// a, b, c: trangle side lengths
float TriangleArea(float a, float b, float c);
XMFLOAT3 getCubicHermiteSplinePos(const XMFLOAT3& startPos, const XMFLOAT3& endPos
, const XMFLOAT3& startTangent, const XMFLOAT3& endTangent
, float atInterval);
+1 -1
View File
@@ -9,7 +9,7 @@ namespace wiVersion
// minor features, major updates
const int minor = 16;
// minor bug fixes, alterations, refactors, updates
const int revision = 32;
const int revision = 33;
long GetVersion()