14 triangleGroups_.clear();
25 if (!colors_.empty()) colors_.reserve(
size_t(
vertexCount) * 4u);
29 triangleGroups_.reserve(
size_t(
indexCount) / 3u);
35 positions_.push_back(
px);
36 positions_.push_back(
py);
37 positions_.push_back(
pz);
38 normals_.push_back(
nx);
39 normals_.push_back(
ny);
40 normals_.push_back(
nz);
43 if (!colors_.empty()) colors_.insert(colors_.end(), {1.f, 1.f, 1.f, 1.f});
47 indices_.push_back(
i0);
48 indices_.push_back(
i1);
49 indices_.push_back(
i2);
50 triangleGroups_.push_back(activeGroup_);
54 if (
name.empty()) { activeGroup_ = -1;
return -1; }
55 for (
int i = 0; i < int(groupNames_.size()); ++i) {
56 if (groupNames_[
size_t(i)] ==
name) { activeGroup_ = i;
return i; }
58 groupNames_.push_back(
name);
59 activeGroup_ = int(groupNames_.size()) - 1;
66 if (groupIndex < 0 || groupIndex >=
getGroupCount())
return {};
67 return groupNames_[size_t(groupIndex)];
71 if (triangleIndex < 0 || triangleIndex >=
int(triangleGroups_.size()))
return -1;
72 return triangleGroups_[size_t(triangleIndex)];
81 if (assignments.size() != indices_.size() / 3u ||
activeGroup < -1 ||
83 std::any_of(names.begin(), names.end(), [](
const std::string &
name) { return name.empty(); }) ||
84 std::any_of(assignments.begin(), assignments.end(),
85 [&](
int group) { return group < -1 || group >= static_cast<int>(names.size()); }))
86 return invalid(
"mesh group sidecar is inconsistent with dense mesh streams",
"groups");
87 groupNames_ = std::move(names);
88 triangleGroups_ = std::move(assignments);
94 if (groupIndex < 0 || groupIndex >=
getGroupCount())
return {};
95 auto result = std::make_unique<MeshBuild>();
96 result->setActiveGroup(groupNames_[
size_t(groupIndex)]);
97 std::unordered_map<uint32_t, uint32_t>
remap;
98 std::vector<float> copiedColors;
99 for (
int t = 0;
t < int(triangleGroups_.size()); ++
t) {
100 if (triangleGroups_[
size_t(
t)] != groupIndex)
continue;
102 for (
int corner = 0; corner < 3; ++corner) {
103 const uint32_t src = indices_[size_t(
t) * 3u + size_t(corner)];
106 const uint32_t next = uint32_t(result->getVertexCount());
111 for (
int component = 0; component < 4; ++component)
112 copiedColors.push_back(
getColor(
int(src), component));
113 remap.emplace(src, next); dst[corner] = next;
114 }
else dst[corner] =
found->second;
116 result->addTriangle(dst[0], dst[1], dst[2]);
118 if (result->empty())
return {};
119 if (!copiedColors.empty()) result->setVertexColors(std::move(copiedColors)).ignore(
"validated copied color stream");
120 result->meta_ = meta_;
121 result->setMeta(
"group", groupNames_[
size_t(groupIndex)]);
126 float yawDegrees,
float sx,
float sy,
float sz) {
127 if (!other || other ==
this || other->
empty() || std::fabs(
sx) < 1e-8f ||
128 std::fabs(
sy) < 1e-8f || std::fabs(
sz) < 1e-8f)
132 std::vector<int> groupMap(
size_t(other->
getGroupCount()), -1);
133 const int previousGroup = activeGroup_;
137 const float radians = yawDegrees * 0.01745329251994329577f;
138 const float c = std::cos(radians),
s = std::sin(radians);
146 float nx =
c * ix +
s * iz;
148 float nz = -
s * ix +
c * iz;
149 const float nl = std::sqrt(
nx *
nx +
ny *
ny +
nz *
nz);
150 if (nl > 1e-8f) {
nx /= nl;
ny /= nl;
nz /= nl; }
154 for (
int component = 0; component < 4; ++component)
155 colors_[colors_.size() - 4u + std::size_t(component)] = other->
getColor(i, component);
157 const bool mirrored = (
sx *
sy *
sz) < 0.f;
160 activeGroup_ = sourceGroup >= 0 ? groupMap[size_t(sourceGroup)] : -1;
161 const uint32_t
a = base + uint32_t(other->
getIndex(i));
162 const uint32_t
b = base + uint32_t(other->
getIndex(i + 1));
163 const uint32_t cidx = base + uint32_t(other->
getIndex(i + 2));
166 activeGroup_ = previousGroup;
176 return positions_[size_t(i) * 3u];
180 return positions_[size_t(i) * 3u + 1u];
184 return positions_[size_t(i) * 3u + 2u];
188 return normals_[size_t(i) * 3u];
192 return normals_[size_t(i) * 3u + 1u];
196 return normals_[size_t(i) * 3u + 2u];
200 return uvs_[size_t(i) * 2u];
204 return uvs_[size_t(i) * 2u + 1u];
207 return !colors_.empty() && colors_.size() == std::size_t(
getVertexCount()) * 4u;
210 if (!hasVertexColors() || vertexIndex < 0 || vertexIndex >= getVertexCount() || component < 0 || component > 3)
212 return colors_[std::size_t(vertexIndex) * 4u + std::size_t(component)];
217 "mesh vertex color count must match the vertex count",
"colors", {},
"procgen.mesh"));
218 if (std::any_of(
colors.begin(),
colors.end(), [](
float value) { return !std::isfinite(value); }))
220 "mesh vertex colors must be finite",
"colors", {},
"procgen.mesh"));
221 colors_ = std::move(
colors);
227 "mesh vertex index is out of range",
"vertexIndex", {},
"procgen.mesh"));
228 if (!std::isfinite(
r) || !std::isfinite(
g) || !std::isfinite(
b) || !std::isfinite(
a))
230 "mesh vertex color components must be finite",
"color", {},
"procgen.mesh"));
232 const auto base = std::size_t(vertexIndex) * 4u;
234 colors_[base + 1u] =
g;
235 colors_[base + 2u] =
b;
236 colors_[base + 3u] =
a;
241 return int(indices_[
size_t(i)]);
247 auto it = meta_.find(
key);
building::EdgeCurveGroup group
std::uint32_t vertexCount
std::unordered_map< std::uint32_t, std::uint32_t > remap
std::vector< float > colors
static Diagnostic error(DiagnosticCode code, std::string message, std::string path={}, DiagnosticDetails details={}, std::string source={})
Construct an error diagnostic with the standard error severity.
Move-only operation result carrying either a value or Status.
static Result success(T value)
Construct a successful result owning value.
static Result failure(Status status)
Construct a failed result from a structured status.
static Status success(StatusCode code=StatusCode::Ok)
Construct a successful status with an explicit non-error outcome.
CPU triangle mesh from procedural mesh recipes (e.g. marching cubes). Positions/normals are xyz-packe...
float getPositionZ(int i) const
Returns the position z.
eve::Result< void > setColor(int vertexIndex, float r, float g, float b, float a)
Set one vertex RGBA (allocates a default-white stream if absent).
float getPositionY(int i) const
Returns the position y.
std::string getMeta(const std::string &key, const std::string &defaultValue) const
Returns the meta.
void addVertex(float px, float py, float pz, float nx, float ny, float nz, float u, float v)
Adds vertex.
void addTriangle(uint32_t i0, uint32_t i1, uint32_t i2)
Adds triangle.
std::unique_ptr< MeshBuild > copyGroup(int groupIndex) const
Copies one named group into a standalone compact mesh.
float getNormalZ(int i) const
Returns the normal z.
int getGroupCount() const
Number of named triangle groups.
void reserve(int vertexCount, int indexCount)
Reserve.
void setMeta(const std::string &key, const std::string &value)
Sets the meta.
int setActiveGroup(const std::string &name)
Select/create the named group assigned to subsequently added triangles.
const std::vector< float > & colors() const
Colors.
float getNormalY(int i) const
Returns the normal y.
int getIndexCount() const
Returns the index count.
eve::Result< void > setVertexColors(std::vector< float > colors)
Atomically replace the optional packed RGBA stream; empty removes it.
eve::Result< void > restoreGroupData(std::vector< std::string > names, std::vector< int > assignments, int activeGroup)
Restore the complete group sidecar after dense streams are loaded.
int getTriangleGroup(int triangleIndex) const
Group index for a triangle (not an index-buffer element), or -1.
float getUvU(int i) const
Returns the uv u.
int getVertexCount() const
float getColor(int vertexIndex, int component) const noexcept
Return one color component, or 1 for an invalid vertex/component or an absent color stream.
bool appendTransformed(const MeshBuild *other, float tx, float ty, float tz, float yawDegrees, float sx, float sy, float sz)
Append another procedural mesh after an affine transform.
int activeGroup() const
Return the currently selected group, or -1 when none is active.
float getUvV(int i) const
Returns the uv v.
float getNormalX(int i) const
Returns the normal x.
int getIndex(int i) const
std::string getGroupName(int groupIndex) const
Group name, or an empty string for an invalid index.
bool hasVertexColors() const noexcept
Return whether this mesh owns one packed RGBA color per vertex.
float getPositionX(int i) const
Returns the position x.
const char * defaultValue
Result< int > invalid(std::string message)
Invalid.
@ ProcgenGroupDataInvalid
Mesh group sidecar data is inconsistent with its dense streams.