Add section remesher and cap UV resolving

master
Jeremy HU 2022-11-03 23:24:35 +11:00
parent 7c9195722c
commit 1fc2399bf6
13 changed files with 428 additions and 245 deletions

View File

@ -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

View File

@ -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");
}
}
}

View File

@ -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"); \
} \
}
}

View File

@ -20,19 +20,91 @@
* SOFTWARE.
*/
#include <algorithm> // std::reverse
#include <algorithm>
#include <dust3d/base/cut_face.h>
#include <dust3d/base/vector2.h>
#include <dust3d/base/vector3.h>
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<Vector2>* 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<Vector2> 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<Vector2>* points)
{
if (points->size() < 3)
return;

View File

@ -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<Vector2> CutFaceToPoints(CutFace cutFace);
#define TMPL_CutFaceToPoints \
std::vector<Vector2> 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<Vector2>* points);
void cutFacePointsFromNodes(std::vector<Vector2>& points, const std::vector<std::tuple<float, float, float, std::string>>& nodes, bool isRing = false,
std::vector<std::string>* pointsIds = nullptr);

View File

@ -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");
}
}
}

View File

@ -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"); \
} \
}
}

View File

@ -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 "";
}
}
}

View File

@ -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 ""; \
} \
}
}

View File

@ -0,0 +1,102 @@
/*
* Copyright (c) 2016-2022 Jeremy HU <jeremy-at-dust3d dot org>. 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 <dust3d/mesh/section_remesher.h>
namespace dust3d {
SectionRemesher::SectionRemesher(const std::vector<Vector3>& vertices, double ringV, double centerV)
: m_vertices(vertices)
, m_ringV(ringV)
, m_centerV(centerV)
{
}
const std::vector<Vector3>& SectionRemesher::generatedVertices()
{
return m_vertices;
}
const std::vector<std::vector<size_t>>& SectionRemesher::generatedFaces()
{
return m_generatedFaces;
}
const std::vector<std::vector<Vector2>>& SectionRemesher::generatedFaceUvs()
{
return m_generatedFaceUvs;
}
bool SectionRemesher::isConvex(const std::vector<Vector3>& 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<double> 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<double>::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<size_t> { i, j, m_ringSize });
m_generatedFaceUvs.emplace_back(std::vector<Vector2> {
Vector2(maxUs[i], m_ringV),
Vector2(maxUs[j], m_ringV),
Vector2((maxUs[i] + maxUs[j]) * 0.5, m_centerV) });
}
return;
}
// TODO: Process non convex
}
}

View File

@ -0,0 +1,53 @@
/*
* Copyright (c) 2016-2022 Jeremy HU <jeremy-at-dust3d dot org>. 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 <dust3d/base/vector2.h>
#include <dust3d/base/vector3.h>
#include <vector>
namespace dust3d {
class SectionRemesher {
public:
SectionRemesher(const std::vector<Vector3>& vertices, double ringV, double centerV);
void remesh();
const std::vector<Vector3>& generatedVertices();
const std::vector<std::vector<size_t>>& generatedFaces();
const std::vector<std::vector<Vector2>>& generatedFaceUvs();
private:
std::vector<std::vector<size_t>> m_generatedFaces;
std::vector<std::vector<Vector2>> m_generatedFaceUvs;
size_t m_ringSize = 0;
double m_ringV = 0.0;
double m_centerV = 0.0;
std::vector<Vector3> m_vertices;
bool isConvex(const std::vector<Vector3>& vertices);
};
}
#endif

View File

@ -23,6 +23,7 @@
#include <algorithm>
#include <dust3d/base/debug.h>
#include <dust3d/mesh/base_normal.h>
#include <dust3d/mesh/section_remesher.h>
#include <dust3d/mesh/tube_mesh_builder.h>
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<std::vector<Vector3>> cutFaceVertexPositions;
for (size_t i = 0; i < m_nodePositions.size(); ++i) {
@ -262,10 +278,10 @@ void TubeMeshBuilder::build()
m_generatedFaces.emplace_back(std::vector<size_t> {
cutFaceI[m], cutFaceI[n], cutFaceJ[n], cutFaceJ[m] });
m_generatedFaceUvs.emplace_back(std::vector<Vector2> {
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<size_t> {
cutFaceJ[m], cutFaceI[m], cutFaceI[n], cutFaceJ[n] });
m_generatedFaceUvs.emplace_back(std::vector<Vector2> {
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<size_t>& section, double ringV, double centerV)
{
std::vector<size_t> vertexIndices = section;
std::vector<Vector3> 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<Vector3>& 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<size_t> 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);
}
}

View File

@ -69,6 +69,7 @@ private:
const Vector3& forwardDirection);
void applyRoundEnd();
void applyInterpolation();
void addCap(const std::vector<size_t>& section, double ringV, double centerV);
};
};