LCOV - code coverage report
Current view: top level - engine/model - obj.cpp (source / functions) Coverage Total Hit
Test: Libprimis Test Coverage Lines: 2.5 % 159 4
Test Date: 2026-05-09 04:28:55 Functions: 18.2 % 11 2

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

Generated by: LCOV version 2.0-1