MXVK Vulkan Framework 0.35.0
C++20 Vulkan rendering framework for practical 2D and 3D application development with SDL3.
Loading...
Searching...
No Matches
mxvk::anonymous_namespace{mxvk_model.cpp} Namespace Reference

Classes

struct  MXMODParseResult
struct  OBJFaceVertex
struct  OBJIndex
struct  Vec2
struct  Vec3

Functions

void appendOBJTriangle (std::vector< VKVertex > &vertices, OBJFaceVertex a, OBJFaceVertex b, OBJFaceVertex c)
void assignNormalIfMissing (OBJFaceVertex &a, OBJFaceVertex &b, OBJFaceVertex &c)
float edgeCross2 (const VKVertex &a, const VKVertex &b, const VKVertex &c, int dropAxis)
Vec3 faceNormal (const VKVertex &a, const VKVertex &b, const VKVertex &c)
std::string inflateCompressedText (const std::vector< unsigned char > &compressedData)
void logMXModelStep (const std::string &message, bool important=false)
std::string materialNameForTextureIndex (uint32_t textureIndex, const std::vector< MXMaterial > &materials)
std::string mtlReferencePath (const std::string &objPath, const std::string &mtlPath)
std::string mtlTextureReferencePath (const std::filesystem::path &mtlPath, const std::string &texturePath)
void parseMTLStream (std::istream &file, const std::string &textureBasePath, std::vector< MXMaterial > &materials)
std::string parseMTLTexturePath (std::istream &stream)
MXMODParseResult parseMXMODStream (std::istream &file, const std::string &sourcePath, float positionScale)
bool parseOBJFaceToken (const std::string &token, OBJIndex &index)
bool parseOBJIndexValue (const std::string &text, int &value)
bool pointInProjectedTriangle (const VKVertex &point, const VKVertex &a, const VKVertex &b, const VKVertex &c, int dropAxis, float windingSign)
float projectedArea2 (const std::vector< OBJFaceVertex > &face, int dropAxis)
std::string resolveManifestPath (const std::string &basePath, const std::string &path)
template<typename T>
int resolveOBJIndex (int objIndex, const std::vector< T > &values)
void stripTrailingComment (std::string &s)
void triangulateOBJFace (const std::vector< OBJFaceVertex > &face, std::vector< VKVertex > &vertices)
void trim (std::string &s)
void writeMTLMaterial (std::ostream &out, const MXMaterial &material)

Function Documentation

◆ appendOBJTriangle()

void mxvk::anonymous_namespace{mxvk_model.cpp}::appendOBJTriangle ( std::vector< VKVertex > & vertices,
OBJFaceVertex a,
OBJFaceVertex b,
OBJFaceVertex c )

Definition at line 222 of file mxvk_model.cpp.

222 {
223 assignNormalIfMissing(a, b, c);
224 vertices.push_back(a.vertex);
225 vertices.push_back(b.vertex);
226 vertices.push_back(c.vertex);
227 }
void assignNormalIfMissing(OBJFaceVertex &a, OBJFaceVertex &b, OBJFaceVertex &c)

◆ assignNormalIfMissing()

void mxvk::anonymous_namespace{mxvk_model.cpp}::assignNormalIfMissing ( OBJFaceVertex & a,
OBJFaceVertex & b,
OBJFaceVertex & c )

Definition at line 173 of file mxvk_model.cpp.

173 {
174 if (a.hasNormal && b.hasNormal && c.hasNormal) {
175 return;
176 }
177
178 const Vec3 normal = faceNormal(a.vertex, b.vertex, c.vertex);
179 for (OBJFaceVertex *faceVertex : {&a, &b, &c}) {
180 if (faceVertex->hasNormal) {
181 continue;
182 }
183 faceVertex->vertex.normal[0] = normal.x;
184 faceVertex->vertex.normal[1] = normal.y;
185 faceVertex->vertex.normal[2] = normal.z;
186 faceVertex->hasNormal = true;
187 }
188 }
Vec3 faceNormal(const VKVertex &a, const VKVertex &b, const VKVertex &c)

