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 : }
|