From 1fc2399bf6766ef18c7a97906c9224114e8a19f7 Mon Sep 17 00:00:00 2001 From: Jeremy HU Date: Thu, 3 Nov 2022 23:24:35 +1100 Subject: [PATCH] Add section remesher and cap UV resolving --- application/application.pro | 2 + dust3d/base/combine_mode.cc | 42 ++++++++++++- dust3d/base/combine_mode.h | 41 +------------ dust3d/base/cut_face.cc | 84 +++++++++++++++++++++++-- dust3d/base/cut_face.h | 78 ----------------------- dust3d/base/part_target.cc | 42 ++++++++++++- dust3d/base/part_target.h | 45 +------------- dust3d/base/texture_type.cc | 60 +++++++++++++++++- dust3d/base/texture_type.h | 58 ------------------ dust3d/mesh/section_remesher.cc | 102 +++++++++++++++++++++++++++++++ dust3d/mesh/section_remesher.h | 53 ++++++++++++++++ dust3d/mesh/tube_mesh_builder.cc | 65 ++++++++++++++++---- dust3d/mesh/tube_mesh_builder.h | 1 + 13 files changed, 428 insertions(+), 245 deletions(-) create mode 100644 dust3d/mesh/section_remesher.cc create mode 100644 dust3d/mesh/section_remesher.h diff --git a/application/application.pro b/application/application.pro index 3508c978..8ae07979 100644 --- a/application/application.pro +++ b/application/application.pro @@ -277,6 +277,8 @@ HEADERS += ../dust3d/mesh/rope_mesh.h SOURCES += ../dust3d/mesh/rope_mesh.cc HEADERS += ../dust3d/mesh/section_preview_mesh_builder.h SOURCES += ../dust3d/mesh/section_preview_mesh_builder.cc +HEADERS += ../dust3d/mesh/section_remesher.h +SOURCES += ../dust3d/mesh/section_remesher.cc HEADERS += ../dust3d/mesh/smooth_normal.h SOURCES += ../dust3d/mesh/smooth_normal.cc HEADERS += ../dust3d/mesh/stitch_mesh_builder.h diff --git a/dust3d/base/combine_mode.cc b/dust3d/base/combine_mode.cc index 7cb1ee4f..c6125ee7 100644 --- a/dust3d/base/combine_mode.cc +++ b/dust3d/base/combine_mode.cc @@ -24,8 +24,44 @@ namespace dust3d { -IMPL_CombineModeToString - IMPL_CombineModeFromString - IMPL_CombineModeToDispName +CombineMode CombineModeFromString(const char* modeString) +{ + std::string mode = modeString; + if (mode == "Normal") + return CombineMode::Normal; + if (mode == "Inversion") + return CombineMode::Inversion; + if (mode == "Uncombined") + return CombineMode::Uncombined; + return CombineMode::Normal; +} + +const char* CombineModeToString(CombineMode mode) +{ + switch (mode) { + case CombineMode::Normal: + return "Normal"; + case CombineMode::Inversion: + return "Inversion"; + case CombineMode::Uncombined: + return "Uncombined"; + default: + return "Normal"; + } +} + +std::string CombineModeToDispName(CombineMode mode) +{ + switch (mode) { + case CombineMode::Normal: + return std::string("Normal"); + case CombineMode::Inversion: + return std::string("Inversion"); + case CombineMode::Uncombined: + return std::string("Uncombined"); + default: + return std::string("Normal"); + } +} } diff --git a/dust3d/base/combine_mode.h b/dust3d/base/combine_mode.h index 41342cac..f5a5b4ce 100644 --- a/dust3d/base/combine_mode.h +++ b/dust3d/base/combine_mode.h @@ -33,49 +33,10 @@ enum class CombineMode { Uncombined, Count }; + CombineMode CombineModeFromString(const char* modeString); -#define IMPL_CombineModeFromString \ - CombineMode CombineModeFromString(const char* modeString) \ - { \ - std::string mode = modeString; \ - if (mode == "Normal") \ - return CombineMode::Normal; \ - if (mode == "Inversion") \ - return CombineMode::Inversion; \ - if (mode == "Uncombined") \ - return CombineMode::Uncombined; \ - return CombineMode::Normal; \ - } const char* CombineModeToString(CombineMode mode); -#define IMPL_CombineModeToString \ - const char* CombineModeToString(CombineMode mode) \ - { \ - switch (mode) { \ - case CombineMode::Normal: \ - return "Normal"; \ - case CombineMode::Inversion: \ - return "Inversion"; \ - case CombineMode::Uncombined: \ - return "Uncombined"; \ - default: \ - return "Normal"; \ - } \ - } std::string CombineModeToDispName(CombineMode mode); -#define IMPL_CombineModeToDispName \ - std::string CombineModeToDispName(CombineMode mode) \ - { \ - switch (mode) { \ - case CombineMode::Normal: \ - return std::string("Normal"); \ - case CombineMode::Inversion: \ - return std::string("Inversion"); \ - case CombineMode::Uncombined: \ - return std::string("Uncombined"); \ - default: \ - return std::string("Normal"); \ - } \ - } } diff --git a/dust3d/base/cut_face.cc b/dust3d/base/cut_face.cc index 50d24838..3848f205 100644 --- a/dust3d/base/cut_face.cc +++ b/dust3d/base/cut_face.cc @@ -20,19 +20,91 @@ * SOFTWARE. */ -#include // std::reverse +#include #include #include #include namespace dust3d { -IMPL_CutFaceFromString - IMPL_CutFaceToString - TMPL_CutFaceToPoints +CutFace CutFaceFromString(const char* faceString) +{ + std::string face = faceString; + if (face == "Quad") + return CutFace::Quad; + if (face == "Pentagon") + return CutFace::Pentagon; + if (face == "Hexagon") + return CutFace::Hexagon; + if (face == "Triangle") + return CutFace::Triangle; + if (face == "UserDefined") + return CutFace::UserDefined; + return CutFace::Quad; +} - static void - correctFlippedNormal(std::vector* points) +std::string CutFaceToString(CutFace cutFace) +{ + switch (cutFace) { + case CutFace::Quad: + return "Quad"; + case CutFace::Pentagon: + return "Pentagon"; + case CutFace::Hexagon: + return "Hexagon"; + case CutFace::Triangle: + return "Triangle"; + case CutFace::UserDefined: + return "UserDefined"; + default: + return ""; + } +} + +std::vector CutFaceToPoints(CutFace cutFace) +{ + switch (cutFace) { + case CutFace::Quad: + return { + { (float)-1.0, (float)-1.0 }, + { (float)1.0, (float)-1.0 }, + { (float)1.0, (float)1.0 }, + { (float)-1.0, (float)1.0 }, + }; + case CutFace::Triangle: + return { + { (float)-1.1527, (float)-0.6655 }, + { (float)1.1527, (float)-0.6655 }, + { (float)0.0, (float)1.33447 }, + }; + case CutFace::Pentagon: + return { + { (float)-0.6498, (float)-0.8944 }, + { (float)0.6498, (float)-0.8944 }, + { (float)1.05146, (float)0.34164 }, + { (float)0.0, (float)1.10557 }, + { (float)-1.05146, (float)0.34164 }, + }; + case CutFace::Hexagon: + return { + { (float)-0.577, (float)-1.0 }, + { (float)0.577, (float)-1.0 }, + { (float)1.1547, (float)0.0 }, + { (float)0.577, (float)1.0 }, + { (float)-0.577, (float)1.0 }, + { (float)-1.1547, (float)0.0 }, + }; + default: + return { + { (float)-1.0, (float)-1.0 }, + { (float)1.0, (float)-1.0 }, + { (float)1.0, (float)1.0 }, + { (float)-1.0, (float)1.0 }, + }; + } +} + +static void correctFlippedNormal(std::vector* points) { if (points->size() < 3) return; diff --git a/dust3d/base/cut_face.h b/dust3d/base/cut_face.h index 9ef2ebe5..e6c7d253 100644 --- a/dust3d/base/cut_face.h +++ b/dust3d/base/cut_face.h @@ -40,86 +40,8 @@ enum class CutFace { }; CutFace CutFaceFromString(const char* faceString); -#define IMPL_CutFaceFromString \ - CutFace CutFaceFromString(const char* faceString) \ - { \ - std::string face = faceString; \ - if (face == "Quad") \ - return CutFace::Quad; \ - if (face == "Pentagon") \ - return CutFace::Pentagon; \ - if (face == "Hexagon") \ - return CutFace::Hexagon; \ - if (face == "Triangle") \ - return CutFace::Triangle; \ - if (face == "UserDefined") \ - return CutFace::UserDefined; \ - return CutFace::Quad; \ - } std::string CutFaceToString(CutFace cutFace); -#define IMPL_CutFaceToString \ - std::string CutFaceToString(CutFace cutFace) \ - { \ - switch (cutFace) { \ - case CutFace::Quad: \ - return "Quad"; \ - case CutFace::Pentagon: \ - return "Pentagon"; \ - case CutFace::Hexagon: \ - return "Hexagon"; \ - case CutFace::Triangle: \ - return "Triangle"; \ - case CutFace::UserDefined: \ - return "UserDefined"; \ - default: \ - return ""; \ - } \ - } std::vector CutFaceToPoints(CutFace cutFace); -#define TMPL_CutFaceToPoints \ - std::vector CutFaceToPoints(CutFace cutFace) \ - { \ - switch (cutFace) { \ - case CutFace::Quad: \ - return { \ - { (float)-1.0, (float)-1.0 }, \ - { (float)1.0, (float)-1.0 }, \ - { (float)1.0, (float)1.0 }, \ - { (float)-1.0, (float)1.0 }, \ - }; \ - case CutFace::Triangle: \ - return { \ - { (float)-1.1527, (float)-0.6655 }, \ - { (float)1.1527, (float)-0.6655 }, \ - { (float)0.0, (float)1.33447 }, \ - }; \ - case CutFace::Pentagon: \ - return { \ - { (float)-0.6498, (float)-0.8944 }, \ - { (float)0.6498, (float)-0.8944 }, \ - { (float)1.05146, (float)0.34164 }, \ - { (float)0.0, (float)1.10557 }, \ - { (float)-1.05146, (float)0.34164 }, \ - }; \ - case CutFace::Hexagon: \ - return { \ - { (float)-0.577, (float)-1.0 }, \ - { (float)0.577, (float)-1.0 }, \ - { (float)1.1547, (float)0.0 }, \ - { (float)0.577, (float)1.0 }, \ - { (float)-0.577, (float)1.0 }, \ - { (float)-1.1547, (float)0.0 }, \ - }; \ - default: \ - return { \ - { (float)-1.0, (float)-1.0 }, \ - { (float)1.0, (float)-1.0 }, \ - { (float)1.0, (float)1.0 }, \ - { (float)-1.0, (float)1.0 }, \ - }; \ - } \ - } - void normalizeCutFacePoints(std::vector* points); void cutFacePointsFromNodes(std::vector& points, const std::vector>& nodes, bool isRing = false, std::vector* pointsIds = nullptr); diff --git a/dust3d/base/part_target.cc b/dust3d/base/part_target.cc index 32c02266..a0aeb3a0 100644 --- a/dust3d/base/part_target.cc +++ b/dust3d/base/part_target.cc @@ -24,8 +24,44 @@ namespace dust3d { -IMPL_PartTargetFromString - IMPL_PartTargetToString - IMPL_PartTargetToDispName +PartTarget PartTargetFromString(const char* targetString) +{ + std::string target = targetString; + if (target == "Model") + return PartTarget::Model; + if (target == "CutFace") + return PartTarget::CutFace; + if (target == "StitchingLine") + return PartTarget::StitchingLine; + return PartTarget::Model; +} + +const char* PartTargetToString(PartTarget target) +{ + switch (target) { + case PartTarget::Model: + return "Model"; + case PartTarget::CutFace: + return "CutFace"; + case PartTarget::StitchingLine: + return "StitchingLine"; + default: + return "Model"; + } +} + +std::string PartTargetToDispName(PartTarget target) +{ + switch (target) { + case PartTarget::Model: + return std::string("Model"); + case PartTarget::CutFace: + return std::string("Cut Face"); + case PartTarget::StitchingLine: + return std::string("Stitching Line"); + default: + return std::string("Model"); + } +} } diff --git a/dust3d/base/part_target.h b/dust3d/base/part_target.h index e81435e3..fb9491ba 100644 --- a/dust3d/base/part_target.h +++ b/dust3d/base/part_target.h @@ -30,52 +30,13 @@ namespace dust3d { enum class PartTarget { Model = 0, CutFace, - Count, // FIXME: Enable StitchingLine after the UI is avaliable - StitchingLine + StitchingLine, + Count }; + PartTarget PartTargetFromString(const char* targetString); -#define IMPL_PartTargetFromString \ - PartTarget PartTargetFromString(const char* targetString) \ - { \ - std::string target = targetString; \ - if (target == "Model") \ - return PartTarget::Model; \ - if (target == "CutFace") \ - return PartTarget::CutFace; \ - if (target == "StitchingLine") \ - return PartTarget::StitchingLine; \ - return PartTarget::Model; \ - } const char* PartTargetToString(PartTarget target); -#define IMPL_PartTargetToString \ - const char* PartTargetToString(PartTarget target) \ - { \ - switch (target) { \ - case PartTarget::Model: \ - return "Model"; \ - case PartTarget::CutFace: \ - return "CutFace"; \ - case PartTarget::StitchingLine: \ - return "StitchingLine"; \ - default: \ - return "Model"; \ - } \ - } std::string PartTargetToDispName(PartTarget target); -#define IMPL_PartTargetToDispName \ - std::string PartTargetToDispName(PartTarget target) \ - { \ - switch (target) { \ - case PartTarget::Model: \ - return std::string("Model"); \ - case PartTarget::CutFace: \ - return std::string("Cut Face"); \ - case PartTarget::StitchingLine: \ - return std::string("Stitching Line"); \ - default: \ - return std::string("Model"); \ - } \ - } } diff --git a/dust3d/base/texture_type.cc b/dust3d/base/texture_type.cc index f1cbb7e2..710b1f2d 100644 --- a/dust3d/base/texture_type.cc +++ b/dust3d/base/texture_type.cc @@ -24,8 +24,62 @@ namespace dust3d { -IMPL_TextureTypeToString - IMPL_TextureTypeFromString - IMPL_TextureTypeToDispName +const char* TextureTypeToString(TextureType type) +{ + switch (type) { + case TextureType::BaseColor: + return "BaseColor"; + case TextureType::Normal: + return "Normal"; + case TextureType::Metallic: + return "Metallic"; + case TextureType::Roughness: + return "Roughness"; + case TextureType::AmbientOcclusion: + return "AmbientOcclusion"; + case TextureType::None: + return "None"; + default: + return ""; + } +} + +TextureType TextureTypeFromString(const char* typeString) +{ + std::string type = typeString; + if (type == "BaseColor") + return TextureType::BaseColor; + if (type == "Normal") + return TextureType::Normal; + if (type == "Metallic") + return TextureType::Metallic; + if (type == "Metalness") + return TextureType::Metallic; + if (type == "Roughness") + return TextureType::Roughness; + if (type == "AmbientOcclusion") + return TextureType::AmbientOcclusion; + return TextureType::None; +} + +std::string TextureTypeToDispName(TextureType type) +{ + switch (type) { + case TextureType::BaseColor: + return std::string("Base Color"); + case TextureType::Normal: + return std::string("Normal Map"); + case TextureType::Metallic: + return std::string("Metallic"); + case TextureType::Roughness: + return std::string("Roughness"); + case TextureType::AmbientOcclusion: + return std::string("Ambient Occlusion"); + case TextureType::None: + return std::string("None"); + default: + return ""; + } +} } diff --git a/dust3d/base/texture_type.h b/dust3d/base/texture_type.h index 91128cb6..e976c86a 100644 --- a/dust3d/base/texture_type.h +++ b/dust3d/base/texture_type.h @@ -38,66 +38,8 @@ enum class TextureType { }; const char* TextureTypeToString(TextureType type); -#define IMPL_TextureTypeToString \ - const char* TextureTypeToString(TextureType type) \ - { \ - switch (type) { \ - case TextureType::BaseColor: \ - return "BaseColor"; \ - case TextureType::Normal: \ - return "Normal"; \ - case TextureType::Metallic: \ - return "Metallic"; \ - case TextureType::Roughness: \ - return "Roughness"; \ - case TextureType::AmbientOcclusion: \ - return "AmbientOcclusion"; \ - case TextureType::None: \ - return "None"; \ - default: \ - return ""; \ - } \ - } TextureType TextureTypeFromString(const char* typeString); -#define IMPL_TextureTypeFromString \ - TextureType TextureTypeFromString(const char* typeString) \ - { \ - std::string type = typeString; \ - if (type == "BaseColor") \ - return TextureType::BaseColor; \ - if (type == "Normal") \ - return TextureType::Normal; \ - if (type == "Metallic") \ - return TextureType::Metallic; \ - if (type == "Metalness") \ - return TextureType::Metallic; \ - if (type == "Roughness") \ - return TextureType::Roughness; \ - if (type == "AmbientOcclusion") \ - return TextureType::AmbientOcclusion; \ - return TextureType::None; \ - } std::string TextureTypeToDispName(TextureType type); -#define IMPL_TextureTypeToDispName \ - std::string TextureTypeToDispName(TextureType type) \ - { \ - switch (type) { \ - case TextureType::BaseColor: \ - return std::string("Base Color"); \ - case TextureType::Normal: \ - return std::string("Normal Map"); \ - case TextureType::Metallic: \ - return std::string("Metallic"); \ - case TextureType::Roughness: \ - return std::string("Roughness"); \ - case TextureType::AmbientOcclusion: \ - return std::string("Ambient Occlusion"); \ - case TextureType::None: \ - return std::string("None"); \ - default: \ - return ""; \ - } \ - } } diff --git a/dust3d/mesh/section_remesher.cc b/dust3d/mesh/section_remesher.cc new file mode 100644 index 00000000..f29eb319 --- /dev/null +++ b/dust3d/mesh/section_remesher.cc @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2016-2022 Jeremy HU . All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include + +namespace dust3d { + +SectionRemesher::SectionRemesher(const std::vector& vertices, double ringV, double centerV) + : m_vertices(vertices) + , m_ringV(ringV) + , m_centerV(centerV) +{ +} + +const std::vector& SectionRemesher::generatedVertices() +{ + return m_vertices; +} + +const std::vector>& SectionRemesher::generatedFaces() +{ + return m_generatedFaces; +} + +const std::vector>& SectionRemesher::generatedFaceUvs() +{ + return m_generatedFaceUvs; +} + +bool SectionRemesher::isConvex(const std::vector& vertices) +{ + if (vertices.size() <= 3) + return true; + + Vector3 previousNormal = Vector3::normal(vertices[0], vertices[1], vertices[2]); + for (size_t i = 1; i < vertices.size(); ++i) { + size_t j = (i + 1) % vertices.size(); + size_t k = (i + 2) % vertices.size(); + Vector3 currentNormal = Vector3::normal(vertices[i], vertices[j], vertices[k]); + if (Vector3::dotProduct(previousNormal, currentNormal) < 0) + return false; + previousNormal = currentNormal; + } + + return true; +} + +void SectionRemesher::remesh() +{ + m_ringSize = m_vertices.size(); + + if (isConvex(m_vertices)) { + Vector3 center; + for (const auto& it : m_vertices) + center += it; + center /= m_vertices.size(); + m_vertices.push_back(center); + double offsetU = 0; + std::vector maxUs(m_ringSize + 1); + for (size_t i = 0; i < m_ringSize; ++i) { + size_t j = (i + 1) % m_ringSize; + maxUs[i] = offsetU; + offsetU += (m_vertices[i] - m_vertices[j]).length(); + } + maxUs[m_ringSize] = offsetU; + offsetU = std::max(offsetU, std::numeric_limits::epsilon()); + for (auto& it : maxUs) + it /= offsetU; + for (size_t i = 0; i < m_ringSize; ++i) { + size_t j = (i + 1) % m_ringSize; + m_generatedFaces.emplace_back(std::vector { i, j, m_ringSize }); + m_generatedFaceUvs.emplace_back(std::vector { + Vector2(maxUs[i], m_ringV), + Vector2(maxUs[j], m_ringV), + Vector2((maxUs[i] + maxUs[j]) * 0.5, m_centerV) }); + } + return; + } + + // TODO: Process non convex +} + +} diff --git a/dust3d/mesh/section_remesher.h b/dust3d/mesh/section_remesher.h new file mode 100644 index 00000000..c1e16bc8 --- /dev/null +++ b/dust3d/mesh/section_remesher.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2016-2022 Jeremy HU . All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef DUST3D_MESH_SECTION_REMESHER_H_ +#define DUST3D_MESH_SECTION_REMESHER_H_ + +#include +#include +#include + +namespace dust3d { + +class SectionRemesher { +public: + SectionRemesher(const std::vector& vertices, double ringV, double centerV); + void remesh(); + const std::vector& generatedVertices(); + const std::vector>& generatedFaces(); + const std::vector>& generatedFaceUvs(); + +private: + std::vector> m_generatedFaces; + std::vector> m_generatedFaceUvs; + + size_t m_ringSize = 0; + double m_ringV = 0.0; + double m_centerV = 0.0; + std::vector m_vertices; + bool isConvex(const std::vector& vertices); +}; + +} + +#endif diff --git a/dust3d/mesh/tube_mesh_builder.cc b/dust3d/mesh/tube_mesh_builder.cc index 9c5dbb2f..5efb05e2 100644 --- a/dust3d/mesh/tube_mesh_builder.cc +++ b/dust3d/mesh/tube_mesh_builder.cc @@ -23,6 +23,7 @@ #include #include #include +#include #include namespace dust3d { @@ -195,6 +196,21 @@ void TubeMeshBuilder::build() m_generatedBaseNormal = m_isCircle ? BaseNormal::calculateCircleBaseNormal(m_nodePositions) : BaseNormal::calculateTubeBaseNormal(m_nodePositions); + // Prepare V of UV for cap + double vOffsetBecauseOfFrontCap = 0.0; + double vTubeRatio = 1.0; + if (!m_isCircle) { + double totalTubeLength = 0.0; + for (const auto& it : m_nodeForwardDistances) + totalTubeLength += it; + double totalLength = m_nodes.front().radius + totalTubeLength + m_nodes.back().radius; + vTubeRatio = totalTubeLength / totalLength; + vOffsetBecauseOfFrontCap = m_nodes.front().radius / totalLength; + } + auto tubeUv = [=](const Vector2& uv) { + return Vector2(uv[0], uv[1] * vTubeRatio + vOffsetBecauseOfFrontCap); + }; + // Build all vertex Positions std::vector> cutFaceVertexPositions; for (size_t i = 0; i < m_nodePositions.size(); ++i) { @@ -262,10 +278,10 @@ void TubeMeshBuilder::build() m_generatedFaces.emplace_back(std::vector { cutFaceI[m], cutFaceI[n], cutFaceJ[n], cutFaceJ[m] }); m_generatedFaceUvs.emplace_back(std::vector { - cutFaceVertexUvs[i][m], - cutFaceVertexUvs[i][m + 1], - cutFaceVertexUvs[j][m + 1], - cutFaceVertexUvs[j][m] }); + tubeUv(cutFaceVertexUvs[i][m]), + tubeUv(cutFaceVertexUvs[i][m + 1]), + tubeUv(cutFaceVertexUvs[j][m + 1]), + tubeUv(cutFaceVertexUvs[j][m]) }); } for (size_t m = halfSize; m < cutFaceI.size(); ++m) { size_t n = (m + 1) % cutFaceI.size(); @@ -276,17 +292,42 @@ void TubeMeshBuilder::build() m_generatedFaces.emplace_back(std::vector { cutFaceJ[m], cutFaceI[m], cutFaceI[n], cutFaceJ[n] }); m_generatedFaceUvs.emplace_back(std::vector { - cutFaceVertexUvs[j][m], - cutFaceVertexUvs[i][m], - cutFaceVertexUvs[i][m + 1], - cutFaceVertexUvs[j][m + 1] }); + tubeUv(cutFaceVertexUvs[j][m]), + tubeUv(cutFaceVertexUvs[i][m]), + tubeUv(cutFaceVertexUvs[i][m + 1]), + tubeUv(cutFaceVertexUvs[j][m + 1]) }); } } if (!m_isCircle) { - m_generatedFaces.emplace_back(cutFaceIndices.back()); - m_generatedFaces.emplace_back(cutFaceIndices.front()); - std::reverse(m_generatedFaces.back().begin(), m_generatedFaces.back().end()); - // TODO: Add UV for end cap + addCap(cutFaceIndices.back(), vOffsetBecauseOfFrontCap + vTubeRatio, 1.0); + auto front = cutFaceIndices.front(); + std::reverse(front.begin(), front.end()); + addCap(front, vOffsetBecauseOfFrontCap, 0.0); + } +} + +void TubeMeshBuilder::addCap(const std::vector& section, double ringV, double centerV) +{ + std::vector vertexIndices = section; + std::vector ringVertices(vertexIndices.size()); + for (size_t i = 0; i < ringVertices.size(); ++i) { + ringVertices[i] = m_generatedVertices[vertexIndices[i]]; + } + SectionRemesher sectionRemesher(ringVertices, ringV, centerV); + sectionRemesher.remesh(); + const std::vector& resultVertices = sectionRemesher.generatedVertices(); + for (size_t i = ringVertices.size(); i < resultVertices.size(); ++i) { + vertexIndices.push_back(m_generatedVertices.size()); + m_generatedVertices.push_back(resultVertices[i]); + } + for (const auto& it : sectionRemesher.generatedFaces()) { + std::vector newFace(it.size()); + for (size_t i = 0; i < it.size(); ++i) + newFace[i] = vertexIndices[it[i]]; + m_generatedFaces.emplace_back(newFace); + } + for (const auto& it : sectionRemesher.generatedFaceUvs()) { + m_generatedFaceUvs.emplace_back(it); } } diff --git a/dust3d/mesh/tube_mesh_builder.h b/dust3d/mesh/tube_mesh_builder.h index a9e7fc6a..23cecbce 100644 --- a/dust3d/mesh/tube_mesh_builder.h +++ b/dust3d/mesh/tube_mesh_builder.h @@ -69,6 +69,7 @@ private: const Vector3& forwardDirection); void applyRoundEnd(); void applyInterpolation(); + void addCap(const std::vector& section, double ringV, double centerV); }; };