◆ edgeCross2()

float mxvk::anonymous_namespace{mxvk_model.cpp}::edgeCross2 ( const VKVertex & a,
const VKVertex & b,
const VKVertex & c,
int dropAxis )
nodiscard

Definition at line 204 of file mxvk_model.cpp.

204 {
205 const float ax = a.pos[(dropAxis + 1) % 3];
206 const float ay = a.pos[(dropAxis + 2) % 3];
207 const float bx = b.pos[(dropAxis + 1) % 3];
208 const float by = b.pos[(dropAxis + 2) % 3];
209 const float cx = c.pos[(dropAxis + 1) % 3];
210 const float cy = c.pos[(dropAxis + 2) % 3];
211 return (bx - ax) * (cy - ay) - (by - ay) * (cx - ax);
212 }

◆ faceNormal()

Vec3 mxvk::anonymous_namespace{mxvk_model.cpp}::faceNormal ( const VKVertex & a,
const VKVertex & b,
const VKVertex & c )
nodiscard

Definition at line 151 of file mxvk_model.cpp.

151 {
152 const float ax = b.pos[0] - a.pos[0];
153 const float ay = b.pos[1] - a.pos[1];
154 const float az = b.pos[2] - a.pos[2];
155 const float bx = c.pos[0] - a.pos[0];
156 const float by = c.pos[1] - a.pos[1];
157 const float bz = c.pos[2] - a.pos[2];
158
159 Vec3 normal{
160 ay * bz - az * by,
161 az * bx - ax * bz,
162 ax * by - ay * bx,
163 };
164 const float len = std::sqrt(normal.x * normal.x + normal.y * normal.y + normal.z * normal.z);
165 if (len > 0.0f) {
166 normal.x /= len;
167 normal.y /= len;
168 normal.z /= len;
169 }
170 return normal;
171 }

◆ inflateCompressedText()

std::string mxvk::anonymous_namespace{mxvk_model.cpp}::inflateCompressedText ( const std::vector< unsigned char > & compressedData)
nodiscard

Definition at line 304 of file mxvk_model.cpp.

304 {
305 z_stream stream{};
306 stream.next_in = const_cast<Bytef *>(reinterpret_cast<const Bytef *>(compressedData.data()));
307 stream.avail_in = static_cast<uInt>(compressedData.size());
308
309 if (inflateInit2(&stream, 15 + 32) != Z_OK) {
310 throw mxvk::Exception("MXModel::loadMXMODZ failed to initialize zlib inflater");
311 }
312
313 std::string output{};
314 std::array<char, 16384> buffer{};
315
316 int result = Z_OK;
317 while (result != Z_STREAM_END) {
318 stream.next_out = reinterpret_cast<Bytef *>(buffer.data());
319 stream.avail_out = static_cast<uInt>(buffer.size());
320
321 result = inflate(&stream, Z_NO_FLUSH);
322 if (result != Z_OK && result != Z_STREAM_END) {
323 inflateEnd(&stream);
324 throw mxvk::Exception("MXModel::loadMXMODZ failed to inflate compressed data");
325 }
326
327 const size_t producedBytes = buffer.size() - static_cast<size_t>(stream.avail_out);
328 output.append(buffer.data(), producedBytes);
329 }
330
331 inflateEnd(&stream);
332
333 if (output.empty()) {
334 throw mxvk::Exception("MXModel::loadMXMODZ produced empty decompressed payload");
335 }
336
337 return output;
338 }

◆ logMXModelStep()

void mxvk::anonymous_namespace{mxvk_model.cpp}::logMXModelStep ( const std::string & message,
bool important = false )

Definition at line 48 of file mxvk_model.cpp.

48 {
49 if (important) {
50 std::cout << "mxvk_model: " << message << '\n';
51 }
52 }

◆ materialNameForTextureIndex()

std::string mxvk::anonymous_namespace{mxvk_model.cpp}::materialNameForTextureIndex ( uint32_t textureIndex,
const std::vector< MXMaterial > & materials )
nodiscard

