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