LCOV - code coverage report
Current view: top level - engine/render - normal.cpp (source / functions) Hit Total Coverage
Test: Libprimis Test Coverage Lines: 10 223 4.5 %
Date: 2024-11-22 05:07:59 Functions: 3 16 18.8 %

          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 1.14