fix recompute smooth normals, system update
This commit is contained in:
+12
-12
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user