Definition at line 443 of file mxvk_model.cpp.

443 {
444 if (textureIndex < static_cast<uint32_t>(materials.size()) && !materials[textureIndex].name.empty()) {
445 return materials[textureIndex].name;
446 }
447 return "material_" + std::to_string(textureIndex);
448 }

◆ mtlReferencePath()

std::string mxvk::anonymous_namespace{mxvk_model.cpp}::mtlReferencePath ( const std::string & objPath,
const std::string & mtlPath )
nodiscard

Definition at line 427 of file mxvk_model.cpp.

427 {
428 const std::filesystem::path objDir = std::filesystem::path(objPath).parent_path();
429 const std::filesystem::path materialPath(mtlPath);
430 if (objDir.empty()) {
431 return materialPath.filename().string();
432 }
433
434 std::error_code ec{};
435 const std::filesystem::path relative = std::filesystem::relative(materialPath, objDir, ec);
436 if (!ec && !relative.empty()) {
437 return relative.string();
438 }
439
440 return materialPath.filename().string();
441 }

◆ mtlTextureReferencePath()

std::string mxvk::anonymous_namespace{mxvk_model.cpp}::mtlTextureReferencePath ( const std::filesystem::path & mtlPath,
const std::string & texturePath )
nodiscard

Definition at line 450 of file mxvk_model.cpp.

450 {
451 if (texturePath.empty()) {
452 return {};
453 }
454
455 std::filesystem::path resolvedTexturePath(texturePath);
456 if (resolvedTexturePath.is_relative()) {
457 resolvedTexturePath = std::filesystem::absolute(resolvedTexturePath);
458 }
459
460 std::filesystem::path mtlDir = mtlPath.parent_path();
461 if (mtlDir.empty()) {
462 mtlDir = std::filesystem::current_path();
463 } else if (mtlDir.is_relative()) {
464 mtlDir = std::filesystem::absolute(mtlDir);
465 }
466
467 std::error_code ec{};
468 const std::filesystem::path relative = std::filesystem::relative(resolvedTexturePath, mtlDir, ec);
469 if (!ec && !relative.empty()) {
470 return relative.string();
471 }
472
473 return resolvedTexturePath.lexically_normal().string();
474 }

◆ parseMTLStream()

void mxvk::anonymous_namespace{mxvk_model.cpp}::parseMTLStream ( std::istream & file,
const std::string & textureBasePath,
std::vector< MXMaterial > & materials )

Definition at line 381 of file mxvk_model.cpp.

381 {
382 MXMaterial *current = nullptr;
383 std::string line{};
384 while (std::getline(file, line)) {
386 if (line.empty()) {
387 continue;
388 }
389
390 std::istringstream stream(line);
391 std::string tag{};
392 stream >> tag;
393
394 if (tag == "newmtl") {
395 materials.emplace_back();
396 current = &materials.back();
397 std::getline(stream, current->name);
398 trim(current->name);
399 continue;
400 }
401
402 if (current == nullptr) {
403 continue;
404 }
405
406 if (tag == "Ka") {
407 stream >> current->ka[0] >> current->ka[1] >> current->ka[2];
408 } else if (tag == "Kd") {
409 stream >> current->kd[0] >> current->kd[1] >> current->kd[2];
410 } else if (tag == "Ks") {
411 stream >> current->ks[0] >> current->ks[1] >> current->ks[2];
412 } else if (tag == "Ns") {
413 stream >> current->ns;
414 } else if (tag == "d") {
415 stream >> current->d;
416 } else if (tag == "illum") {
417 stream >> current->illum;
418 } else if (tag == "map_Kd") {
419 const std::string mapPath = parseMTLTexturePath(stream);
420 if (!mapPath.empty()) {
421 current->map_kd = resolveManifestPath(textureBasePath, mapPath);
422 }
423 }
424 }
425 }
std::string parseMTLTexturePath(std::istream &stream)
std::string resolveManifestPath(const std::string &basePath, const std::string &path)
Parsed subset of Wavefront MTL material fields.
std::string map_kd
std::string name

