LCOV - code coverage report
Current view: top level - engine/model - obj.cpp (source / functions) Coverage Total Hit
Test: Libprimis Test Coverage Lines: 2.5 % 157 4
Test Date: 2025-04-16 12:09:02 Functions: 20.0 % 10 2

            Line data    Source code
       1              : /* obj.cpp: wavefront model support
       2              :  *
       3              :  * Libprimis supports the Wavefront (obj) model format for simple static models.
       4              :  * This file contains the implementation functions, while the class for the obj
       5              :  * model type is located in obj.h.
       6              :  */
       7              : #include "../libprimis-headers/cube.h"
       8              : #include "../../shared/geomexts.h"
       9              : #include "../../shared/glemu.h"
      10              : #include "../../shared/glexts.h"
      11              : #include "../../shared/stream.h"
      12              : 
      13              : #include <optional>
      14              : #include <memory>
      15              : #include <format>
      16              : 
      17              : #include "render/rendergl.h"
      18              : #include "render/rendermodel.h"
      19              : #include "render/renderwindow.h"
      20              : #include "render/shader.h"
      21              : #include "render/shaderparam.h"
      22              : #include "render/texture.h"
      23              : 
      24              : #include "interface/console.h"
      25              : #include "interface/control.h"
      26              : #include "interface/cs.h"
      27              : 
      28              : #include "world/entities.h"
      29              : #include "world/octaworld.h"
      30              : #include "world/bih.h"
      31              : 
      32              : #include "model.h"
      33              : #include "ragdoll.h"
      34              : #include "animmodel.h"
      35              : #include "vertmodel.h"
      36              : 
      37              : #include "obj.h"
      38              : 
      39              : #include "interface/console.h"
      40              : 
      41              : vertcommands<obj> obj::objcommands;
      42              : 
      43            0 : obj::obj(std::string name) : vertloader(name)
      44              : {
      45            0 : }
      46              : 
      47           59 : const char *obj::formatname()
      48              : {
      49           59 :     return "obj";
      50              : }
      51              : 
      52            1 : bool obj::cananimate()
      53              : {
      54            1 :     return false;
      55              : }
      56              : 
      57            0 : bool obj::flipy() const
      58              : {
      59            0 :     return true;
      60              : }
      61              : 
      62            0 : int obj::type() const
      63              : {
      64            0 :     return MDL_OBJ;
      65              : }
      66              : 
      67            0 : bool obj::skeletal() const
      68              : {
      69            0 :     return false;
      70              : }
      71              : 
      72            0 : bool obj::objmeshgroup::load(const char *filename, float smooth)
      73              : {
      74            0 :     int len = std::strlen(filename);
      75            0 :     if(len < 4 || strcasecmp(&filename[len-4], ".obj")) //note: strcasecmp is not in std namespace, it is POSIX
      76              :     {
      77            0 :         return false;
      78              :     }
      79            0 :     stream *file = openfile(filename, "rb");
      80            0 :     if(!file)
      81              :     {
      82            0 :         return false;
      83              :     }
      84            0 :     name = filename;
      85            0 :     numframes = 1;
      86            0 :     std::array<std::vector<vec>, 3> attrib;
      87              :     char buf[512];
      88            0 :     std::unordered_map<ivec, uint> verthash;
      89            0 :     std::vector<vert> verts;
      90            0 :     std::vector<tcvert> tcverts;
      91            0 :     std::vector<tri> tris;
      92              : 
      93            0 :     string meshname = "";
      94            0 :     vertmesh *curmesh = nullptr;
      95            0 :     while(file->getline(buf, sizeof(buf)))
      96              :     {
      97            0 :         char *c = buf;
      98            0 :         while(std::isspace(*c))
      99              :         {
     100            0 :             c++;
     101              :         }
     102            0 :         switch(*c)
     103              :         {
     104            0 :             case '#':
     105              :             {
     106            0 :                 continue;
     107              :             }
     108            0 :             case 'v':
     109              :             {
     110            0 :                 if(std::isspace(c[1]))
     111              :                 {
     112            0 :                     parsevert(c, attrib[0]);
     113              :                 }
     114            0 :                 else if(c[1]=='t')
     115              :                 {
     116            0 :                     parsevert(c, attrib[1]);
     117              :                 }
     118            0 :                 else if(c[1]=='n')
     119              :                 {
     120            0 :                     parsevert(c, attrib[2]);
     121              :                 }
     122            0 :                 break;
     123              :             }
     124            0 :             case 'g':
     125              :             {
     126            0 :                 while(std::isalpha(*c))
     127              :                 {
     128            0 :                     c++;
     129              :                 }
     130            0 :                 while(std::isspace(*c))
     131              :                 {
     132            0 :                     c++;
     133              :                 }
     134            0 :                 char *name = c;
     135            0 :                 size_t namelen = std::strlen(name);
     136            0 :                 while(namelen > 0 && std::isspace(name[namelen-1]))
     137              :                 {
     138            0 :                     namelen--;
     139              :                 }
     140            0 :                 copystring(meshname, name, std::min(namelen+1, sizeof(meshname)));
     141            0 :                 if(curmesh)
     142              :                 {
     143            0 :                     flushmesh(*curmesh, verts, tcverts, tris, attrib[2], smooth);
     144              :                 }
     145            0 :                 curmesh = nullptr;
     146            0 :                 break;
     147              :             }
     148            0 :             case 'f':
     149              :             {
     150            0 :                 if(!curmesh)
     151              :                 {
     152              :                     //startmesh
     153            0 :                     vertmesh &m = *new vertmesh(meshname[0] ? std::string(meshname) : "", this);
     154            0 :                     meshes.push_back(&m);
     155            0 :                     curmesh = &m;
     156            0 :                     verthash.clear();
     157            0 :                     verts.clear();
     158            0 :                     tcverts.clear();
     159            0 :                     tris.clear();
     160              :                 }
     161            0 :                 std::optional<uint> v0 = std::nullopt,
     162            0 :                                     v1 = std::nullopt;
     163            0 :                 while(std::isalpha(*c))
     164              :                 {
     165            0 :                     c++;
     166              :                 }
     167              :                 for(;;)
     168              :                 {
     169            0 :                     while(std::isspace(*c))
     170              :                     {
     171            0 :                         c++;
     172              :                     }
     173            0 :                     if(!*c)
     174              :                     {
     175            0 :                         break;
     176              :                     }
     177            0 :                     ivec vkey(-1, -1, -1);
     178            0 :                     for(int i = 0; i < 3; ++i)
     179              :                     {
     180            0 :                         vkey[i] = std::strtol(c, &c, 10);
     181            0 :                         if(vkey[i] < 0)
     182              :                         {
     183            0 :                             vkey[i] = attrib[i].size() + vkey[i];
     184              :                         }
     185              :                         else
     186              :                         {
     187            0 :                             vkey[i]--;
     188              :                         }
     189            0 :                         if(!(attrib[i].size() > static_cast<uint>(vkey[i])))
     190              :                         {
     191            0 :                             vkey[i] = -1;
     192              :                         }
     193            0 :                         if(*c!='/')
     194              :                         {
     195            0 :                             break;
     196              :                         }
     197            0 :                         c++;
     198              :                     }
     199            0 :                     std::unordered_map<ivec, uint>::iterator itr = verthash.find(vkey);
     200              :                     uint *index;
     201            0 :                     if(itr == verthash.end())
     202              :                     {
     203            0 :                         index = &verthash[vkey];
     204            0 :                         *index = verts.size();
     205            0 :                         vert v;
     206            0 :                         v.pos = vkey.x < 0 ? vec(0, 0, 0) : attrib[0][vkey.x];
     207            0 :                         v.pos = vec(v.pos.z, -v.pos.x, v.pos.y);
     208            0 :                         v.norm = vkey.z < 0 ? vec(0, 0, 0) : attrib[2][vkey.z];
     209            0 :                         v.norm = vec(v.norm.z, -v.norm.x, v.norm.y);
     210            0 :                         verts.push_back(std::move(v));
     211            0 :                         tcverts.push_back({vkey.y < 0 ? vec2(0, 0) : vec2(attrib[1][vkey.y].x, 1-attrib[1][vkey.y].y)});
     212              :                     }
     213              :                     else
     214              :                     {
     215            0 :                         index = &(*itr).second;
     216              :                     }
     217            0 :                     if(!v0)
     218              :                     {
     219            0 :                         v0 = *index;
     220              :                     }
     221            0 :                     else if(!v1)
     222              :                     {
     223            0 :                         v1 = *index;
     224              :                     }
     225              :                     else
     226              :                     {
     227            0 :                         tris.push_back({*index, v1.value(), v0.value()});
     228            0 :                         v1 = *index;
     229              :                     }
     230            0 :                 }
     231            0 :                 break;
     232              :             }
     233            0 :         }
     234              :     }
     235            0 :     if(curmesh)
     236              :     {
     237            0 :         flushmesh(*curmesh, verts, tcverts, tris, attrib[2], smooth);
     238              :     }
     239            0 :     delete file;
     240            0 :     return true;
     241            0 : }
     242              : 
     243            0 : void obj::objmeshgroup::parsevert(char *s, std::vector<vec> &out)
     244              : {
     245            0 :     out.emplace_back(0, 0, 0);
     246            0 :     vec &v = out.back();
     247            0 :     while(std::isalpha(*s))
     248              :     {
     249            0 :         s++;
     250              :     }
     251            0 :     for(int i = 0; i < 3; ++i)
     252              :     {
     253            0 :         v[i] = std::strtod(s, &s);
     254            0 :         while(std::isspace(*s))
     255              :         {
     256            0 :             s++;
     257              :         }
     258            0 :         if(!*s)
     259              :         {
     260            0 :             break;
     261              :         }
     262              :     }
     263            0 : }
     264              : 
     265            0 : void obj::objmeshgroup::flushmesh(vertmesh &curmesh,
     266              :                                   const std::vector<vert> &verts,
     267              :                                   const std::vector<tcvert> &tcverts,
     268              :                                   const std::vector<tri> &tris,
     269              :                                   const std::vector<vec> &attrib,
     270              :                                   float smooth)
     271              : {
     272            0 :     curmesh.numverts = verts.size();
     273            0 :     if(verts.size())
     274              :     {
     275            0 :         curmesh.verts = new vert[verts.size()];
     276            0 :         std::memcpy(curmesh.verts, verts.data(), verts.size()*sizeof(vert));
     277            0 :         curmesh.tcverts = new tcvert[verts.size()];
     278            0 :         std::memcpy(curmesh.tcverts, tcverts.data(), tcverts.size()*sizeof(tcvert));
     279              :     }
     280            0 :     curmesh.numtris = tris.size();
     281            0 :     if(tris.size())
     282              :     {
     283            0 :         curmesh.tris = new tri[tris.size()];
     284            0 :         std::memcpy(curmesh.tris, tris.data(), tris.size()*sizeof(tri));
     285              :     }
     286            0 :     if(attrib.empty())
     287              :     {
     288            0 :         if(smooth <= 1)
     289              :         {
     290            0 :             curmesh.smoothnorms(smooth);
     291              :         }
     292              :         else
     293              :         {
     294            0 :             curmesh.buildnorms();
     295              :         }
     296              :     }
     297            0 :     curmesh.calctangents();
     298            0 : }
     299              : 
     300            0 : bool obj::loaddefaultparts()
     301              : {
     302            0 :     part &mdl = addpart();
     303            0 :     std::string pname = parentdir(modelname().c_str());
     304            0 :     std::string name1 = std::format("{}{}/tris.obj", modelpath, modelname());
     305            0 :     mdl.meshes = sharemeshes(path(name1.data()));
     306            0 :     if(!mdl.meshes)
     307              :     {
     308            0 :         std::string name2 = std::format("{}{}/tris.obj", modelpath, pname);
     309            0 :         mdl.meshes = sharemeshes(path(name2.data()));
     310            0 :         if(!mdl.meshes)
     311              :         {
     312            0 :             return false;
     313              :         }
     314            0 :     }
     315              :     Texture *tex, *masks;
     316            0 :     loadskin(modelname(), pname, tex, masks);
     317            0 :     mdl.initskins(tex, masks);
     318            0 :     if(tex==notexture)
     319              :     {
     320            0 :         conoutf("could not load model skin for %s", name1.c_str());
     321              :     }
     322            0 :     return true;
     323            0 : }
        

Generated by: LCOV version 2.0-1