1 // Hyperbolic Rogue -- basic geometry
2 // Copyright (C) 2011-2019 Zeno Rogue, see 'hyper.cpp' for details
3 
4 /** \file geometry.cpp
5  *  \brief Calculation of basic, and less basic, constants in each geometry
6  */
7 
8 #include "hyper.h"
9 namespace hr {
10 
11 #if HDR
12 struct usershapelayer {
13   vector<hyperpoint> list;
14   bool sym;
15   int rots;
16   color_t color;
17   hyperpoint shift, spin;
18   ld zlevel;
19   int texture_offset;
20   PPR prio;
21   };
22 
23 extern int usershape_changes;
24 
25 static const int USERLAYERS = 32;
26 
27 struct usershape { usershapelayer d[USERLAYERS]; };
28 
29 struct hpcshape {
30   int s, e;
31   PPR prio;
32   int flags;
33   hyperpoint intester;
34   struct basic_textureinfo *tinf;
35   int texture_offset;
36   int shs, she;
clearhr::hpcshape37   void clear() { s = e = shs = she = texture_offset = 0; prio = PPR::ZERO; tinf = NULL; flags = 0; }
38   };
39 
40 #define SIDE_SLEV 0
41 #define SIDE_WTS3 3
42 #define SIDE_WALL 4
43 #define SIDE_LAKE 5
44 #define SIDE_LTOB 6
45 #define SIDE_BTOI 7
46 #define SIDE_SKY  8
47 #define SIDE_HIGH 9
48 #define SIDE_HIGH2 10
49 #define SIDE_ASHA 11
50 #define SIDE_BSHA 12
51 #define SIDEPARS  13
52 
53 /** GOLDBERG_BITS controls the size of tables for Goldberg: 2*(x+y) should be below (1<<GOLDBERG_BITS) */
54 
55 #ifndef GOLDBERG_BITS
56 #define GOLDBERG_BITS 5
57 #endif
58 
59 static const int GOLDBERG_LIMIT = (1<<GOLDBERG_BITS);
60 static const int GOLDBERG_MASK = (GOLDBERG_LIMIT-1);
61 
62 #ifndef BADMODEL
63 #define BADMODEL 0
64 #endif
65 
66 #ifndef WINGS
67 static const int WINGS = (BADMODEL ? 1 : 4);
68 #endif
69 
70 typedef array<hpcshape, WINGS+1> hpcshape_animated;
71 
72 extern vector<hpcshape> shPlainWall3D, shWireframe3D, shWall3D, shMiniWall3D;
73 
74 struct floorshape {
75   bool is_plain;
76   int shapeid;
77   int id;
78   int pstrength; // pattern strength in 3D
79   int fstrength; // frame strength in 3D
80   PPR prio;
81   vector<hpcshape> b, shadow, side[SIDEPARS], levels[SIDEPARS], cone[2];
82   vector<vector<hpcshape>> gpside[SIDEPARS];
floorshapehr::floorshape83   floorshape() { prio = PPR::FLOOR; pstrength = fstrength = 10; }
84   };
85 
86 struct plain_floorshape : floorshape {
87   ld rad0, rad1;
configurehr::plain_floorshape88   void configure(ld r0, ld r1) { rad0 = r0; rad1 = r1; }
89   };
90 
91 extern vector<ld> equal_weights;
92 
93 // noftype: 0 (shapeid2 is heptagonal or just use shapeid1), 1 (shapeid2 is pure heptagonal), 2 (shapeid2 is Euclidean), 3 (shapeid2 is hexagonal)
94 struct escher_floorshape : floorshape {
95   int shapeid0, shapeid1, noftype, shapeid2;
96   ld scale;
97   };
98 
99 struct basic_textureinfo {
100   int texture_id;
101   vector<glvertex> tvertices;
102   vector<glvertex> colors;
103   };
104 
105 /** additional modules can add extra shapes etc. */
106 struct gi_extension {
~gi_extensionhr::gi_extension107   virtual ~gi_extension() {}
108   };
109 
110 /** both for 'heptagon' 3D cells and subdivided 3D cells */
111 struct subcellshape {
112   /** \brief raw coordinates of vertices of all faces */
113   vector<vector<hyperpoint>> faces;
114   /** \brief raw coordinates of all vertices in one vector */
115   vector<hyperpoint> vertices_only;
116   /** \brief cooked coordinates of vertices of all faces, computed from faces as: from_cellcenter * final_coords(v) */
117   vector<vector<hyperpoint>> faces_local;
118   /** \brief cooked coordinates of all vertices in one vector */
119   vector<hyperpoint> vertices_only_local;
120   /** \brief weights -- used to generate wall shapes in some geometries, empty otherwise */
121   vector<vector<double>> weights;
122   /** the center of every raw face */
123   vector<hyperpoint> face_centers;
124   vector<vector<char>> dirdist;
125   hyperpoint cellcenter;
126   transmatrix to_cellcenter;
127   transmatrix from_cellcenter;
128   /** \brief for adjacent directions a,b, next_dir[a][b] is the next direction adjacent to a, in (counter?)clockwise order from b */
129   vector<vector<char>> next_dir;
130   /** useful in product geometries */
131   vector<hyperpoint> walltester;
132 
133   /** compute all the properties based on `faces`, for the main heptagon cellshape */
134   void compute_hept();
135 
136   /** compute all the properties based on `faces`, for subcells */
137   void compute_sub();
138 
139   /** common part of compute_hept and compute_sub */
140   void compute_common();
141   };
142 
143 /** basic geometry parameters */
144 struct geometry_information {
145 
146   /** distance from heptagon center to another heptagon center */
147   ld tessf;
148 
149   /** distance from heptagon center to adjacent cell center (either hcrossf or tessf) */
150   ld crossf;
151 
152   /** distance from heptagon center to small heptagon vertex */
153   ld hexf;
154 
155   /** distance from heptagon center to big heptagon vertex */
156   ld hcrossf;
157 
158   /** distance between adjacent hexagon vertices */
159   ld hexhexdist;
160 
161   /** distance between hexagon vertex and hexagon center */
162   ld hexvdist;
163 
164   /** distance between heptagon vertex and hexagon center (either hcrossf or something else) */
165   ld hepvdist;
166 
167   /** distance from heptagon center to heptagon vertex (either hexf or hcrossf) */
168   ld rhexf;
169 
170   /** basic parameters for 3D geometries */
171   map<int, int> close_distances;
172 
173   int loop, face, schmid;
174 
175   transmatrix spins[32], adjmoves[32];
176 
177   unique_ptr<struct subcellshape> heptshape;
178   vector<struct subcellshape> subshapes;
179 
180   ld adjcheck;
181   ld strafedist;
182 
183   ld ultra_mirror_dist, ultra_material_part, ultra_mirror_part;
184 
185   vector<transmatrix> ultra_mirrors;
186 
187   vector<pair<string, string> > rels;
188   int xp_order, r_order, rx_order;
189 
190   transmatrix full_X, full_R, full_P;
191 
192   /** for 2D geometries */
193   vector<transmatrix> heptmove, hexmove, invhexmove;
194 
195   int base_distlimit;
196 
197   /** size of the Sword (from Orb of the Sword), used in the shmup mode */
198   ld sword_size;
199   /** scale factor for the graphics of most things*/
200   ld scalefactor;
201   ld orbsize, floorrad0, floorrad1, zhexf;
202   ld corner_bonus;
203   ld hexshift;
204   ld asteroid_size[8];
205   ld wormscale;
206   ld tentacle_length;
207   /** level in product geometries */
208   ld plevel;
209   /** level for a z-step */
210   int single_step;
211   /** the number of levels in PSL */
212   int psl_steps;
213 
214   /** for binary tilings */
215   transmatrix direct_tmatrix[14];
216   transmatrix inverse_tmatrix[14];
217 
218   /** a bitmask for hr::bt::use_direct_for */
219   int use_direct;
220 
221   /** various parameters related to the 3D view */
222   ld INFDEEP, BOTTOM, HELLSPIKE, LAKE, WALL, FLOOR, STUFF,
223     SLEV[4], FLATEYE,
224     LEG0, LEG1, LEG, LEG3, GROIN, GROIN1, GHOST,
225     BODY, BODY1, BODY2, BODY3,
226     NECK1, NECK, NECK3, HEAD, HEAD1, HEAD2, HEAD3,
227     ALEG0, ALEG, ABODY, AHEAD, BIRD, LOWSKY, SKY, HIGH, HIGH2,
228     SHALLOW;
229   ld human_height, slev;
230 
231   ld eyelevel_familiar, eyelevel_human, eyelevel_dog;
232 
233 #if CAP_SHAPES
234 hpcshape
235   shSemiFloorSide[SIDEPARS],
236   shBFloor[2],
237   shWave[8][2],
238   shCircleFloor,
239   shBarrel,
240   shWall[2], shMineMark[2], shBigMineMark[2], shFan,
241   shZebra[5],
242   shSwitchDisk,
243   shTower[11],
244   shEmeraldFloor[6],
245   shSemiFeatherFloor[2],
246   shSemiFloor[2], shSemiBFloor[2], shSemiFloorShadow,
247   shMercuryBridge[2],
248   shTriheptaSpecial[14],
249   shCross, shGiantStar[2], shLake, shMirror,
250   shHalfFloor[6], shHalfMirror[3],
251   shGem[2], shStar, shDisk, shDiskT, shDiskS, shDiskM, shDiskSq, shRing,
252   shTinyBird, shTinyShark,
253   shEgg,
254   shSpikedRing, shTargetRing, shSawRing, shGearRing, shPeaceRing, shHeptaRing,
255   shSpearRing, shLoveRing,
256   shFrogRing,
257   shPowerGearRing, shProtectiveRing, shTerraRing, shMoveRing, shReserved4, shMoonDisk,
258   shDaisy, shTriangle, shNecro, shStatue, shKey, shWindArrow,
259   shGun,
260   shFigurine, shTreat,
261   shElementalShard,
262   // shBranch,
263   shIBranch, shTentacle, shTentacleX, shILeaf[3],
264   shMovestar,
265   shWolf, shYeti, shDemon, shGDemon, shEagle, shGargoyleWings, shGargoyleBody,
266   shFoxTail1, shFoxTail2,
267   shDogBody, shDogHead, shDogFrontLeg, shDogRearLeg, shDogFrontPaw, shDogRearPaw,
268   shDogTorso,
269   shHawk,
270   shCatBody, shCatLegs, shCatHead, shFamiliarHead, shFamiliarEye,
271   shWolf1, shWolf2, shWolf3,
272   shRatEye1, shRatEye2, shRatEye3,
273   shDogStripes,
274   shPBody, shPSword, shPKnife,
275   shFerocityM, shFerocityF,
276   shHumanFoot, shHumanLeg, shHumanGroin, shHumanNeck, shSkeletalFoot, shYetiFoot,
277   shMagicSword, shMagicShovel, shSeaTentacle, shKrakenHead, shKrakenEye, shKrakenEye2,
278   shArrow,
279   shPHead, shPFace, shGolemhead, shHood, shArmor,
280   shAztecHead, shAztecCap,
281   shSabre, shTurban1, shTurban2, shVikingHelmet, shRaiderHelmet, shRaiderArmor, shRaiderBody, shRaiderShirt,
282   shWestHat1, shWestHat2, shGunInHand,
283   shKnightArmor, shKnightCloak, shWightCloak,
284   shGhost, shEyes, shSlime, shJelly, shJoint, shWormHead, shTentHead, shShark, shWormSegment, shSmallWormSegment, shWormTail, shSmallWormTail,
285   shSlimeEyes, shDragonEyes, shWormEyes, shGhostEyes,
286   shMiniGhost, shMiniEyes,
287   shHedgehogBlade, shHedgehogBladePlayer,
288   shWolfBody, shWolfHead, shWolfLegs, shWolfEyes,
289   shWolfFrontLeg, shWolfRearLeg, shWolfFrontPaw, shWolfRearPaw,
290   shFemaleBody, shFemaleHair, shFemaleDress, shWitchDress,
291   shWitchHair, shBeautyHair, shFlowerHair, shFlowerHand, shSuspenders, shTrophy,
292   shBugBody, shBugArmor, shBugLeg, shBugAntenna,
293   shPickAxe, shPike, shFlailBall, shFlailTrunk, shFlailChain, shHammerHead,
294   shBook, shBookCover, shGrail,
295   shBoatOuter, shBoatInner, shCompass1, shCompass2, shCompass3,
296   shKnife, shTongue, shFlailMissile, shTrapArrow,
297   shPirateHook, shPirateHood, shEyepatch, shPirateX,
298   // shScratch,
299   shHeptaMarker, shSnowball, shHugeDisk, shSun, shNightStar, shEuclideanSky,
300   shSkeletonBody, shSkull, shSkullEyes, shFatBody, shWaterElemental,
301   shPalaceGate, shFishTail,
302   shMouse, shMouseLegs, shMouseEyes,
303   shPrincessDress, shPrinceDress,
304   shWizardCape1, shWizardCape2,
305   shBigCarpet1, shBigCarpet2, shBigCarpet3,
306   shGoatHead, shRose, shRoseItem, shThorns,
307   shRatHead, shRatTail, shRatEyes, shRatCape1, shRatCape2,
308   shWizardHat1, shWizardHat2,
309   shTortoise[13][6],
310   shDragonLegs, shDragonTail, shDragonHead, shDragonSegment, shDragonNostril,
311   shDragonWings,
312   shSolidBranch, shWeakBranch, shBead0, shBead1,
313   shBatWings, shBatBody, shBatMouth, shBatFang, shBatEye,
314   shParticle[16], shAsteroid[8],
315   shReptile[5][4],
316   shReptileBody, shReptileHead, shReptileFrontFoot, shReptileRearFoot,
317   shReptileFrontLeg, shReptileRearLeg, shReptileTail, shReptileEye,
318 
319   shTrylobite, shTrylobiteHead, shTrylobiteBody,
320   shTrylobiteFrontLeg, shTrylobiteRearLeg, shTrylobiteFrontClaw, shTrylobiteRearClaw,
321 
322   shBullBody, shBullHead, shBullHorn, shBullRearHoof, shBullFrontHoof,
323 
324   shButterflyBody, shButterflyWing, shGadflyBody, shGadflyWing, shGadflyEye,
325 
326   shTerraArmor1, shTerraArmor2, shTerraArmor3, shTerraHead, shTerraFace,
327   shJiangShi, shJiangShiDress, shJiangShiCap1, shJiangShiCap2,
328 
329   shPikeBody, shPikeEye,
330 
331   shAsymmetric,
332 
333   shPBodyOnly, shPBodyArm, shPBodyHand, shPHeadOnly,
334 
335   shDodeca;
336 
337   hpcshape shFrogRearFoot, shFrogFrontFoot, shFrogRearLeg, shFrogFrontLeg, shFrogRearLeg2, shFrogBody, shFrogEye, shFrogStripe, shFrogJumpFoot, shFrogJumpLeg;
338 
339   hpcshape_animated
340     shAnimatedEagle, shAnimatedTinyEagle, shAnimatedGadfly, shAnimatedHawk, shAnimatedButterfly,
341     shAnimatedGargoyle, shAnimatedGargoyle2, shAnimatedBat, shAnimatedBat2;
342 
343   hpcshape shTinyArrow;
344 
345   hpcshape shReserved[16];
346 
347   int orb_inner_ring; //< for shDisk* shapes, the number of vertices in the inner ring
348   int res1, res2;
349 
350   map<int, hpcshape> shPipe;
351 
352   vector<hpcshape> shPlainWall3D, shWireframe3D, shWall3D, shMiniWall3D;
353   vector<hyperpoint> walltester;
354 
355   vector<int> wallstart;
356   vector<transmatrix> raywall;
357 
358   vector<struct plain_floorshape*> all_plain_floorshapes;
359   vector<struct escher_floorshape*> all_escher_floorshapes;
360 
361   plain_floorshape
362     shFloor,
363     shMFloor, shMFloor2, shMFloor3, shMFloor4, shFullFloor,
364     shBigTriangle, shTriheptaFloor, shBigHepta;
365 
366   escher_floorshape
367     shStarFloor, shCloudFloor, shCrossFloor, shChargedFloor,
368     shSStarFloor, shOverFloor, shTriFloor, shFeatherFloor,
369     shBarrowFloor, shNewFloor, shTrollFloor, shButterflyFloor,
370     shLavaFloor, shLavaSeabed, shSeabed, shCloudSeabed,
371     shCaveSeabed, shPalaceFloor, shDemonFloor, shCaveFloor,
372     shDesertFloor, shPowerFloor, shRoseFloor, shSwitchFloor,
373     shTurtleFloor, shRedRockFloor[3], shDragonFloor;
374 
375   ld dlow_table[SIDEPARS], dhi_table[SIDEPARS], dfloor_table[SIDEPARS];
376 
377   int prehpc;
378   vector<hyperpoint> hpc;
379   bool first;
380 
381   bool validsidepar[SIDEPARS];
382 
383   vector<glvertex> ourshape;
384 #endif
385 
386   hpcshape shFullCross[2];
387   hpcshape *last;
388 
389   int SD3, SD6, SD7, S12, S14, S21, S28, S42, S36, S84;
390 
391   vector<pair<int, cell*>> walloffsets;
392 
393   vector<array<int, 3>> symmetriesAt;
394 
395   struct cellrotation_t {
396     transmatrix M;
397     vector<int> mapping;
398     int inverse_id;
399     };
400 
401   vector<cellrotation_t> cellrotations;
402 
403   #ifndef SCALETUNER
404   static constexpr
405   #endif
406   double bscale7 = 1, brot7 = 0, bscale6 = 1, brot6 = 0;
407 
408   vector<hpcshape*> allshapes;
409 
410   transmatrix shadowmulmatrix;
411 
412   map<usershapelayer*, hpcshape> ushr;
413 
414   void prepare_basics();
415   void prepare_compute3();
416   void prepare_shapes();
417   void prepare_usershapes();
418 
419   void hpcpush(hyperpoint h);
420   void hpcsquare(hyperpoint h1, hyperpoint h2, hyperpoint h3, hyperpoint h4);
421   void chasmifyPoly(double fac, double fac2, int k);
422   void shift(hpcshape& sh, double dx, double dy, double dz);
423   void initPolyForGL();
424   void extra_vertices();
425   transmatrix ddi(int a, ld x);
426   void drawTentacle(hpcshape &h, ld rad, ld var, ld divby);
427   hyperpoint hpxyzsc(double x, double y, double z);
428   hyperpoint turtlevertex(int u, double x, double y, double z);
429 
430   void bshape(hpcshape& sh, PPR prio);
431   void finishshape();
432   void bshape(hpcshape& sh, PPR prio, double shzoom, int shapeid, double bonus = 0, flagtype flags = 0);
433 
434   void copyshape(hpcshape& sh, hpcshape& orig, PPR prio);
435   void zoomShape(hpcshape& old, hpcshape& newsh, double factor, PPR prio);
436   void pushShape(usershapelayer& ds);
437   void make_sidewalls();
438   void procedural_shapes();
439   void make_wall(int id, const vector<hyperpoint> vertices, vector<ld> weights = equal_weights);
440 
441   void reserve_wall3d(int i);
442   void compute_cornerbonus();
443   void create_wall3d();
444   void configure_floorshapes();
445 
446   void init_floorshapes();
447   void bshape2(hpcshape& sh, PPR prio, int shapeid, struct matrixlist& m);
448   void bshape_regular(floorshape &fsh, int id, int sides, ld shift, ld size, cell *model);
449   void generate_floorshapes_for(int id, cell *c, int siid, int sidir);
450   void generate_floorshapes();
451   void make_floor_textures_here();
452 
453   vector<hyperpoint> get_shape(hpcshape sh);
454   void add_cone(ld z0, const vector<hyperpoint>& vh, ld z1);
455   void add_prism_sync(ld z0, vector<hyperpoint> vh0, ld z1, vector<hyperpoint> vh1);
456   void add_prism(ld z0, vector<hyperpoint> vh0, ld z1, vector<hyperpoint> vh1);
457   void shift_last(ld z);
458   void shift_shape(hpcshape& sh, ld z);
459   void shift_shape_orthogonally(hpcshape& sh, ld z);
460   void add_texture(hpcshape& sh);
461   void make_ha_3d(hpcshape& sh, bool isarmor, ld scale);
462   void make_humanoid_3d(hpcshape& sh);
463   void addtri(array<hyperpoint, 3> hs, int kind);
464   void make_armor_3d(hpcshape& sh, int kind = 1);
465   void make_foot_3d(hpcshape& sh);
466   void make_head_only();
467   void make_head_3d(hpcshape& sh);
468   void make_paw_3d(hpcshape& sh, hpcshape& legsh);
469   void make_abody_3d(hpcshape& sh, ld tail);
470   void make_ahead_3d(hpcshape& sh);
471   void make_skeletal(hpcshape& sh, ld push = 0);
472   void make_revolution(hpcshape& sh, int mx = 180, ld push = 0);
473   void make_revolution_cut(hpcshape &sh, int each = 180, ld push = 0, ld width = 99);
474   void clone_shape(hpcshape& sh, hpcshape& target);
475   void animate_bird(hpcshape& orig, hpcshape_animated& animated, ld body);
476   void slimetriangle(hyperpoint a, hyperpoint b, hyperpoint c, ld rad, int lev);
477   void balltriangle(hyperpoint a, hyperpoint b, hyperpoint c, ld rad, int lev);
478   void make_ball(hpcshape& sh, ld rad, int lev);
479   void make_star(hpcshape& sh, ld rad);
480   void make_euclidean_sky();
481   void adjust_eye(hpcshape& eye, hpcshape head, ld shift_eye, ld shift_head, int q, ld zoom=1);
482   void shift_last_straight(ld z);
483   void queueball(const transmatrix& V, ld rad, color_t col, eItem what);
484   void make_shadow(hpcshape& sh);
485   void make_3d_models();
486 
487   /* Goldberg parameters */
488   #if CAP_GP
489   struct gpdata_t {
490     vector<array<array<array<transmatrix, 6>, GOLDBERG_LIMIT>, GOLDBERG_LIMIT>> Tf;
491     transmatrix corners;
492     ld alpha;
493     int area;
494     int pshid[3][8][GOLDBERG_LIMIT][GOLDBERG_LIMIT][8];
495     int nextid;
496     };
497   shared_ptr<gpdata_t> gpdata = nullptr;
498   #endif
499 
500   int state = 0;
501   int usershape_state = 0;
502 
503   /** contains the texture point coordinates for 3D models */
504   basic_textureinfo models_texture;
505 
geometry_informationhr::geometry_information506   geometry_information() { last = NULL; }
507 
require_basicshr::geometry_information508   void require_basics() { if(state & 1) return; state |= 1; prepare_basics(); }
require_shapeshr::geometry_information509   void require_shapes() { if(state & 2) return; state |= 2; prepare_shapes(); }
require_usershapeshr::geometry_information510   void require_usershapes() { if(usershape_state == usershape_changes) return; usershape_state = usershape_changes; prepare_usershapes(); }
511   int timestamp;
512 
513   hpcshape& generate_pipe(ld length, ld width);
514 
515   map<string, unique_ptr<gi_extension>> ext;
516   };
517 #endif
518 
get_hsh()519 EX subcellshape& get_hsh() {
520   if(!cgi.heptshape) cgi.heptshape = (unique_ptr<subcellshape>) (new subcellshape);
521   return *cgi.heptshape;
522   }
523 
add_wall(int i,const vector<hyperpoint> & h)524 EX void add_wall(int i, const vector<hyperpoint>& h) {
525   auto& f = get_hsh().faces;
526   if(isize(f) <= i) f.resize(i+1);
527   f[i] = h;
528   }
529 
530 /** values of hcrossf and hexf for the standard geometry. Since polygons are
531  *  usually drawn in this geometry, the scale in other geometries is usually
532  *  based on comparing these values to the values in the other geometry.
533  */
534 
535 #if HDR
536 static const ld hcrossf7 = 0.620672, hexf7 = 0.378077, tessf7 = 1.090550, hexhexdist7 = 0.566256;
537 #endif
538 
scale_used()539 EX bool scale_used() { return (shmup::on && geometry == gNormal && BITRUNCATED) ? (cheater || autocheat) : true; }
540 
is_subcube_based(eVariation var)541 EX bool is_subcube_based(eVariation var) {
542   return among(var, eVariation::subcubes, eVariation::dual_subcubes, eVariation::bch, eVariation::bch_oct);
543   }
544 
is_reg3_variation(eVariation var)545 EX bool is_reg3_variation(eVariation var) {
546   return var == eVariation::coxeter;
547   }
548 
prepare_basics()549 void geometry_information::prepare_basics() {
550 
551   DEBBI(DF_INIT | DF_POLY | DF_GEOM, ("prepare_basics"));
552 
553   hexshift = 0;
554 
555   ld ALPHA = 2 * M_PI / S7;
556 
557   ld fmin, fmax;
558 
559   ld s3, beta;
560 
561   heptshape = nullptr;
562 
563   if(arcm::in() && !prod)
564     ginf[gArchimedean].cclass = gcHyperbolic;
565 
566   dynamicval<eVariation> gv(variation, variation);
567   bool inv = INVERSE;
568   if(INVERSE) {
569     variation = gp::variation_for(gp::param);
570     println(hlog, "bitruncated = ", BITRUNCATED);
571     }
572 
573   if(hybri) {
574     auto t = this;
575     ld d = prod ? 1 : 2;
576     hybrid::in_underlying_geometry([&] {
577       t->rhexf = cgi.rhexf / d;
578       t->hexf = cgi.hexf / d;
579       t->crossf = cgi.crossf / d;
580       t->hcrossf = cgi.crossf / d;
581       t->tessf = cgi.tessf / d;
582       t->hexvdist = cgi.hexvdist / d;
583       t->hexhexdist = hdist(xpush0(cgi.hcrossf), xspinpush0(M_PI*2/S7, cgi.hcrossf)) / d;
584       t->base_distlimit = cgi.base_distlimit-1;
585       });
586     goto hybrid_finish;
587     }
588 
589   if((sphere || hyperbolic) && WDIM == 3 && !bt::in()) {
590     rhexf = hexf = 0.378077;
591     crossf = hcrossf = 0.620672;
592     tessf = 1.090550;
593     hexhexdist = 0.566256;
594     goto finish;
595     }
596 
597   s3 = S3;
598   if(fake::in() && !arcm::in()) s3 = fake::around;
599 
600   beta = (S3 >= OINF && !fake::in()) ? 0 : 2*M_PI/s3;
601 
602   tessf = euclid ? 1 : edge_of_triangle_with_angles(beta, M_PI/S7, M_PI/S7);
603 
604   if(elliptic && S7 == 4 && !fake::in()) tessf = M_PI/2;
605 
606   hcrossf = euclid ? tessf / 2 / sin(M_PI/s3) : edge_of_triangle_with_angles(M_PI/2, M_PI/S7, beta/2);
607 
608   if(S3 >= OINF) hcrossf = 10;
609 
610   crossf = BITRUNCATED ? hcrossf : tessf;
611 
612   fmin = 0, fmax = tessf;
613   for(int p=0; p<100; p++) {
614     ld f =  (fmin+fmax) / 2;
615     hyperpoint H = xpush0(f);
616     hyperpoint H1 = spin(2*M_PI/S7) * H;
617     hyperpoint H2 = xpush0(tessf-f);
618     ld v1 = intval(H, H1), v2 = intval(H, H2);
619 
620     if(fake::in() && WDIM == 2) {
621       hexvdist = hdist(xpush0(f), xspinpush0(ALPHA/2, hcrossf));
622       v2 = hdist(
623         spin(M_PI/2/S3) * xpush0(hexvdist),
624         spin(-M_PI/2/S3) * xpush0(hexvdist)
625         );
626 
627       v1 = hdist(
628         spin(M_PI/S7) * xpush0(f),
629         spin(-M_PI/S7) * xpush0(f)
630         );
631       }
632 
633     if(v1 < v2) fmin = f; else fmax = f;
634     }
635   hexf = fmin;
636 
637   rhexf = BITRUNCATED ? hexf : hcrossf;
638 
639   if(BITRUNCATED && !(S7&1))
640     hexshift = ALPHA/2 + ALPHA * ((S7-1)/2) + M_PI;
641 
642   finish:
643 
644   heptmove.resize(S7);
645   hexmove.resize(S7);
646   invhexmove.resize(S7);
647 
648   for(int d=0; d<S7; d++)
649     heptmove[d] = spin(-d * ALPHA) * xpush(tessf) * spin(M_PI);
650 
651   for(int d=0; d<S7; d++)
652     hexmove[d] = spin(hexshift-d * ALPHA) * xpush(-crossf)* spin(M_PI);
653 
654   for(int d=0; d<S7; d++) invhexmove[d] = iso_inverse(hexmove[d]);
655 
656   hexvdist = hdist(xpush0(hexf), xspinpush0(ALPHA/2, hcrossf));
657 
658   hexhexdist = fake::in() ?
659     2 * hdist0(mid(xspinpush0(M_PI/S6, hexvdist), xspinpush0(-M_PI/S6, hexvdist)))
660     : hdist(xpush0(crossf), xspinpush0(M_PI*2/S7, crossf));
661 
662   DEBB(DF_GEOM | DF_POLY,
663     (format("S7=%d S6=%d hexf = " LDF" hcross = " LDF" tessf = " LDF" hexshift = " LDF " hexhex = " LDF " hexv = " LDF "\n", S7, S6, hexf, hcrossf, tessf, hexshift,
664     hexhexdist, hexvdist)));
665 
666   base_distlimit = ginf[geometry].distlimit[!BITRUNCATED];
667 
668   #if CAP_GP
669   gp::compute_geometry(inv);
670   #endif
671   #if CAP_IRR
672   irr::compute_geometry();
673   #endif
674   #if CAP_ARCM
675   if(arcm::in()) {
676     auto& ac = arcm::current_or_fake();
677     if(fake::in()) ac = arcm::current;
678     ac.compute_geometry();
679     crossf = hcrossf7 * ac.scale();
680     hexvdist = ac.scale() * .5;
681     rhexf = ac.scale() * .5;
682     }
683   #endif
684   #if CAP_BT
685   if(bt::in()) hexvdist = rhexf = 1, tessf = 1, scalefactor = 1, crossf = hcrossf7;
686   if(geometry == gHoroRec || kite::in() || sol || nil || nih) hexvdist = rhexf = .5, tessf = .5, scalefactor = .5, crossf = hcrossf7/2;
687   if(bt::in()) scalefactor *= min<ld>(vid.binary_width, 1), crossf *= min<ld>(vid.binary_width, 1);
688   #endif
689   #if CAP_BT && MAXMDIM >= 4
690   if(bt::in()) bt::build_tmatrix();
691   #endif
692   #if MAXMDIM >= 4
693   if(reg3::in()) reg3::generate();
694   if(euc::in(3)) euc::generate();
695   #if CAP_SOLV
696   else if(sn::in()) sn::create_faces();
697   #endif
698   #if CAP_BT
699   else if(bt::in()) bt::create_faces();
700   #endif
701   else if(nil) nilv::create_faces();
702   #endif
703 
704   hybrid_finish:
705 
706   scalefactor = crossf / hcrossf7;
707   orbsize = crossf;
708 
709   if(fake::in() && WDIM == 2) {
710     auto& u = *fake::underlying_cgip;
711     geometry = fake::underlying;
712     ld orig = xpush0(u.hcrossf)[0] / xpush0(u.hcrossf)[GDIM];
713     geometry = gFake;
714     ld our = xpush0(hcrossf)[0] / xpush0(hcrossf)[GDIM];
715     fake::scale = our / orig;
716     // if(debugflags & DF_GEOM)
717     }
718 
719   if(fake::in() && WDIM == 3) {
720     auto& u = fake::underlying_cgip;
721     crossf = u->crossf * fake::scale;
722     scalefactor = u->scalefactor * fake::scale;
723     orbsize = u->orbsize * fake::scale;
724     hexf = u->hexf * fake::scale;
725     rhexf = u->rhexf * fake::scale;
726     hexvdist = u->hexvdist * fake::scale;
727     hcrossf = u->hcrossf * fake::scale;
728     }
729 
730   if(arb::in()) {
731     auto csc = arb::current_or_slided().cscale;
732     scalefactor = csc;
733     hcrossf = crossf = orbsize = hcrossf7 * csc;
734     hexf = rhexf = hexvdist = csc * arb::current_or_slided().floor_scale;
735     base_distlimit = arb::current.range;
736     }
737 
738   if(is_subcube_based(variation)) {
739     scalefactor /= reg3::subcube_count;
740     orbsize /= reg3::subcube_count;
741     }
742 
743   if(scale_used()) {
744     scalefactor *= vid.creature_scale;
745     orbsize *= vid.creature_scale;
746     }
747 
748   zhexf = BITRUNCATED ? hexf : crossf* .55;
749   if(scale_used()) zhexf *= vid.creature_scale;
750   if(WDIM == 2 && GDIM == 3) zhexf *= 1.5, orbsize *= 1.2;
751 
752   floorrad0 = hexvdist* (GDIM == 3 ? 1 : 0.92);
753   floorrad1 = rhexf * (GDIM == 3 ? 1 : 0.94);
754 
755   if(euc::in(2,4)) {
756     if(!BITRUNCATED)
757       floorrad0 = floorrad1 = rhexf * (GDIM == 3 ? 1 : .94);
758     else
759       floorrad0 = hexvdist * (GDIM == 3 ? 1 : .9),
760       floorrad1 = rhexf * (GDIM == 3 ? 1 : .8);
761     }
762 
763   plevel = vid.plevel_factor * scalefactor;
764   single_step = 1;
765   if(hybri && !prod) {
766     #if CAP_ARCM
767     if(hybrid::underlying == gArchimedean)
768       arcm::current.get_step_values(psl_steps, single_step);
769     #else
770     if(0) ;
771     #endif
772     else {
773       single_step = S3 * S7 - 2 * S7 - 2 * S3;
774       psl_steps = 2 * S7;
775       if(BITRUNCATED) psl_steps *= S3;
776       if(inv) psl_steps = 2 * S3;
777       if(single_step < 0) single_step = -single_step;
778       }
779     DEBB(DF_GEOM | DF_POLY, ("steps = ", psl_steps, " / ", single_step));
780     plevel = M_PI * single_step / psl_steps;
781     }
782 
783   if(hybri) {
784     /* we do not want too short creatures, better make the scale factor smaller */
785     scalefactor = min(scalefactor, cgi.plevel * 1.8 / vid.height_width);
786     }
787 
788   set_sibling_limit();
789 
790   prepare_compute3();
791   if(hyperbolic && &currfp != &fieldpattern::fp_invalid)
792     currfp.analyze();
793 
794   #if CAP_SOLV
795   if(asonov::in()) {
796     asonov::prepare();
797     asonov::prepare_walls();
798     }
799   #endif
800   }
801 
xspinpush(ld dir,ld dist)802 EX transmatrix xspinpush(ld dir, ld dist) {
803   if(euclid)
804     return eupush(cos(dir) * dist, -sin(dir) * dist);
805   else
806     return spin(dir) * xpush(dist) * spin(-dir);
807   }
808 
809 EX purehookset hooks_swapdim;
810 
811 EX namespace geom3 {
812 
813   // Here we convert between the following parameters:
814 
815   // abslev: level below the plane
816   // lev: level above the world (abslev = depth-lev)
817   // projection: projection parameter
818   // factor: zoom factor
819 
abslev_to_projection(ld abslev)820   EX ld abslev_to_projection(ld abslev) {
821     if(sphere || euclid) return vid.camera+abslev;
822     return tanh(abslev) / tanh(vid.camera);
823     }
824 
projection_to_abslev(ld proj)825   ld projection_to_abslev(ld proj) {
826     if(sphere || euclid) return proj-vid.camera;
827     // tanh(abslev) / tanh(camera) = proj
828     return atanh(proj * tanh(vid.camera));
829     }
830 
lev_to_projection(ld lev)831   ld lev_to_projection(ld lev) {
832     return abslev_to_projection(vid.depth - lev);
833     }
834 
projection_to_factor(ld proj)835   ld projection_to_factor(ld proj) {
836     return lev_to_projection(0) / proj;
837     }
838 
factor_to_projection(ld fac)839   EX ld factor_to_projection(ld fac) {
840     return lev_to_projection(0) / fac;
841     }
842 
lev_to_factor(ld lev)843   EX ld lev_to_factor(ld lev) {
844     if(prod) return -lev;
845     if(WDIM == 3) return lev;
846     if(GDIM == 3) return vid.depth - lev;
847     return projection_to_factor(lev_to_projection(lev));
848     }
factor_to_lev(ld fac)849   EX ld factor_to_lev(ld fac) {
850     if(prod) return -fac;
851     if(GDIM == 3) return fac;
852     return vid.depth - projection_to_abslev(factor_to_projection(fac));
853     }
854 
do_auto_eye()855   EX void do_auto_eye() {
856     if(!vid.auto_eye) return;
857     auto& cs = getcs();
858     if(cs.charid < 4)
859       vid.eye = cgi.eyelevel_human;
860     else if(cs.charid < 8)
861       vid.eye = cgi.eyelevel_dog;
862     else if(cs.charid == 8)
863       vid.eye = cgi.eyelevel_familiar;
864     }
865 
866   // how should we scale at level lev
scale_at_lev(ld lev)867   EX ld scale_at_lev(ld lev) {
868     if(sphere || euclid) return 1;
869     return cosh(vid.depth - lev);
870     }
871 
872   EX string invalid;
873 
actual_wall_height()874   EX ld actual_wall_height() {
875       if(hybri) return cgi.plevel;
876       #if CAP_GP
877       if(GOLDBERG && vid.gp_autoscale_heights)
878         return vid.wall_height * min<ld>(4 / hypot_d(2, gp::next), 1);
879       #endif
880       return vid.wall_height;
881       }
882   EX }
883 
prepare_compute3()884   void geometry_information::prepare_compute3() {
885     using namespace geom3;
886     DEBBI(DF_INIT | DF_POLY | DF_GEOM, ("geom3::compute"));
887     // tanh(depth) / tanh(camera) == pconf.alpha
888     invalid = "";
889 
890     if(GDIM == 3) ;
891     else if(vid.tc_alpha < vid.tc_depth && vid.tc_alpha < vid.tc_camera)
892       pconf.alpha = tan_auto(vid.depth) / tan_auto(vid.camera);
893     else if(vid.tc_depth < vid.tc_alpha && vid.tc_depth < vid.tc_camera) {
894       ld v = pconf.alpha * tan_auto(vid.camera);
895       if(hyperbolic && (v<1e-6-12 || v>1-1e-12)) invalid = XLAT("cannot adjust depth"), vid.depth = vid.camera;
896       else vid.depth = atan_auto(v);
897       }
898     else {
899       ld v = tan_auto(vid.depth) / pconf.alpha;
900       if(hyperbolic && (v<1e-12-1 || v>1-1e-12)) invalid = XLAT("cannot adjust camera"), vid.camera = vid.depth;
901       else vid.camera = atan_auto(v);
902       }
903 
904     if(fabs(pconf.alpha) < 1e-6) invalid = XLAT("does not work with perfect Klein");
905 
906     if(invalid != "") {
907       INFDEEP = .7;
908       BOTTOM = .8;
909       HELLSPIKE = .85;
910       LAKE = .9;
911       FLOOR = 1;
912       WALL = 1.25;
913       SLEV[0] = 1;
914       SLEV[1] = 1.08;
915       SLEV[2] = 1.16;
916       SLEV[3] = 1.24;
917       FLATEYE = 1.03;
918       LEG1 = 1.025;
919       LEG = 1.05;
920       LEG3 = 1.075;
921       GROIN = 1.09;
922       GROIN1 = 1.105;
923       GHOST = 1.1;
924       BODY = 1.15;
925       BODY1 = 1.151;
926       BODY2 = 1.152;
927       BODY3 = 1.153;
928       NECK1 = 1.16;
929       NECK = 1.17;
930       NECK3 = 1.18;
931       HEAD = 1.188;
932       HEAD1= 1.189;
933       HEAD2= 1.190;
934       HEAD3= 1.191;
935       ABODY = 1.08;
936       AHEAD = 1.12;
937       BIRD = 1.20;
938       }
939     else {
940       INFDEEP = GDIM == 3 ? (sphere ? M_PI/2 : +5) : (euclid || sphere) ? 0.01 : lev_to_projection(0) * tanh(vid.camera);
941       ld wh = actual_wall_height();
942       WALL = lev_to_factor(wh);
943       FLOOR = lev_to_factor(0);
944 
945       human_height = vid.human_wall_ratio * wh;
946       if(WDIM == 3) human_height = scalefactor * vid.height_width / 2;
947       if(hybri) human_height = min(human_height, cgi.plevel * .9);
948 
949       ld reduce = (WDIM == 3 ? human_height / 2 : 0);
950 
951       LEG0  = lev_to_factor(human_height * .0 - reduce);
952       LEG1  = lev_to_factor(human_height * .1 - reduce);
953       LEG   = lev_to_factor(human_height * .2 - reduce);
954       LEG3  = lev_to_factor(human_height * .3 - reduce);
955       GROIN = lev_to_factor(human_height * .4 - reduce);
956       GROIN1= lev_to_factor(human_height * .5 - reduce);
957       BODY  = lev_to_factor(human_height * .6 - reduce);
958       BODY1 = lev_to_factor(human_height * .61 - reduce);
959       BODY2 = lev_to_factor(human_height * .62 - reduce);
960       BODY3 = lev_to_factor(human_height * .63 - reduce);
961       NECK1 = lev_to_factor(human_height * .7 - reduce);
962       NECK  = lev_to_factor(human_height * .8 - reduce);
963       NECK3 = lev_to_factor(human_height * .9 - reduce);
964       HEAD  = lev_to_factor(human_height * .97 - reduce);
965       HEAD1 = lev_to_factor(human_height * .98 - reduce);
966       HEAD2 = lev_to_factor(human_height * .99 - reduce);
967       HEAD3 = lev_to_factor(human_height - reduce);
968 
969       reduce = (GDIM == 3 ? human_height * .3 : 0);
970 
971       STUFF = lev_to_factor(0) - max(orbsize * 0.3, zhexf * .6);
972 
973       ABODY = lev_to_factor(human_height * .4 - reduce);
974       ALEG0 = lev_to_factor(human_height * .0 - reduce);
975       ALEG  = lev_to_factor(human_height * .2 - reduce);
976       AHEAD = lev_to_factor(human_height * .6 - reduce);
977       BIRD = lev_to_factor(WDIM == 3 ? 0 : (vid.human_wall_ratio+1)/2 * wh * .8);
978       GHOST = lev_to_factor(WDIM == 3 ? 0 : human_height * .5);
979       FLATEYE = lev_to_factor(human_height * .15);
980 
981       slev = vid.rock_wall_ratio * wh / 3;
982       for(int s=0; s<=3; s++)
983         SLEV[s] = lev_to_factor(vid.rock_wall_ratio * wh * s/3);
984       LAKE = lev_to_factor(-vid.lake_top);
985       SHALLOW = lev_to_factor(-.4);
986       HELLSPIKE = lev_to_factor(-(vid.lake_top+vid.lake_bottom)/2);
987       BOTTOM = lev_to_factor(-vid.lake_bottom);
988       LOWSKY = lev_to_factor(2 * wh);
989       HIGH = LOWSKY;
990       HIGH2 = lev_to_factor(3 * wh);
991       SKY = LOWSKY - 5;
992       }
993     }
994 
995 EX namespace geom3 {
996 
apply_always3()997 EX void apply_always3() {
998     for(geometryinfo& gi: ginf) {
999       auto &g = gi.g;
1000       if(vid.always3 && g.gameplay_dimension == 2 && g.graphical_dimension == 2) {
1001         g.graphical_dimension++;
1002         g.homogeneous_dimension++;
1003         g.sig[3] = g.sig[2];
1004         g.sig[2] = g.sig[1];
1005         }
1006       if(!vid.always3 && g.gameplay_dimension == 2 && g.graphical_dimension == 3) {
1007         g.graphical_dimension--;
1008         g.homogeneous_dimension--;
1009         g.sig[1] = g.sig[2];
1010         g.sig[2] = g.sig[3];
1011         }
1012       }
1013     }
1014 
1015   #if MAXMDIM >= 4
switch_always3()1016 EX void switch_always3() {
1017     if(dual::split(switch_always3)) return;
1018     #if CAP_GL && CAP_RUG
1019     if(rug::rugged) rug::close();
1020     #endif
1021     vid.always3 = !vid.always3;
1022     apply_always3();
1023     swapmatrix(View);
1024     callhooks(hooks_swapdim);
1025     }
1026 #endif
1027 
switch_tpp()1028   EX void switch_tpp() {
1029     if(dual::split(switch_fpp)) return;
1030     if(pmodel == mdDisk && pconf.camera_angle) {
1031       vid.yshift = 0;
1032       pconf.camera_angle = 0;
1033       pconf.xposition = 0;
1034       pconf.yposition = 0;
1035       pconf.scale = 1;
1036       vid.fixed_facing = false;
1037       }
1038     else {
1039       vid.yshift = -0.3;
1040       pconf.camera_angle = -45;
1041       pconf.scale = 18/16. * vid.xres / vid.yres / multi::players;
1042       pconf.xposition = 0;
1043       pconf.yposition = -0.9;
1044       vid.fixed_facing = true;
1045       vid.fixed_facing_dir = 90;
1046       }
1047     }
1048 
switch_fpp()1049   EX void switch_fpp() {
1050 #if MAXMDIM >= 4
1051     #if CAP_GL && CAP_RUG
1052     if(rug::rugged) rug::close();
1053     #endif
1054     if(dual::split(switch_fpp)) return;
1055     check_cgi(); cgi.require_basics();
1056     View = iso_inverse(models::rotmatrix()) * View;
1057     if(!vid.always3) {
1058       vid.always3 = true;
1059       apply_always3();
1060       ld ms = min<ld>(cgi.scalefactor, 1);
1061       vid.wall_height = 1.5 * ms;
1062       if(sphere) {
1063         vid.depth = M_PI / 6;
1064         vid.wall_height = M_PI / 3;
1065         }
1066       vid.human_wall_ratio = 0.8;
1067       if(euclid && allowIncreasedSight() && vid.use_smart_range == 0) {
1068         genrange_bonus = gamerange_bonus = sightrange_bonus = cgi.base_distlimit * 3/2;
1069         }
1070       vid.camera = 0;
1071       vid.depth = ms;
1072       if(pmodel == mdDisk) pmodel = mdPerspective;
1073       swapmatrix(View);
1074       swapmatrix(current_display->which_copy);
1075       callhooks(hooks_swapdim);
1076       for(auto m: allmaps) m->on_dim_change();
1077       if(cgflags & qIDEAL && vid.texture_step < 32)
1078         vid.texture_step = 32;
1079 #if CAP_RACING
1080       racing::player_relative = true;
1081 #endif
1082       }
1083     else {
1084       vid.always3 = false;
1085       apply_always3();
1086       vid.wall_height = .3;
1087       vid.human_wall_ratio = .7;
1088       vid.camera = 1;
1089       vid.depth = 1;
1090       if(pmodel == mdPerspective) pmodel = mdDisk;
1091       swapmatrix(View);
1092       swapmatrix(current_display->which_copy);
1093       callhooks(hooks_swapdim);
1094       for(auto m: allmaps) m->on_dim_change();
1095       }
1096     View = models::rotmatrix() * View;
1097 #endif
1098     }
1099 
1100   EX }
1101 
1102 EX geometry_information *cgip;
1103 EX map<string, geometry_information> cgis;
1104 
1105 #if HDR
1106 #define cgi (*cgip)
1107 #endif
1108 
1109 EX int last_texture_step;
1110 
1111 int ntimestamp;
1112 
1113 EX hookset<void(string&)> hooks_cgi_string;
1114 
cgi_string()1115 EX string cgi_string() {
1116   string s;
1117   auto V = [&] (string a, string b) { s += a; s += ": "; s += b; s += "; "; };
1118   V("GEO", its(int(geometry)));
1119   V("VAR", its(int(variation)));
1120 
1121   if(arb::in() && arb::using_slided) {
1122     for(auto& sl: arb::current.sliders)
1123       V("AS", fts(sl.current));
1124     }
1125 
1126   if(fake::in()) {
1127     if(hyperbolic) V("H", fts(fake::around));
1128     if(euclid) V("E", fts(fake::around));
1129     if(sphere) V("S", fts(fake::around));
1130     V("G", FPIU(cgi_string()));
1131     return s;
1132     }
1133 
1134   if(GOLDBERG_INV) V("GP", its(gp::param.first) + "," + its(gp::param.second));
1135   if(IRREGULAR) V("IRR", its(irr::irrid));
1136   if(is_subcube_based(variation)) V("SC", its(reg3::subcube_count));
1137   if(variation == eVariation::coxeter) V("COX", its(reg3::coxeter_param));
1138 
1139   #if CAP_ARCM
1140   if(arcm::in()) V("ARCM", arcm::current.symbol);
1141   #endif
1142 
1143   if(arb::in()) V("ARB", its(arb::current.order));
1144 
1145   if(cryst) V("CRYSTAL", its(ginf[gCrystal].sides) + its(ginf[gCrystal].vertex));
1146 
1147   if(bt::in() || GDIM == 3) V("WQ", its(vid.texture_step));
1148 
1149   if(hybri) {
1150     V("U", PIU(cgi_string()));
1151     // its(int(hybrid::underlying)));
1152     }
1153 
1154   if(prod) V("PL", fts(vid.plevel_factor));
1155 
1156   if(geometry == gFieldQuotient) { V("S3=", its(S3)); V("S7=", its(S7)); }
1157   if(nil) V("NIL", its(S7));
1158 
1159   if(bt::in()) V("BT", fts(vid.binary_width));
1160 
1161   if(nil) V("NILW", fts(nilv::nilwidth));
1162 
1163   if(GDIM == 2) {
1164     V("CAMERA", fts(vid.camera));
1165     }
1166 
1167   if(WDIM == 2) {
1168     V("WH", fts(vid.wall_height));
1169     V("HW", fts(vid.human_wall_ratio));
1170     V("RW", fts(vid.rock_wall_ratio));
1171     V("DEPTH", fts(vid.depth));
1172     V("ASH", ONOFF(vid.gp_autoscale_heights));
1173     V("LT", fts(vid.lake_top));
1174     V("LB", fts(vid.lake_bottom));
1175     }
1176 
1177   V("3D", ONOFF(vid.always3));
1178 
1179   if(scale_used()) V("CS", fts(vid.creature_scale));
1180 
1181   if(WDIM == 3) V("HTW", fts(vid.height_width));
1182 
1183   V("LQ", its(vid.linequality));
1184 
1185   callhooks(hooks_cgi_string, s);
1186 
1187   return s;
1188   }
1189 
check_cgi()1190 EX void check_cgi() {
1191   string s = cgi_string();
1192 
1193   cgip = &cgis[s];
1194   cgi.timestamp = ++ntimestamp;
1195   if(hybri) hybrid::underlying_cgip->timestamp = ntimestamp;
1196   if(fake::in()) fake::underlying_cgip->timestamp = ntimestamp;
1197   if(arcm::alt_cgip) arcm::alt_cgip->timestamp = ntimestamp;
1198 
1199   if(isize(cgis) > 4) {
1200     vector<pair<int, string>> timestamps;
1201     for(auto& t: cgis) timestamps.emplace_back(-t.second.timestamp, t.first);
1202     sort(timestamps.begin(), timestamps.end());
1203     while(isize(timestamps) > 4) {
1204       DEBB(DF_GEOM, ("erasing geometry ", timestamps.back().second));
1205       cgis.erase(timestamps.back().second);
1206       timestamps.pop_back();
1207       }
1208     }
1209 
1210   if(floor_textures && last_texture_step != vid.texture_step) {
1211     println(hlog, "changed ", last_texture_step, " to ", vid.texture_step);
1212     delete floor_textures;
1213     floor_textures = NULL;
1214     }
1215 
1216   #if MAXMDIM >= 4 && CAP_GL
1217   if(!floor_textures && GDIM == 3 && (cgi.state & 2))
1218     make_floor_textures();
1219   #endif
1220 
1221   }
1222 
clear_cgis()1223 void clear_cgis() {
1224   printf("clear_cgis\n");
1225   for(auto& p: cgis) if(&p.second != &cgi) { cgis.erase(p.first); return; }
1226   }
1227 
1228 auto ah_clear_geo = addHook(hooks_clear_cache, 0, clear_cgis);
1229 
1230 }
1231