◆ parseMTLTexturePath()

std::string mxvk::anonymous_namespace{mxvk_model.cpp}::parseMTLTexturePath ( std::istream & stream)
nodiscard

Definition at line 353 of file mxvk_model.cpp.

353 {
354 std::string mapPath{};
355 std::string mapToken{};
356 while (stream >> mapToken) {
357 if (!mapToken.empty() && mapToken[0] == '-') {
358 if (mapToken == "-blendu" || mapToken == "-blendv" || mapToken == "-cc" || mapToken == "-clamp" || mapToken == "-imfchan" || mapToken == "-type") {
359 stream >> mapToken;
360 } else if (mapToken == "-mm") {
361 stream >> mapToken;
362 stream >> mapToken;
363 } else if (mapToken == "-o" || mapToken == "-s" || mapToken == "-t") {
364 stream >> mapToken;
365 stream >> mapToken;
366 stream >> mapToken;
367 } else if (mapToken == "-bm" || mapToken == "-boost" || mapToken == "-texres") {
368 stream >> mapToken;
369 }
370 continue;
371 }
372
373 if (!mapPath.empty()) {
374 mapPath += ' ';
375 }
376 mapPath += mapToken;
377 }
378 return mapPath;
379 }

◆ parseMXMODStream()

MXMODParseResult mxvk::anonymous_namespace{mxvk_model.cpp}::parseMXMODStream ( std::istream & file,
const std::string & sourcePath,
float positionScale )
nodiscard

Definition at line 490 of file mxvk_model.cpp.

490 {
491 struct TriBlock {
492 uint32_t textureIndex = 0;
493 std::vector<Vec3> pos{};
494 std::vector<Vec2> uv{};
495 std::vector<Vec3> nrm{};
496 std::vector<uint32_t> fileIndices{};
497 };
498
499 std::vector<TriBlock> triBlocks{};
500 TriBlock *current = nullptr;
501 int sectionType = -1;
502
503 std::string line{};
504 while (std::getline(file, line)) {
506 if (line.empty()) {
507 continue;
508 }
509
510 std::istringstream stream(line);
511 const char c = line[line.find_first_not_of(" \t")];
512 const bool isData = (c >= '0' && c <= '9') || c == '-' || c == '+' || c == '.';
513
514 if (isData && current != nullptr) {
515 float x = 0.0f;
516 float y = 0.0f;
517 float z = 0.0f;
518
519 switch (sectionType) {
520 case 0:
521 if (stream >> x >> y >> z) {
522 current->pos.push_back({x * positionScale, y * positionScale, z * positionScale});
523 }
524 break;
525 case 1:
526 if (stream >> x >> y) {
527 current->uv.push_back({x, y});
528 }
529 break;
530 case 2:
531 if (stream >> x >> y >> z) {
532 current->nrm.push_back({x, y, z});
533 }
534 break;
535 case 5: {
536 uint32_t idx = 0;
537 while (stream >> idx) {
538 current->fileIndices.push_back(idx);
539 }
540 } break;
541 default:
542 break;
543 }
544
545 continue;
546 }
547
548 std::string tag{};
549 stream >> tag;
550 if (tag == "tri") {
551 uint32_t surfaceType = 0;
552 uint32_t textureIndex = 0;
553 stream >> surfaceType >> textureIndex;
554 static_cast<void>(surfaceType);
555 triBlocks.emplace_back();
556 current = &triBlocks.back();
557 current->textureIndex = textureIndex;
558 sectionType = -1;
559 continue;
560 }
561
562 if (tag == "vert") {
563 sectionType = 0;
564 continue;
565 }
566 if (tag == "tex") {
567 sectionType = 1;
568 continue;
569 }
570 if (tag == "norm") {
571 sectionType = 2;
572 continue;
573 }
574 if (tag == "indices") {
575 sectionType = 5;
576 continue;
577 }
578 }
579
580 if (triBlocks.empty()) {
581 throw mxvk::Exception("MXModel::loadMXMOD no geometry found in " + sourcePath);
582 }
583
584 MXMODParseResult parsed{};
585 for (const TriBlock &blk : triBlocks) {
586 if (blk.pos.empty()) {
587 continue;
588 }
589
590 const uint32_t vertexBase = static_cast<uint32_t>(parsed.vertices.size());
591 for (size_t i = 0; i < blk.pos.size(); ++i) {
592 VKVertex v{};
593 v.pos[0] = blk.pos[i].x;
594 v.pos[1] = blk.pos[i].y;
595 v.pos[2] = blk.pos[i].z;
596
597 if (i < blk.uv.size()) {
598 v.texCoord[0] = blk.uv[i].x;
599 v.texCoord[1] = blk.uv[i].y;
600 }
601 if (i < blk.nrm.size()) {
602 v.normal[0] = blk.nrm[i].x;
603 v.normal[1] = blk.nrm[i].y;
604 v.normal[2] = blk.nrm[i].z;
605 }
606
607 parsed.vertices.push_back(v);
608 }
609
610 const uint32_t firstIndex = static_cast<uint32_t>(parsed.indices.size());
611 if (!blk.fileIndices.empty()) {
612 for (uint32_t idx : blk.fileIndices) {
613 parsed.indices.push_back(vertexBase + idx);
614 }
615 } else {
616 for (uint32_t i = 0; i < static_cast<uint32_t>(blk.pos.size()); ++i) {
617 parsed.indices.push_back(vertexBase + i);
618 }
619 }
620
621 SubMesh sm{};
622 sm.firstIndex = firstIndex;
623 sm.indexCount = static_cast<uint32_t>(parsed.indices.size()) - firstIndex;
624 sm.textureIndex = blk.textureIndex;
625 parsed.subMeshes.push_back(sm);
626 }
627
628 if (parsed.vertices.empty() || parsed.indices.empty()) {
629 throw mxvk::Exception("MXModel::loadMXMOD no renderable data found in " + sourcePath);
630 }
631
632 return parsed;
633 }

