Add section remesher and cap UV resolving
parent
7c9195722c
commit
1fc2399bf6
|
@ -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
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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"); \
|
||||
} \
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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"); \
|
||||
} \
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -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 "";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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 ""; \
|
||||
} \
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
}
|
|
@ -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
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -69,6 +69,7 @@ private:
|
|||
const Vector3& forwardDirection);
|
||||
void applyRoundEnd();
|
||||
void applyInterpolation();
|
||||
void addCap(const std::vector<size_t>& section, double ringV, double centerV);
|
||||
};
|
||||
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue