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