◆ parseOBJFaceToken()

bool mxvk::anonymous_namespace{mxvk_model.cpp}::parseOBJFaceToken ( const std::string & token,
OBJIndex & index )
nodiscard

Definition at line 116 of file mxvk_model.cpp.

116 {
117 std::array<std::string, 3> fields{};
118 size_t fieldIndex = 0;
119 size_t fieldBegin = 0;
120
121 while (true) {
122 if (fieldIndex >= fields.size()) {
123 return false;
124 }
125
126 const size_t slashPos = token.find('/', fieldBegin);
127 fields[fieldIndex++] = token.substr(fieldBegin, slashPos == std::string::npos ? std::string::npos : slashPos - fieldBegin);
128 if (slashPos == std::string::npos) {
129 break;
130 }
131 fieldBegin = slashPos + 1;
132 }
133
134 if (!parseOBJIndexValue(fields[0], index.position) || !parseOBJIndexValue(fields[1], index.texcoord) || !parseOBJIndexValue(fields[2], index.normal)) {
135 return false;
136 }
137
138 return index.position != 0;
139 }
bool parseOBJIndexValue(const std::string &text, int &value)

◆ parseOBJIndexValue()

bool mxvk::anonymous_namespace{mxvk_model.cpp}::parseOBJIndexValue ( const std::string & text,
int & value )
nodiscard

Definition at line 100 of file mxvk_model.cpp.

100 {
101 if (text.empty()) {
102 value = 0;
103 return true;
104 }
105
106 size_t consumed = 0;
107 try {
108 value = std::stoi(text, &consumed, 10);
109 } catch (const std::exception &) {
110 return false;
111 }
112
113 return consumed == text.size();
114 }

◆ pointInProjectedTriangle()

