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
|
SOURCES += ../dust3d/mesh/rope_mesh.cc
|
||||||
HEADERS += ../dust3d/mesh/section_preview_mesh_builder.h
|
HEADERS += ../dust3d/mesh/section_preview_mesh_builder.h
|
||||||
SOURCES += ../dust3d/mesh/section_preview_mesh_builder.cc
|
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
|
HEADERS += ../dust3d/mesh/smooth_normal.h
|
||||||
SOURCES += ../dust3d/mesh/smooth_normal.cc
|
SOURCES += ../dust3d/mesh/smooth_normal.cc
|
||||||
HEADERS += ../dust3d/mesh/stitch_mesh_builder.h
|
HEADERS += ../dust3d/mesh/stitch_mesh_builder.h
|
||||||
|
|
|
@ -24,8 +24,44 @@
|
||||||
|
|
||||||
namespace dust3d {
|
namespace dust3d {
|
||||||
|
|
||||||
IMPL_CombineModeToString
|
CombineMode CombineModeFromString(const char* modeString)
|
||||||
IMPL_CombineModeFromString
|
{
|
||||||
IMPL_CombineModeToDispName
|
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,
|
Uncombined,
|
||||||
Count
|
Count
|
||||||
};
|
};
|
||||||
|
|
||||||
CombineMode CombineModeFromString(const char* modeString);
|
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);
|
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);
|
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.
|
* SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <algorithm> // std::reverse
|
#include <algorithm>
|
||||||
#include <dust3d/base/cut_face.h>
|
#include <dust3d/base/cut_face.h>
|
||||||
#include <dust3d/base/vector2.h>
|
#include <dust3d/base/vector2.h>
|
||||||
#include <dust3d/base/vector3.h>
|
#include <dust3d/base/vector3.h>
|
||||||
|
|
||||||
namespace dust3d {
|
namespace dust3d {
|
||||||
|
|
||||||
IMPL_CutFaceFromString
|
CutFace CutFaceFromString(const char* faceString)
|
||||||
IMPL_CutFaceToString
|
{
|
||||||
TMPL_CutFaceToPoints
|
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
|
std::string CutFaceToString(CutFace cutFace)
|
||||||
correctFlippedNormal(std::vector<Vector2>* points)
|
{
|
||||||
|
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)
|
if (points->size() < 3)
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -40,86 +40,8 @@ enum class CutFace {
|
||||||
};
|
};
|
||||||
|
|
||||||
CutFace CutFaceFromString(const char* faceString);
|
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);
|
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);
|
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 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,
|
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);
|
std::vector<std::string>* pointsIds = nullptr);
|
||||||
|
|
|
@ -24,8 +24,44 @@
|
||||||
|
|
||||||
namespace dust3d {
|
namespace dust3d {
|
||||||
|
|
||||||
IMPL_PartTargetFromString
|
PartTarget PartTargetFromString(const char* targetString)
|
||||||
IMPL_PartTargetToString
|
{
|
||||||
IMPL_PartTargetToDispName
|
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 {
|
enum class PartTarget {
|
||||||
Model = 0,
|
Model = 0,
|
||||||
CutFace,
|
CutFace,
|
||||||
Count, // FIXME: Enable StitchingLine after the UI is avaliable
|
StitchingLine,
|
||||||
StitchingLine
|
Count
|
||||||
};
|
};
|
||||||
|
|
||||||
PartTarget PartTargetFromString(const char* targetString);
|
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);
|
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);
|
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 {
|
namespace dust3d {
|
||||||
|
|
||||||
IMPL_TextureTypeToString
|
const char* TextureTypeToString(TextureType type)
|
||||||
IMPL_TextureTypeFromString
|
{
|
||||||
IMPL_TextureTypeToDispName
|
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);
|
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);
|
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);
|
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 <algorithm>
|
||||||
#include <dust3d/base/debug.h>
|
#include <dust3d/base/debug.h>
|
||||||
#include <dust3d/mesh/base_normal.h>
|
#include <dust3d/mesh/base_normal.h>
|
||||||
|
#include <dust3d/mesh/section_remesher.h>
|
||||||
#include <dust3d/mesh/tube_mesh_builder.h>
|
#include <dust3d/mesh/tube_mesh_builder.h>
|
||||||
|
|
||||||
namespace dust3d {
|
namespace dust3d {
|
||||||
|
@ -195,6 +196,21 @@ void TubeMeshBuilder::build()
|
||||||
|
|
||||||
m_generatedBaseNormal = m_isCircle ? BaseNormal::calculateCircleBaseNormal(m_nodePositions) : BaseNormal::calculateTubeBaseNormal(m_nodePositions);
|
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
|
// Build all vertex Positions
|
||||||
std::vector<std::vector<Vector3>> cutFaceVertexPositions;
|
std::vector<std::vector<Vector3>> cutFaceVertexPositions;
|
||||||
for (size_t i = 0; i < m_nodePositions.size(); ++i) {
|
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> {
|
m_generatedFaces.emplace_back(std::vector<size_t> {
|
||||||
cutFaceI[m], cutFaceI[n], cutFaceJ[n], cutFaceJ[m] });
|
cutFaceI[m], cutFaceI[n], cutFaceJ[n], cutFaceJ[m] });
|
||||||
m_generatedFaceUvs.emplace_back(std::vector<Vector2> {
|
m_generatedFaceUvs.emplace_back(std::vector<Vector2> {
|
||||||
cutFaceVertexUvs[i][m],
|
tubeUv(cutFaceVertexUvs[i][m]),
|
||||||
cutFaceVertexUvs[i][m + 1],
|
tubeUv(cutFaceVertexUvs[i][m + 1]),
|
||||||
cutFaceVertexUvs[j][m + 1],
|
tubeUv(cutFaceVertexUvs[j][m + 1]),
|
||||||
cutFaceVertexUvs[j][m] });
|
tubeUv(cutFaceVertexUvs[j][m]) });
|
||||||
}
|
}
|
||||||
for (size_t m = halfSize; m < cutFaceI.size(); ++m) {
|
for (size_t m = halfSize; m < cutFaceI.size(); ++m) {
|
||||||
size_t n = (m + 1) % cutFaceI.size();
|
size_t n = (m + 1) % cutFaceI.size();
|
||||||
|
@ -276,17 +292,42 @@ void TubeMeshBuilder::build()
|
||||||
m_generatedFaces.emplace_back(std::vector<size_t> {
|
m_generatedFaces.emplace_back(std::vector<size_t> {
|
||||||
cutFaceJ[m], cutFaceI[m], cutFaceI[n], cutFaceJ[n] });
|
cutFaceJ[m], cutFaceI[m], cutFaceI[n], cutFaceJ[n] });
|
||||||
m_generatedFaceUvs.emplace_back(std::vector<Vector2> {
|
m_generatedFaceUvs.emplace_back(std::vector<Vector2> {
|
||||||
cutFaceVertexUvs[j][m],
|
tubeUv(cutFaceVertexUvs[j][m]),
|
||||||
cutFaceVertexUvs[i][m],
|
tubeUv(cutFaceVertexUvs[i][m]),
|
||||||
cutFaceVertexUvs[i][m + 1],
|
tubeUv(cutFaceVertexUvs[i][m + 1]),
|
||||||
cutFaceVertexUvs[j][m + 1] });
|
tubeUv(cutFaceVertexUvs[j][m + 1]) });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!m_isCircle) {
|
if (!m_isCircle) {
|
||||||
m_generatedFaces.emplace_back(cutFaceIndices.back());
|
addCap(cutFaceIndices.back(), vOffsetBecauseOfFrontCap + vTubeRatio, 1.0);
|
||||||
m_generatedFaces.emplace_back(cutFaceIndices.front());
|
auto front = cutFaceIndices.front();
|
||||||
std::reverse(m_generatedFaces.back().begin(), m_generatedFaces.back().end());
|
std::reverse(front.begin(), front.end());
|
||||||
// TODO: Add UV for end cap
|
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);
|
const Vector3& forwardDirection);
|
||||||
void applyRoundEnd();
|
void applyRoundEnd();
|
||||||
void applyInterpolation();
|
void applyInterpolation();
|
||||||
|
void addCap(const std::vector<size_t>& section, double ringV, double centerV);
|
||||||
};
|
};
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue