5 #include <glm\gtc\matrix_transform.hpp>
6 #include <glm\gtc\type_ptr.hpp>
17 #include "convexPolytope.h"
18 #include "fuzzy_select.h"
19 #include "interpolation.h"
27 bool Vector3f(std::string str, glm::vec3* vec)
29 str = sutil::removeChar(str, '(');
30 str = sutil::removeChar(str, ')');
32 std::vector<std::string> elems = split(str, ' ');
33 std::vector<float> pelems;
35 for (int i = 0; i < elems.size(); i++) {
36 std::string f = sutil::trim(elems[i]);
38 //TODO: error check against invalid values here
39 float e = ::atof(f.c_str());
43 if (pelems.size() == 3) {
44 *vec = glm::vec3(pelems[0], pelems[1], pelems[2]);
51 //Parse Vector 3 with square barackets. Thanks again, valve
52 bool Vector3fS(std::string str, glm::vec3* vec)
54 str = sutil::removeChar(str, '[');
55 str = sutil::removeChar(str, ']');
57 std::vector<std::string> elems = split(str, ' ');
58 std::vector<float> pelems;
60 for (int i = 0; i < elems.size(); i++) {
61 std::string f = sutil::trim(elems[i]);
63 //TODO: error check against invalid values here
64 float e = ::atof(f.c_str());
68 if (pelems.size() == 3) {
69 *vec = glm::vec3(pelems[0], pelems[1], pelems[2]);
76 //Parse plane from standard 3 point notation (ax, ay, az) (bx, by, bz) ...
77 bool plane(std::string str, Plane* plane)
79 std::vector<std::string> points = split(str, '(');
81 if (points.size() != 4) { return false; }
85 if (!(Vector3f(points[1], &A) && Vector3f(points[2], &B) && Vector3f(points[3], &C))) {
89 *plane = Plane(A, B, C);
100 void progress_callback() {
103 if (current_line == line_count - 1)
104 std::cout << "Line " << current_line << "/" << line_count << "\n";
105 if(((current_line % 10000) == 0))
106 std::cout << "Line " << current_line << "/" << line_count << "\r";
121 glm::vec3 startposition;
123 std::vector<std::vector<glm::vec3>> normals;
124 std::vector<std::vector<float>> distances;
126 // OpenGL generated mesh
135 DispInfo* displacement = NULL;
141 bool containsDisplacements = false;
142 std::vector<Side> faces;
147 std::vector<unsigned short> visgroupids;
161 std::string classname;
164 std::map<std::string, std::string> keyValues;
165 std::vector<Solid> internal_solids;
172 kv::FileData internal;
173 std::vector<Mesh> meshes;
174 std::vector<Solid> solids;
175 std::vector<Entity> entities;
177 std::map<unsigned short, std::string> visgroups;
179 vmf(std::string path)
181 std::ifstream ifs(path);
183 std::cout << "Could not open file... " << path << std::endl;
184 throw std::exception("File read error");
187 std::cout << "Initializing VMF read\n";
188 std::ifstream _ifs(path);
190 // new lines will be skipped unless we stop it from happening:
191 _ifs.unsetf(std::ios_base::skipws);
193 // count the newlines with an algorithm specialized for counting:
194 line_count = std::count(
195 std::istream_iterator<char>(_ifs),
196 std::istream_iterator<char>(),
201 std::cout << "Reading raw VMF\n";
202 std::string str((std::istreambuf_iterator<char>(ifs)), std::istreambuf_iterator<char>());
204 std::cout << "Processing VMF data\n";
205 kv::FileData data(str, &progress_callback);
207 this->internal = data;
209 #pragma region Solids
210 std::cout << "Processing solids\n";
212 //Process solids list
213 std::vector<kv::DataBlock> SolidList = data.headNode.GetFirstByName("world")->GetAllByName("solid");
214 int total = SolidList.size();
216 for (int i = 0; i < SolidList.size(); i++){
217 std::cout << "Solid " << i + 1 << "/" << total << "\r";
219 kv::DataBlock cBlock = SolidList[i];
224 std::vector<kv::DataBlock> Sides = cBlock.GetAllByName("side");
225 for (int j = 0; j < Sides.size(); j++)
227 kv::DataBlock cSide = Sides[j];
230 side.ID = ::atof(cSide.Values["id"].c_str());
231 side.texture = cSide.Values["material"];
234 if (!vmf_parse::plane(cSide.Values["plane"], &plane))
236 valid = false; break;
241 // Deal with displacement info. Oh no
243 #pragma region displacements
245 DispInfo* dispInfo = new DispInfo;
247 kv::DataBlock* dblockInfo = cSide.GetFirstByName("dispinfo");
249 if (dblockInfo != NULL){
250 solid.containsDisplacements = true; // Mark we have displacements here
252 kv::DataBlock* dblockNormals = dblockInfo->GetFirstByName("normals");
253 kv::DataBlock* dblockDistances = dblockInfo->GetFirstByName("distances");
254 dispInfo->power = std::stoi(dblockInfo->Values["power"]);
255 vmf_parse::Vector3fS(dblockInfo->Values["startposition"], &dispInfo->startposition);
257 int i_target = glm::pow(2, dispInfo->power) + 1;
259 for (int x = 0; x < i_target; x++) { //Row
260 dispInfo->normals.push_back(std::vector<glm::vec3>()); //Create row container
261 dispInfo->distances.push_back(std::vector<float>()); //Create distances container
263 //Parse in the normals
264 std::vector<std::string> values = split(dblockNormals->Values["row" + std::to_string(x)]);
265 std::vector<float> list;
266 for (auto && v : values) list.push_back(::atof(v.c_str()));
268 //Parse in the distances
269 std::vector<std::string> _values = split(dblockDistances->Values["row" + std::to_string(x)]);
270 for (auto && v : _values) dispInfo->distances[x].push_back(std::stof(v.c_str()));
272 for (int xx = 0; xx < i_target; xx++) { //Column
273 dispInfo->normals[x].push_back(
274 glm::vec3(list[xx * 3 + 0],
280 side.displacement = dispInfo;
284 solid.faces.push_back(side);
287 kv::DataBlock* editorValues = cBlock.GetFirstByName("editor");
289 //Gather up the visgroups
291 while (editorValues->Values.count("visgroupid" + (++viscount > 0 ? std::to_string(viscount) : "")))
292 solid.visgroupids.push_back(std::stoi(editorValues->Values["visgroupid" + (viscount > 0 ? std::to_string(viscount) : "")]));
295 if (vmf_parse::Vector3f(editorValues->Values["color"], &color))
296 solid.color = glm::vec3(color.x / 255.0f, color.y / 255.0f, color.z / 255.0f);
298 solid.color = glm::vec3(1, 0, 0);
300 this->solids.push_back(solid);
305 #pragma endregion Solids
307 std::cout << "Processing entites\n";
309 //Process entities list
310 std::vector<kv::DataBlock> EntitiesList = data.headNode.GetAllByName("entity");
311 for (auto && block : EntitiesList) {
313 //if (block.Values["classname"] != "prop_static") continue; //Skip anything else than prop static for now
315 //Check wether origin can be resolved for entity
316 if ((block.GetFirstByName("solid") == NULL) && (block.Values.count("origin") == 0)) {
317 std::cout << "Origin could not be resolved for entity with ID " << block.Values["id"]; continue;
321 ent.classname = block.Values["classname"];
322 ent.ID = (int)::atof(block.Values["id"].c_str());
323 ent.keyValues = block.Values;
325 glm::vec3 loc = glm::vec3();
326 if (block.Values.count("origin")) { //Start with hammer origin
327 vmf_parse::Vector3f(block.Values["origin"], &loc);
328 ent.origin = glm::vec3(loc.x, loc.y, loc.z);
330 else if (block.GetFirstByName("solid") != NULL) { //Try to process it from solid
332 std::vector<kv::DataBlock> _solids = block.GetAllByName("solid");
333 //std::vector<Solid> _solids_ent;
334 for (int i = 0; i < _solids.size(); i++)
336 kv::DataBlock cBlock = _solids[i];
341 std::vector<kv::DataBlock> Sides = cBlock.GetAllByName("side");
342 for (int j = 0; j < Sides.size(); j++)
344 kv::DataBlock cSide = Sides[j];
347 side.ID = ::atof(cSide.Values["id"].c_str());
348 side.texture = cSide.Values["material"];
351 if (!vmf_parse::plane(cSide.Values["plane"], &plane))
353 valid = false; break;
358 solid.faces.push_back(side);
361 kv::DataBlock* editorValues = cBlock.GetFirstByName("editor");
363 //Gather up the visgroups
365 while (editorValues->Values.count("visgroupid" + (++viscount > 0 ? std::to_string(viscount) : "")))
366 solid.visgroupids.push_back(std::stoi(editorValues->Values["visgroupid" + (viscount > 0 ? std::to_string(viscount) : "")]));
369 if (vmf_parse::Vector3f(editorValues->Values["color"], &color))
370 solid.color = glm::vec3(color.x / 255.0f, color.y / 255.0f, color.z / 255.0f);
372 solid.color = glm::vec3(1, 0, 0);
374 ent.internal_solids.push_back(solid);
377 //Process convex polytopes & calculate origin
379 std::vector<Polytope> polytopes;
381 for (auto && iSolid : ent.internal_solids) {
382 std::vector<Plane> planes;
383 for (auto f : iSolid.faces) planes.push_back(f.plane);
385 polytopes.push_back(Polytope(planes, false));
388 glm::vec3 NWU = polytopes[0].NWU;
389 glm::vec3 SEL = polytopes[0].SEL;
391 for (auto && iPoly : polytopes) {
392 if (iPoly.NWU.z > NWU.z) NWU.z = iPoly.NWU.z;
393 if (iPoly.NWU.y > NWU.y) NWU.y = iPoly.NWU.y;
394 if (iPoly.NWU.x > NWU.x) NWU.x = iPoly.NWU.x;
396 if (iPoly.SEL.z < SEL.z) SEL.z = iPoly.SEL.z;
397 if (iPoly.SEL.y < SEL.y) SEL.y = iPoly.SEL.y;
398 if (iPoly.SEL.x < SEL.x) SEL.x = iPoly.SEL.x;
401 ent.origin = (NWU + SEL) * 0.5f;
404 this->entities.push_back(ent);
407 std::cout << "Processing visgroups\n";
410 std::vector<kv::DataBlock> VisList = data.headNode.GetFirstByName("visgroups")->GetAllByName("visgroup");
411 for (auto v : VisList) {
412 this->visgroups.insert({ std::stoi(v.Values["visgroupid"]), v.Values["name"] });
414 std::cout << "Visgroup {" << std::stoi(v.Values["visgroupid"]) << "} = '" << v.Values["name"] << "'\n";
418 std::vector<Solid*> getSolidsInVisGroup(std::string visgroup) {
419 std::vector<Solid*> list;
420 for (auto && v : this->solids) {
421 for (auto && vid : v.visgroupids) {
422 if (this->visgroups[vid] == visgroup) {
431 std::vector<Solid*> getAllBrushesInVisGroup(std::string visgroup) {
432 std::vector<Solid*> list;
435 for (auto && v : this->solids) {
436 for (auto && vid : v.visgroupids) {
437 if (this->visgroups[vid] == visgroup) {
443 // All entity brush solids
444 for (auto && e : this->entities) {
445 for (auto && es : e.internal_solids) {
446 for (auto && esvid : es.visgroupids) {
447 if (this->visgroups[esvid] == visgroup) {
457 std::vector<Solid*> getAllBrushesByClassName(std::string classname) {
458 std::vector<Solid*> list;
459 for (auto && ent : this->entities) {
460 if (ent.classname == classname) {
461 for (auto && s : ent.internal_solids) {
469 /* Gets a list of entities with matching classname */
470 std::vector<Entity*> findEntitiesByClassName(std::string classname) {
471 std::vector<Entity*> list;
472 for (auto && ent : this->entities) {
473 if (ent.classname == classname) {
474 list.push_back(&ent);
480 glm::vec3* calculateSpawnLocation(team _team) {
482 std::vector<Entity*> spawns = this->findEntitiesByClassName(_team == team::terrorist ? "info_player_terrorist" : "info_player_counterterrorist");
484 if (spawns.size() <= 0) return NULL;
486 //Find lowest priority (highest)
487 int lowest = kv::tryGetValue<int>(spawns[0]->keyValues, "priority", 0);
488 for (auto && s : spawns) {
489 int l = kv::tryGetValue<int>(s->keyValues, "priority", 0);
490 lowest = l < lowest ? l : lowest;
493 //Collect all spawns with that priority
494 glm::vec3* location = new glm::vec3();
496 for (auto && s : spawns) {
497 if (kv::tryGetValue<int>(s->keyValues, "priority", 0) == lowest) {
498 *location += s->origin; c++;
503 *location = *location / (float)c;
507 void ComputeGLMeshes() {
508 auto start = std::chrono::high_resolution_clock::now();
510 std::cout << "Processing solid meshes... ";
511 for (int i = 0; i < this->solids.size(); i++) {
512 std::vector<Plane> sidePlanes;
513 for (int j = 0; j < this->solids[i].faces.size(); j++)
514 sidePlanes.push_back(this->solids[i].faces[j].plane);
516 Polytope p = Polytope(sidePlanes);
517 this->solids[i].mesh = p.GeneratedMesh;
518 this->solids[i].origin = (p.NWU + p.SEL) * 0.5f;
519 this->solids[i].bounds.NWU = p.NWU;
520 this->solids[i].bounds.SEL = p.SEL;
522 std::cout << "done\n";
524 std::cout << "Processing entity solid meshes... ";
525 for (auto && ent : this->entities) {
526 for (auto && _solid : ent.internal_solids) {
527 std::vector<Plane> sidePlanes;
528 for (auto f : _solid.faces)
529 sidePlanes.push_back(f.plane);
531 Polytope p = Polytope(sidePlanes);
532 _solid.mesh = p.GeneratedMesh;
533 _solid.origin = (p.NWU + p.SEL) * 0.5f;
534 _solid.bounds.NWU = p.NWU;
535 _solid.bounds.SEL = p.SEL;
538 std::cout << "done\n";
540 auto elapsed = std::chrono::high_resolution_clock::now() - start;
541 long long milliseconds = std::chrono::duration_cast<std::chrono::milliseconds>(elapsed).count();
543 std::cout << "GL mesh computation: " << milliseconds << "ms" << std::endl;
546 void ComputeDisplacements() {
547 auto start = std::chrono::high_resolution_clock::now();
549 std::cout << "Computing displacements...\n";
551 for (auto && v :this->solids) {
552 if (v.containsDisplacements) {
554 std::vector<Plane> planes;
555 for (auto && face : v.faces) planes.push_back(face.plane);
557 Polytope polyTope = Polytope(planes, true, false); // Generate polytope so we can look at the individual ngon points
559 for (auto && side :v.faces) {
560 if (side.displacement != NULL) {
561 DispInfo* info = side.displacement;
563 BrushPolygon* bpoly = NULL;
565 std::map<float, BrushPolygon*> normalCorrelations;
567 //Sort planes by their similarity to the face's normal direction
568 for (auto && fuck : polyTope.ngons)
569 normalCorrelations.insert({ glm::distance(fuck.plane.normal, side.plane.normal), &fuck });
571 bpoly = normalCorrelations.begin()->second;
573 if (bpoly->vertices.size() != 4) {
574 std::cout << "Displacement info matched to face with {" << bpoly->vertices.size() << "} vertices!!!\n"; continue;
577 // Match the 'starting point' of dispinfo
578 std::map<float, glm::vec3*> distancesToStart;
579 for (auto && point : bpoly->vertices)
580 distancesToStart.insert({ glm::distance(info->startposition, point), &point });
582 // The corners of the displacement
583 glm::vec3* SW = distancesToStart.begin()->second;
585 // Find what point in the vector it was
587 for (auto && point : bpoly->vertices)
588 if (&point == SW) break; else pos++;
590 // Get the rest of the points, in clockwise order (they should already be sorted by polytope generation)
591 glm::vec3* NW = &bpoly->vertices[(pos + 1) % 4];
592 glm::vec3* NE = &bpoly->vertices[(pos + 2) % 4];
593 glm::vec3* SE = &bpoly->vertices[(pos + 3) % 4];
595 int points = glm::pow(2, info->power) + 1; // calculate the point count (5, 9, 17)
597 // Initialize list for floats
598 std::vector<float> meshPoints;
600 std::vector<glm::vec3> finalPoints;
602 glm::vec3* NWU = &v.bounds.NWU;
603 glm::vec3* SEL = &v.bounds.SEL;
605 for (int row = 0; row < points; row++) {
606 for (int col = 0; col < points; col++) {
607 //Generate original base points
609 float dx = (float)col / (float)(points-1); //Time values for linear interpolation
610 float dy = (float)row / (float)(points-1);
612 glm::vec3 LWR = lerp(*SW, *SE, dx);
613 glm::vec3 UPR = lerp(*NW, *NE, dx);
614 glm::vec3 P = lerp(LWR, UPR, dy); // Original point location
616 glm::vec3 offset = info->normals[col][row] * info->distances[col][row]; // Calculate offset
617 P = P + offset; //Add offset to P
619 finalPoints.push_back(P);
621 //Recompute bounds while we are at it
622 NWU->x = glm::max(-P.x, NWU->x);
623 NWU->y = glm::max(P.z, NWU->y);
624 NWU->z = glm::max(P.y, NWU->z);
626 SEL->x = glm::min(-P.x, SEL->x);
627 SEL->y = glm::min(P.z, SEL->y);
628 SEL->z = glm::min(P.y, SEL->z);
632 /* TESTING TRIANGLES */
633 meshPoints.push_back(-P.x);
634 meshPoints.push_back(P.z);
635 meshPoints.push_back(P.y);
636 meshPoints.push_back(0);
637 meshPoints.push_back(0);
638 meshPoints.push_back(1);
640 meshPoints.push_back(-P.x);
641 meshPoints.push_back(P.z);
642 meshPoints.push_back(P.y + 8.0f);
643 meshPoints.push_back(0);
644 meshPoints.push_back(0);
645 meshPoints.push_back(1);
648 meshPoints.push_back(-P.x + 8.0f);
649 meshPoints.push_back(P.z);
650 meshPoints.push_back(P.y + 8.0f);
651 meshPoints.push_back(0);
652 meshPoints.push_back(0);
653 meshPoints.push_back(1);
658 for (int row = 0; row < points - 1; row++) {
659 for (int col = 0; col < points - 1; col++) {
661 // Gather point pointers
663 glm::vec3* SW = &finalPoints[((row + 0) * points) + (col + 0)];
664 glm::vec3* SE = &finalPoints[((row + 0) * points) + (col + 1)];
666 glm::vec3* NW = &finalPoints[((row + 1) * points) + (col + 0)];
667 glm::vec3* NE = &finalPoints[((row + 1) * points) + (col + 1)];
669 if (i_condition++ % 2 == 0) {//Condition 0
670 meshPoints.push_back(-SW->x);
671 meshPoints.push_back(SW->z);
672 meshPoints.push_back(SW->y);
673 meshPoints.push_back(0);
674 meshPoints.push_back(0);
675 meshPoints.push_back(1);
676 meshPoints.push_back(-NW->x);
677 meshPoints.push_back(NW->z);
678 meshPoints.push_back(NW->y);
679 meshPoints.push_back(0);
680 meshPoints.push_back(0);
681 meshPoints.push_back(1);
682 meshPoints.push_back(-NE->x);
683 meshPoints.push_back(NE->z);
684 meshPoints.push_back(NE->y);
685 meshPoints.push_back(0);
686 meshPoints.push_back(0);
687 meshPoints.push_back(1);
689 meshPoints.push_back(-SW->x);
690 meshPoints.push_back(SW->z);
691 meshPoints.push_back(SW->y);
692 meshPoints.push_back(0);
693 meshPoints.push_back(0);
694 meshPoints.push_back(1);
695 meshPoints.push_back(-NE->x);
696 meshPoints.push_back(NE->z);
697 meshPoints.push_back(NE->y);
698 meshPoints.push_back(0);
699 meshPoints.push_back(0);
700 meshPoints.push_back(1);
701 meshPoints.push_back(-SE->x);
702 meshPoints.push_back(SE->z);
703 meshPoints.push_back(SE->y);
704 meshPoints.push_back(0);
705 meshPoints.push_back(0);
706 meshPoints.push_back(1);
709 meshPoints.push_back(-SW->x);
710 meshPoints.push_back(SW->z);
711 meshPoints.push_back(SW->y);
712 meshPoints.push_back(0);
713 meshPoints.push_back(0);
714 meshPoints.push_back(1);
715 meshPoints.push_back(-NW->x);
716 meshPoints.push_back(NW->z);
717 meshPoints.push_back(NW->y);
718 meshPoints.push_back(0);
719 meshPoints.push_back(0);
720 meshPoints.push_back(1);
721 meshPoints.push_back(-SE->x);
722 meshPoints.push_back(SE->z);
723 meshPoints.push_back(SE->y);
724 meshPoints.push_back(0);
725 meshPoints.push_back(0);
726 meshPoints.push_back(1);
728 meshPoints.push_back(-NW->x);
729 meshPoints.push_back(NW->z);
730 meshPoints.push_back(NW->y);
731 meshPoints.push_back(0);
732 meshPoints.push_back(0);
733 meshPoints.push_back(1);
734 meshPoints.push_back(-NE->x);
735 meshPoints.push_back(NE->z);
736 meshPoints.push_back(NE->y);
737 meshPoints.push_back(0);
738 meshPoints.push_back(0);
739 meshPoints.push_back(1);
740 meshPoints.push_back(-SE->x);
741 meshPoints.push_back(SE->z);
742 meshPoints.push_back(SE->y);
743 meshPoints.push_back(0);
744 meshPoints.push_back(0);
745 meshPoints.push_back(1);
751 Mesh* _glMesh = new Mesh(meshPoints);
752 info->glMesh = _glMesh;
758 auto elapsed = std::chrono::high_resolution_clock::now() - start;
759 long long milliseconds = std::chrono::duration_cast<std::chrono::milliseconds>(elapsed).count();
761 std::cout << "Displacement computation: " << milliseconds << "ms" << std::endl;
765 for (int i = 0; i < this->solids.size(); i++) {
766 delete this->solids[i].mesh;
767 this->solids[i].mesh = NULL;
770 for (auto i : this->entities) {
771 for (auto m : i.internal_solids) {
783 BoundingBox getSolidListBounds(std::vector<Solid*> list) {
784 if (list.size() <= 0) return BoundingBox();
787 bounds.NWU = list[0]->bounds.NWU;
788 bounds.SEL = list[0]->bounds.SEL;
790 for (auto && iSolid : list) {
791 if (iSolid->bounds.NWU.z > bounds.NWU.z) bounds.NWU.z = iSolid->bounds.NWU.z;
792 if (iSolid->bounds.NWU.y > bounds.NWU.y) bounds.NWU.y = iSolid->bounds.NWU.y;
793 if (iSolid->bounds.NWU.x > bounds.NWU.x) bounds.NWU.x = iSolid->bounds.NWU.x;
795 if (iSolid->bounds.SEL.z < bounds.SEL.z) bounds.SEL.z = iSolid->bounds.SEL.z;
796 if (iSolid->bounds.SEL.y < bounds.SEL.y) bounds.SEL.y = iSolid->bounds.SEL.y;
797 if (iSolid->bounds.SEL.x < bounds.SEL.x) bounds.SEL.x = iSolid->bounds.SEL.x;