bool mxvk::anonymous_namespace{mxvk_model.cpp}::pointInProjectedTriangle ( const VKVertex & point,
const VKVertex & a,
const VKVertex & b,
const VKVertex & c,
int dropAxis,
float windingSign )
nodiscard

Definition at line 214 of file mxvk_model.cpp.

214 {
215 constexpr float epsilon = 1e-6f;
216 const float ab = edgeCross2(a, b, point, dropAxis) * windingSign;
217 const float bc = edgeCross2(b, c, point, dropAxis) * windingSign;
218 const float ca = edgeCross2(c, a, point, dropAxis) * windingSign;
219 return ab >= -epsilon && bc >= -epsilon && ca >= -epsilon;
220 }
float edgeCross2(const VKVertex &a, const VKVertex &b, const VKVertex &c, int dropAxis)

◆ projectedArea2()

float mxvk::anonymous_namespace{mxvk_model.cpp}::projectedArea2 ( const std::vector< OBJFaceVertex > & face,
int dropAxis )
nodiscard

Definition at line 190 of file mxvk_model.cpp.

190 {
191 float area = 0.0f;
192 for (size_t i = 0; i < face.size(); ++i) {
193 const VKVertex &a = face[i].vertex;
194 const VKVertex &b = face[(i + 1) % face.size()].vertex;
195 const float ax = a.pos[(dropAxis + 1) % 3];
196 const float ay = a.pos[(dropAxis + 2) % 3];
197 const float bx = b.pos[(dropAxis + 1) % 3];
198 const float by = b.pos[(dropAxis + 2) % 3];
199 area += ax * by - bx * ay;
200 }
201 return area;
202 }
Vertex payload consumed by MXVK model shaders.

◆ resolveManifestPath()

std::string mxvk::anonymous_namespace{mxvk_model.cpp}::resolveManifestPath ( const std::string & basePath,
const std::string & path )
nodiscard

Definition at line 340 of file mxvk_model.cpp.

340 {
341 if (path.empty()) {
342 return {};
343 }
344
345 std::filesystem::path resolved(path);
346 if (resolved.is_absolute() || basePath.empty()) {
347 return resolved.lexically_normal().string();
348 }
349
350 return (std::filesystem::path(basePath) / resolved).lexically_normal().string();
351 }

◆ resolveOBJIndex()

template<typename T>
int mxvk::anonymous_namespace{mxvk_model.cpp}::resolveOBJIndex ( int objIndex,
const std::vector< T > & values )
nodiscard

Definition at line 141 of file mxvk_model.cpp.

141 {
142 if (objIndex > 0) {
143 return objIndex - 1;
144 }
145 if (objIndex < 0) {
146 return static_cast<int>(values.size()) + objIndex;
147 }
148 return -1;
149 }

◆ stripTrailingComment()

void mxvk::anonymous_namespace{mxvk_model.cpp}::stripTrailingComment ( std::string & s)

Definition at line 92 of file mxvk_model.cpp.

92 {
93 const size_t commentPos = s.find('#');
94 if (commentPos != std::string::npos) {
95 s = s.substr(0, commentPos);
96 }
97 trim(s);
98 }

◆ triangulateOBJFace()

void mxvk::anonymous_namespace{mxvk_model.cpp}::triangulateOBJFace ( const std::vector< OBJFaceVertex > & face,
std::vector< VKVertex > & vertices )

Definition at line 229 of file mxvk_model.cpp.

