LCOV - code coverage report
Current view: top level - engine/render - normal.cpp (source / functions) Coverage Total Hit
Test: Libprimis Test Coverage Lines: 4.5 % 223 10
Test Date: 2025-02-21 06:59:27 Functions: 18.8 % 16 3

            Line data    Source code
       1              : /* normal.cpp: cube geometry normal interpolation
       2              :  *
       3              :  * cube geometry in the libprimis engine is faceted, only allowing 8 ticks of
       4              :  * movement; as a result, normal vectors of geometry are not very smooth
       5              :  *
       6              :  * to resolve this, adjacent cube faces with low differences in their angle can
       7              :  * have their faces "merged" by interpolating the normal maps of their respective
       8              :  * faces
       9              :  *
      10              :  * this is controlled by the lerp variables and is generally uniformly done for
      11              :  * all geometry on the map; see `lerpangle` for the threshold variable
      12              :  */
      13              : #include "../libprimis-headers/cube.h"
      14              : #include "../../shared/geomexts.h"
      15              : 
      16              : #include "octarender.h"
      17              : 
      18              : #include "world/octaworld.h"
      19              : #include "world/world.h"
      20              : 
      21              : struct normalkey
      22              : {
      23              :     vec pos;
      24              :     int smooth;
      25              : 
      26            0 :     bool operator==(const normalkey &k) const
      27              :     {
      28            0 :         return k.pos == pos && smooth == k.smooth;
      29              :     }
      30              : };
      31              : 
      32              : template<>
      33              : struct std::hash<normalkey>
      34              : {
      35            0 :     size_t operator()(const normalkey &k) const
      36              :     {
      37              :         auto vechash = std::hash<vec>();
      38            0 :         return vechash(k.pos);
      39              :     }
      40              : };
      41              : 
      42              : namespace //internal functionality not seen by other files
      43              : {
      44              :     struct normalgroup
      45              :     {
      46              :         vec pos;
      47              :         int smooth, flat, normals, tnormals;
      48              : 
      49              :         normalgroup() : smooth(0), flat(0), normals(-1), tnormals(-1) {}
      50            0 :         normalgroup(const normalkey &key) : pos(key.pos), smooth(key.smooth), flat(0), normals(-1), tnormals(-1) {}
      51              :     };
      52              : 
      53              :     struct normal
      54              :     {
      55              :         int next;
      56              :         vec surface;
      57              :     };
      58              : 
      59              :     struct tnormal
      60              :     {
      61              :         int next;
      62              :         float offset;
      63              :         int normals[2];
      64              :         normalgroup *groups[2];
      65              :     };
      66              : 
      67              :     std::unordered_map<normalkey, normalgroup> normalgroups;
      68              :     std::vector<normal> normals;
      69              :     std::vector<tnormal> tnormals;
      70              :     std::vector<int> smoothgroups;
      71              : 
      72              :     VARR(lerpangle, 0, 44, 180); //max angle to merge octree faces' normals smoothly
      73              : 
      74              :     bool usetnormals = true;
      75              : 
      76            0 :     int addnormal(const vec &pos, int smooth, const vec &surface)
      77              :     {
      78            0 :         normalkey key = { pos, smooth };
      79            0 :         auto itr = normalgroups.find(key);
      80            0 :         if(itr == normalgroups.end())
      81              :         {
      82            0 :             itr = normalgroups.insert( { key, normalgroup(key) } ).first;
      83              :         }
      84            0 :         normal n;
      85            0 :         n.next = (*itr).second.normals;
      86            0 :         n.surface = surface;
      87            0 :         normals.push_back(n);
      88            0 :         return (*itr).second.normals = normals.size()-1;
      89              :     }
      90              : 
      91            0 :     void addtnormal(const vec &pos, int smooth, float offset, int normal1, int normal2, const vec &pos1, const vec &pos2)
      92              :     {
      93            0 :         normalkey key = { pos, smooth };
      94            0 :         auto itr = normalgroups.find(key);
      95            0 :         if(itr == normalgroups.end())
      96              :         {
      97            0 :             itr = normalgroups.insert( { key, normalgroup(key) } ).first;
      98              :         }
      99              :         tnormal n;
     100            0 :         n.next = (*itr).second.tnormals;
     101            0 :         n.offset = offset;
     102            0 :         n.normals[0] = normal1;
     103            0 :         n.normals[1] = normal2;
     104            0 :         normalkey key1 = { pos1, smooth },
     105            0 :                   key2 = { pos2, smooth };
     106            0 :         n.groups[0] = &((*normalgroups.find(key1)).second);
     107            0 :         n.groups[1] = &((*normalgroups.find(key2)).second);
     108            0 :         tnormals.push_back(n);
     109            0 :         (*itr).second.tnormals = tnormals.size()-1;
     110            0 :     }
     111              : 
     112            0 :     int addnormal(const vec &pos, int smooth, int axis)
     113              :     {
     114            0 :         normalkey key = { pos, smooth };
     115            0 :         auto itr = normalgroups.find(key);
     116            0 :         if(itr == normalgroups.end())
     117              :         {
     118            0 :             itr = normalgroups.insert( { key, normalgroup(key) } ).first;
     119              :         }
     120            0 :         (*itr).second.flat += 1<<(4*axis);
     121            0 :         return axis - 6;
     122              :     }
     123              : 
     124            0 :     void findnormal(const normalgroup &g, float lerpthreshold, const vec &surface, vec &v)
     125              :     {
     126            0 :         v = vec(0, 0, 0);
     127            0 :         int total = 0;
     128              :         //check if abs value of x component of the surface normal is greater than the lerp threshold
     129              :         //note the assignments to the int n are bitshifted to pack all three axes within a single int
     130            0 :         if(surface.x >= lerpthreshold)
     131              :         {
     132            0 :             int n = (g.flat>>4)&0xF;
     133            0 :             v.x += n; total += n;
     134              :         }
     135            0 :         else if(surface.x <= -lerpthreshold)
     136              :         {
     137            0 :             int n = g.flat&0xF;
     138            0 :             v.x -= n;
     139            0 :             total += n;
     140              :         }
     141              :         //ditto y component
     142            0 :         if(surface.y >= lerpthreshold)
     143              :         {
     144            0 :             int n = (g.flat>>12)&0xF;
     145            0 :             v.y += n;
     146            0 :             total += n;
     147              :         }
     148            0 :         else if(surface.y <= -lerpthreshold)
     149              :         {
     150            0 :             int n = (g.flat>>8)&0xF;
     151            0 :             v.y -= n;
     152            0 :             total += n;
     153              :         }
     154              :         //ditto z component
     155            0 :         if(surface.z >= lerpthreshold)
     156              :         {
     157            0 :             int n = (g.flat>>20)&0xF;
     158            0 :             v.z += n;
     159            0 :             total += n;
     160              :         }
     161            0 :         else if(surface.z <= -lerpthreshold)
     162              :         {
     163            0 :             int n = (g.flat>>16)&0xF;
     164            0 :             v.z -= n;
     165            0 :             total += n;
     166              :         }
     167              : 
     168            0 :         for(int cur = g.normals; cur >= 0;)
     169              :         {
     170            0 :             normal &o = normals[cur];
     171            0 :             if(o.surface.dot(surface) >= lerpthreshold)
     172              :             {
     173            0 :                 v.add(o.surface);
     174            0 :                 total++;
     175              :             }
     176            0 :             cur = o.next;
     177              :         }
     178            0 :         if(total > 1)
     179              :         {
     180            0 :             v.normalize();
     181              :         }
     182            0 :         else if(!total)
     183              :         {
     184            0 :             v = surface;
     185              :         }
     186            0 :     }
     187              : 
     188            0 :     bool findtnormal(const normalgroup &g, float lerpthreshold, const vec &surface, vec &v)
     189              :     {
     190            0 :         float bestangle = lerpthreshold;
     191            0 :         const tnormal *bestnorm = nullptr;
     192            0 :         for(int cur = g.tnormals; cur >= 0;)
     193              :         {
     194            0 :             const tnormal &o = tnormals[cur];
     195              :             const std::array<vec, 6> flats = { vec(-1,  0,  0),
     196              :                                                vec( 1,  0,  0),
     197              :                                                vec( 0, -1,  0),
     198              :                                                vec( 0,  1,  0),
     199              :                                                vec( 0,  0, -1),
     200            0 :                                                vec( 0,  0,  1) };
     201            0 :             vec n1 = o.normals[0] < 0 ? flats[o.normals[0]+6] : normals[o.normals[0]].surface,
     202            0 :                 n2 = o.normals[1] < 0 ? flats[o.normals[1]+6] : normals[o.normals[1]].surface,
     203            0 :                 nt;
     204            0 :             nt.lerp(n1, n2, o.offset).normalize();
     205            0 :             float tangle = nt.dot(surface);
     206            0 :             if(tangle >= bestangle)
     207              :             {
     208            0 :                 bestangle = tangle;
     209            0 :                 bestnorm = &o;
     210              :             }
     211            0 :             cur = o.next;
     212              :         }
     213            0 :         if(!bestnorm)
     214              :         {
     215            0 :             return false;
     216              :         }
     217            0 :         vec n1, n2;
     218            0 :         findnormal(*bestnorm->groups[0], lerpthreshold, surface, n1);
     219            0 :         findnormal(*bestnorm->groups[1], lerpthreshold, surface, n2);
     220            0 :         v.lerp(n1, n2, bestnorm->offset).normalize();
     221            0 :         return true;
     222              :     }
     223              : 
     224              :     VARR(lerpsubdiv, 0, 2, 4);      //Linear intERPolation SUBDIVisions
     225              :     VARR(lerpsubdivsize, 4, 4, 128);//Linear intERPolation SUBDIVision cube SIZE
     226              : 
     227            0 :     void addnormals(const cube &c, const ivec &o, int size)
     228              :     {
     229            0 :         if(c.children)
     230              :         {
     231            0 :             size >>= 1;
     232            0 :             for(size_t i = 0; i < c.children->size(); ++i)
     233              :             {
     234            0 :                 addnormals((*c.children)[i], ivec(i, o, size), size);
     235              :             }
     236            0 :             return;
     237              :         }
     238            0 :         else if(c.isempty())
     239              :         {
     240            0 :             return;
     241              :         }
     242            0 :         vec pos[Face_MaxVerts];
     243              :         int norms[Face_MaxVerts],
     244            0 :             tj = usetnormals && c.ext ? c.ext->tjoints : -1, vis;
     245            0 :         for(int i = 0; i < 6; ++i)
     246              :         {
     247            0 :             if((vis = visibletris(c, i, o, size)))
     248              :             {
     249            0 :                 if(c.texture[i] == Default_Sky)
     250              :                 {
     251            0 :                     continue;
     252              :                 }
     253              : 
     254            0 :                 std::array<vec, 2> planes;
     255            0 :                 int numverts = c.ext ? c.ext->surfaces[i].numverts&Face_MaxVerts : 0,
     256            0 :                     convex = 0,
     257            0 :                     numplanes = 0;
     258            0 :                 if(numverts)
     259              :                 {
     260            0 :                     vertinfo *verts = c.ext->verts() + c.ext->surfaces[i].verts;
     261            0 :                     vec vo(static_cast<ivec>(o).mask(~0xFFF));
     262            0 :                     for(int j = 0; j < numverts; ++j)
     263              :                     {
     264            0 :                         vertinfo &v = verts[j];
     265            0 :                         pos[j] = vec(v.x, v.y, v.z).mul(1.0f/8).add(vo);
     266              :                     }
     267            0 :                     if(!(c.merged&(1<<i)) && !flataxisface(c, i))
     268              :                     {
     269            0 :                         convex = faceconvexity(verts, numverts, size);
     270              :                     }
     271              :                 }
     272            0 :                 else if(c.merged&(1<<i))
     273              :                 {
     274            0 :                     continue;
     275              :                 }
     276              :                 else
     277              :                 {
     278            0 :                     std::array<ivec, 4> v;
     279            0 :                     genfaceverts(c, i, v);
     280            0 :                     if(!flataxisface(c, i))
     281              :                     {
     282            0 :                         convex = faceconvexity(v);
     283              :                     }
     284            0 :                     int order = vis&4 || convex < 0 ? 1 : 0;
     285            0 :                     vec vo(o);
     286            0 :                     pos[numverts++] = static_cast<vec>(v[order]).mul(size/8.0f).add(vo);
     287            0 :                     if(vis&1)
     288              :                     {
     289            0 :                         pos[numverts++] = static_cast<vec>(v[order+1]).mul(size/8.0f).add(vo);
     290              :                     }
     291            0 :                     pos[numverts++] = static_cast<vec>(v[order+2]).mul(size/8.0f).add(vo);
     292            0 :                     if(vis&2)
     293              :                     {
     294            0 :                         pos[numverts++] = static_cast<vec>(v[(order+3)&3]).mul(size/8.0f).add(vo);
     295              :                     }
     296              :                 }
     297              : 
     298            0 :                 if(!flataxisface(c, i))
     299              :                 {
     300            0 :                     planes[numplanes++].cross(pos[0], pos[1], pos[2]).normalize();
     301            0 :                     if(convex)
     302              :                     {
     303            0 :                         planes[numplanes++].cross(pos[0], pos[2], pos[3]).normalize();
     304              :                     }
     305              :                 }
     306              : 
     307            0 :                 const VSlot &vslot = lookupvslot(c.texture[i], false);
     308            0 :                 int smooth = vslot.slot->smooth;
     309              : 
     310            0 :                 if(!numplanes)
     311              :                 {
     312            0 :                     for(int k = 0; k < numverts; ++k)
     313              :                     {
     314            0 :                         norms[k] = addnormal(pos[k], smooth, i);
     315              :                     }
     316              :                 }
     317            0 :                 else if(numplanes==1)
     318              :                 {
     319            0 :                     for(int k = 0; k < numverts; ++k)
     320              :                     {
     321            0 :                         norms[k] = addnormal(pos[k], smooth, planes[0]);
     322              :                     }
     323              :                 }
     324              :                 else
     325              :                 {
     326            0 :                     vec avg = vec(planes[0]).add(planes[1]).normalize();
     327            0 :                     norms[0] = addnormal(pos[0], smooth, avg);
     328            0 :                     norms[1] = addnormal(pos[1], smooth, planes[0]);
     329            0 :                     norms[2] = addnormal(pos[2], smooth, avg);
     330            0 :                     for(int k = 3; k < numverts; k++)
     331              :                     {
     332            0 :                         norms[k] = addnormal(pos[k], smooth, planes[1]);
     333              :                     }
     334              :                 }
     335              : 
     336            0 :                 while(tj >= 0 && tjoints[tj].edge < i*(Face_MaxVerts+1))
     337              :                 {
     338            0 :                     tj = tjoints[tj].next;
     339              :                 }
     340            0 :                 while(tj >= 0 && tjoints[tj].edge < (i+1)*(Face_MaxVerts+1))
     341              :                 {
     342            0 :                     int edge = tjoints[tj].edge,
     343            0 :                         e1 = edge%(Face_MaxVerts+1),
     344            0 :                         e2 = (e1+1)%numverts;
     345            0 :                     const vec &v1 = pos[e1],
     346            0 :                               &v2 = pos[e2];
     347            0 :                     ivec d(vec(v2).sub(v1).mul(8));
     348            0 :                     int axis = std::abs(d.x) > std::abs(d.y) ? (std::abs(d.x) > std::abs(d.z) ? 0 : 2) : (std::abs(d.y) > std::abs(d.z) ? 1 : 2);
     349            0 :                     if(d[axis] < 0)
     350              :                     {
     351            0 :                         d.neg();
     352              :                     }
     353            0 :                     reduceslope(d);
     354            0 :                     int origin  =  static_cast<int>(std::min(v1[axis], v2[axis])*8)&~0x7FFF,
     355            0 :                         offset1 = (static_cast<int>(v1[axis]*8) - origin) / d[axis],
     356            0 :                         offset2 = (static_cast<int>(v2[axis]*8) - origin) / d[axis];
     357            0 :                     vec o = vec(v1).sub(vec(d).mul(offset1/8.0f)),
     358            0 :                         n1, n2;
     359            0 :                     float doffset = 1.0f / (offset2 - offset1);
     360            0 :                     while(tj >= 0)
     361              :                     {
     362            0 :                         tjoint &t = tjoints[tj];
     363            0 :                         if(t.edge != edge)
     364              :                         {
     365            0 :                             break;
     366              :                         }
     367            0 :                         float offset = (t.offset - offset1) * doffset;
     368            0 :                         vec tpos = vec(d).mul(t.offset/8.0f).add(o);
     369            0 :                         addtnormal(tpos, smooth, offset, norms[e1], norms[e2], v1, v2);
     370            0 :                         tj = t.next;
     371              :                     }
     372              :                 }
     373              :             }
     374              :         }
     375              :     }
     376              : }
     377              : 
     378              : /* externally relevant functionality */
     379              : ///////////////////////////////////////
     380              : 
     381            0 : void findnormal(const vec &pos, int smooth, const vec &surface, vec &v)
     382              : {
     383            0 :     normalkey key = { pos, smooth };
     384            0 :     auto itr = normalgroups.find(key);
     385            0 :     if(smooth < 0)
     386              :     {
     387            0 :         smooth = 0;
     388              :     }
     389            0 :     bool usegroup = (static_cast<int>(smoothgroups.size()) > smooth) && smoothgroups[smooth] >= 0;
     390            0 :     if(itr != normalgroups.end())
     391              :     {
     392            0 :         int angle = usegroup ? smoothgroups[smooth] : lerpangle;
     393            0 :         float lerpthreshold = cos360(angle) - 1e-5f;
     394            0 :         if((*itr).second.tnormals < 0 || !findtnormal((*itr).second, lerpthreshold, surface, v))
     395              :         {
     396            0 :             findnormal((*itr).second, lerpthreshold, surface, v);
     397              :         }
     398              :     }
     399              :     else
     400              :     {
     401            0 :         v = surface;
     402              :     }
     403            0 : }
     404              : 
     405            0 : void cubeworld::calcnormals(bool lerptjoints)
     406              : {
     407            0 :     usetnormals = lerptjoints;
     408            0 :     if(usetnormals)
     409              :     {
     410            0 :         findtjoints();
     411              :     }
     412            0 :     for(size_t i = 0; i < worldroot->size(); ++i)
     413              :     {
     414            0 :         addnormals((*worldroot)[i], ivec(i, ivec(0, 0, 0), mapsize()/2), mapsize()/2);
     415              :     }
     416            0 : }
     417              : 
     418            0 : void clearnormals()
     419              : {
     420            0 :     normalgroups.clear();
     421            0 :     normals.clear();
     422            0 :     tnormals.clear();
     423            0 : }
     424              : 
     425            0 : void resetsmoothgroups()
     426              : {
     427            0 :     smoothgroups.clear();
     428            0 : }
     429              : 
     430              : static constexpr int maxsmoothgroups = 10000;
     431              : //returns the smoothgroup at the idth location in the smoothgroups vector
     432              : //returns -1 (failure) if you try to ask for an id greater than 10,000
     433            1 : int smoothangle(int id, int angle)
     434              : {
     435            1 :     if(id < 0)
     436              :     {
     437            0 :         id = smoothgroups.size();
     438              :     }
     439            1 :     if(id >= maxsmoothgroups)
     440              :     {
     441            0 :         return -1;
     442              :     }
     443            2 :     while(static_cast<int>(smoothgroups.size()) <= id)
     444              :     {
     445            1 :         smoothgroups.push_back(-1);
     446              :     }
     447            1 :     if(angle >= 0)
     448              :     {
     449            0 :         smoothgroups[id] = std::min(angle, 180);
     450              :     }
     451            1 :     return id;
     452              : }
     453              : 
     454            1 : void initnormalcmds()
     455              : {
     456            2 :     addcommand("smoothangle", reinterpret_cast<identfun>(+[] (int *id, int *angle) {intret(smoothangle(*id, *angle));}), "ib", Id_Command);
     457            1 : }
        

Generated by: LCOV version 2.0-1