229 {
230 if (face.size() < 3) {
231 return;
232 }
233
234 if (face.size() == 3) {
235 appendOBJTriangle(vertices, face[0], face[1], face[2]);
236 return;
237 }
238
239 const Vec3 normal = faceNormal(face[0].vertex, face[1].vertex, face[2].vertex);
240 int dropAxis = 0;
241 if (std::fabs(normal.y) > std::fabs(normal.x) && std::fabs(normal.y) >= std::fabs(normal.z)) {
242 dropAxis = 1;
243 } else if (std::fabs(normal.z) > std::fabs(normal.x) && std::fabs(normal.z) > std::fabs(normal.y)) {
244 dropAxis = 2;
245 }
246
247 float windingSign = projectedArea2(face, dropAxis) >= 0.0f ? 1.0f : -1.0f;
248 if (std::fabs(projectedArea2(face, dropAxis)) <= 1e-6f) {
249 for (size_t i = 2; i < face.size(); ++i) {
250 appendOBJTriangle(vertices, face[0], face[i - 1], face[i]);
251 }
252 return;
253 }
254
255 std::vector<size_t> remaining(face.size());
256 for (size_t i = 0; i < remaining.size(); ++i) {
257 remaining[i] = i;
258 }
259
260 while (remaining.size() > 3) {
261 bool clippedEar = false;
262 for (size_t i = 0; i < remaining.size(); ++i) {
263 const size_t previous = remaining[(i + remaining.size() - 1) % remaining.size()];
264 const size_t current = remaining[i];
265 const size_t next = remaining[(i + 1) % remaining.size()];
266
267 const float cross = edgeCross2(face[previous].vertex, face[current].vertex, face[next].vertex, dropAxis) * windingSign;
268 if (cross <= 1e-6f) {
269 continue;
270 }
271
272 bool containsPoint = false;
273 for (const size_t test : remaining) {
274 if (test == previous || test == current || test == next) {
275 continue;
276 }
277 if (pointInProjectedTriangle(face[test].vertex, face[previous].vertex, face[current].vertex, face[next].vertex, dropAxis, windingSign)) {
278 containsPoint = true;
279 break;
280 }
281 }
282
283 if (containsPoint) {
284 continue;
285 }
286
287 appendOBJTriangle(vertices, face[previous], face[current], face[next]);
288 remaining.erase(remaining.begin() + static_cast<std::ptrdiff_t>(i));
289 clippedEar = true;
290 break;
291 }
292
293 if (!clippedEar) {
294 for (size_t i = 2; i < remaining.size(); ++i) {
295 appendOBJTriangle(vertices, face[remaining[0]], face[remaining[i - 1]], face[remaining[i]]);
296 }
297 return;
298 }
299 }
300
301 appendOBJTriangle(vertices, face[remaining[0]], face[remaining[1]], face[remaining[2]]);
302 }
bool pointInProjectedTriangle(const VKVertex &point, const VKVertex &a, const VKVertex &b, const VKVertex &c, int dropAxis, float windingSign)
float projectedArea2(const std::vector< OBJFaceVertex > &face, int dropAxis)
void appendOBJTriangle(std::vector< VKVertex > &vertices, OBJFaceVertex a, OBJFaceVertex b, OBJFaceVertex c)

◆ trim()

void mxvk::anonymous_namespace{mxvk_model.cpp}::trim ( std::string & s)

Definition at line 82 of file mxvk_model.cpp.

82 {
83 const size_t begin = s.find_first_not_of(" \t\r\n");
84 if (begin == std::string::npos) {
85 s.clear();
86 return;
87 }
88 const size_t end = s.find_last_not_of(" \t\r\n");
89 s = s.substr(begin, end - begin + 1);
90 }

◆ writeMTLMaterial()

void mxvk::anonymous_namespace{mxvk_model.cpp}::writeMTLMaterial ( std::ostream & out,
const MXMaterial & material )

Definition at line 476 of file mxvk_model.cpp.

476 {
477 out << "newmtl " << material.name << '\n';
478 out << "Ka " << material.ka[0] << ' ' << material.ka[1] << ' ' << material.ka[2] << '\n';
479 out << "Kd " << material.kd[0] << ' ' << material.kd[1] << ' ' << material.kd[2] << '\n';
480 out << "Ks " << material.ks[0] << ' ' << material.ks[1] << ' ' << material.ks[2] << '\n';
481 out << "Ns " << material.ns << '\n';
482 out << "d " << material.d << '\n';
483 out << "illum " << material.illum << '\n';
484 if (!material.map_kd.empty()) {
485 out << "map_Kd " << material.map_kd << '\n';
486 }
487 out << '\n';
488 }