1
0
mirror of https://github.com/zenorogue/hyperrogue.git synced 2024-12-11 03:50:27 +00:00
hyperrogue/geometry.cpp

1515 lines
46 KiB
C++
Raw Normal View History

// Hyperbolic Rogue -- basic geometry
// Copyright (C) 2011-2019 Zeno Rogue, see 'hyper.cpp' for details
2016-08-26 09:58:03 +00:00
/** \file geometry.cpp
* \brief Calculation of basic, and less basic, constants in each geometry
*/
2015-08-08 13:57:52 +00:00
#include "hyper.h"
namespace hr {
2019-08-09 23:56:00 +00:00
#if HDR
struct usershapelayer {
vector<hyperpoint> list;
bool sym;
int rots;
color_t color;
hyperpoint shift, spin;
ld zlevel;
int texture_offset;
PPR prio;
};
2019-08-10 08:57:14 +00:00
extern int usershape_changes;
2019-08-09 23:56:00 +00:00
static const int USERLAYERS = 32;
struct usershape { usershapelayer d[USERLAYERS]; };
struct hpcshape {
int s, e;
PPR prio;
int flags;
hyperpoint intester;
struct basic_textureinfo *tinf;
int texture_offset;
int shs, she;
void clear() { s = e = shs = she = texture_offset = 0; prio = PPR::ZERO; tinf = NULL; flags = 0; }
};
2019-08-10 08:57:14 +00:00
#define SIDE_SLEV 0
#define SIDE_WTS3 3
#define SIDE_WALL 4
#define SIDE_LAKE 5
#define SIDE_LTOB 6
#define SIDE_BTOI 7
#define SIDE_SKY 8
#define SIDE_HIGH 9
#define SIDE_HIGH2 10
2020-02-26 00:15:30 +00:00
#define SIDE_ASHA 11
#define SIDE_BSHA 12
#define SIDEPARS 13
2019-08-10 08:57:14 +00:00
/** GOLDBERG_BITS controls the size of tables for Goldberg. see gp::check_limits */
#ifndef GOLDBERG_BITS
#define GOLDBERG_BITS 5
#endif
static const int GOLDBERG_LIMIT = (1<<GOLDBERG_BITS);
static const int GOLDBERG_MASK = (GOLDBERG_LIMIT-1);
2020-05-25 00:27:04 +00:00
#ifndef BADMODEL
2019-08-10 08:57:14 +00:00
#define BADMODEL 0
2020-05-25 00:27:04 +00:00
#endif
2019-08-10 08:57:14 +00:00
2020-05-25 00:27:04 +00:00
#ifndef WINGS
2019-08-09 23:56:00 +00:00
static const int WINGS = (BADMODEL ? 1 : 4);
2020-05-25 00:27:04 +00:00
#endif
2019-08-09 23:56:00 +00:00
typedef array<hpcshape, WINGS+1> hpcshape_animated;
extern vector<hpcshape> shPlainWall3D, shWireframe3D, shWall3D, shMiniWall3D;
struct floorshape {
bool is_plain;
int shapeid;
int id;
int pstrength; // pattern strength in 3D
int fstrength; // frame strength in 3D
PPR prio;
2020-01-18 15:03:32 +00:00
vector<hpcshape> b, shadow, side[SIDEPARS], levels[SIDEPARS], cone[2];
vector<vector<hpcshape>> gpside[SIDEPARS];
2019-08-09 23:56:00 +00:00
floorshape() { prio = PPR::FLOOR; pstrength = fstrength = 10; }
};
struct plain_floorshape : floorshape {
ld rad0, rad1;
void configure(ld r0, ld r1) { rad0 = r0; rad1 = r1; }
};
2019-08-10 08:57:14 +00:00
extern vector<ld> equal_weights;
2019-08-09 23:56:00 +00:00
// noftype: 0 (shapeid2 is heptagonal or just use shapeid1), 1 (shapeid2 is pure heptagonal), 2 (shapeid2 is Euclidean), 3 (shapeid2 is hexagonal)
struct escher_floorshape : floorshape {
int shapeid0, shapeid1, noftype, shapeid2;
ld scale;
};
struct basic_textureinfo {
int texture_id;
vector<glvertex> tvertices;
2020-09-11 09:08:27 +00:00
vector<glvertex> colors;
};
2021-03-31 08:30:03 +00:00
/** additional modules can add extra shapes etc. */
struct gi_extension {
2021-03-31 11:35:28 +00:00
virtual ~gi_extension() {}
2021-03-31 08:30:03 +00:00
};
2021-12-11 22:28:05 +00:00
struct expansion_analyzer;
/** both for 'heptagon' 3D cells and subdivided 3D cells */
2021-07-06 23:48:20 +00:00
struct subcellshape {
/** \brief raw coordinates of vertices of all faces */
2021-07-06 23:48:20 +00:00
vector<vector<hyperpoint>> faces;
/** \brief raw coordinates of all vertices in one vector */
2021-07-06 23:48:20 +00:00
vector<hyperpoint> vertices_only;
/** \brief cooked coordinates of vertices of all faces, computed from faces as: from_cellcenter * final_coords(v) */
vector<vector<hyperpoint>> faces_local;
/** \brief cooked coordinates of all vertices in one vector */
2021-07-06 23:48:20 +00:00
vector<hyperpoint> vertices_only_local;
/** \brief weights -- used to generate wall shapes in some geometries, empty otherwise */
vector<vector<double>> weights;
/** the center of every raw face */
2021-07-06 23:48:20 +00:00
vector<hyperpoint> face_centers;
2021-07-11 13:07:40 +00:00
vector<vector<char>> dirdist;
2021-07-06 23:48:20 +00:00
hyperpoint cellcenter;
transmatrix to_cellcenter;
transmatrix from_cellcenter;
/** \brief for adjacent directions a,b, next_dir[a][b] is the next direction adjacent to a, in (counter?)clockwise order from b */
vector<vector<char>> next_dir;
/** useful in product geometries */
vector<hyperpoint> walltester;
/** compute all the properties based on `faces`, for the main heptagon cellshape */
void compute_hept();
/** compute all the properties based on `faces`, for subcells */
void compute_sub();
/** common part of compute_hept and compute_sub */
void compute_common();
2021-07-06 23:48:20 +00:00
};
2022-07-14 09:28:24 +00:00
enum class ePipeEnd {sharp, ball};
/** basic geometry parameters */
2019-08-09 23:56:00 +00:00
struct geometry_information {
/** distance from heptagon center to another heptagon center */
ld tessf;
/** distance from heptagon center to adjacent cell center (either hcrossf or tessf) */
ld crossf;
/** distance from heptagon center to small heptagon vertex */
ld hexf;
/** distance from heptagon center to big heptagon vertex */
ld hcrossf;
/** distance between adjacent hexagon vertices */
ld hexhexdist;
/** distance between hexagon vertex and hexagon center */
ld hexvdist;
/** distance between heptagon vertex and hexagon center (either hcrossf or something else) */
ld hepvdist;
2019-08-09 23:56:00 +00:00
/** distance from heptagon center to heptagon vertex (either hexf or hcrossf) */
ld rhexf;
2019-08-09 23:56:00 +00:00
/** edge length */
ld edgelen;
2020-04-05 08:53:34 +00:00
/** basic parameters for 3D geometries */
map<int, int> close_distances;
int loop, face, schmid;
2020-04-05 08:53:34 +00:00
transmatrix spins[32], adjmoves[32];
2021-07-06 23:48:20 +00:00
unique_ptr<struct subcellshape> heptshape;
2021-07-06 23:48:20 +00:00
vector<struct subcellshape> subshapes;
2020-04-05 08:53:34 +00:00
ld adjcheck;
ld strafedist;
2020-05-27 23:50:00 +00:00
ld ultra_mirror_dist, ultra_material_part, ultra_mirror_part;
vector<transmatrix> ultra_mirrors;
2020-04-05 08:53:34 +00:00
int xp_order, r_order, rx_order;
transmatrix full_X, full_R, full_P;
/** for 2D geometries */
2020-01-18 15:03:32 +00:00
vector<transmatrix> heptmove, hexmove, invhexmove;
2019-08-09 23:56:00 +00:00
int base_distlimit;
/** size of the Sword (from Orb of the Sword), used in the shmup mode */
2019-08-09 23:56:00 +00:00
ld sword_size;
/** scale factor for the graphics of most things*/
ld scalefactor;
ld orbsize, floorrad0, floorrad1, zhexf;
2019-08-09 23:56:00 +00:00
ld corner_bonus;
ld hexshift;
ld asteroid_size[8];
ld wormscale;
ld tentacle_length;
2019-08-18 19:16:27 +00:00
/** level in product geometries */
ld plevel;
2019-08-26 12:16:55 +00:00
/** level for a z-step */
int single_step;
/** the number of levels in PSL */
int psl_steps;
/** for binary tilings */
transmatrix direct_tmatrix[14];
transmatrix inverse_tmatrix[14];
/** a bitmask for hr::bt::use_direct_for */
int use_direct;
2019-08-09 23:56:00 +00:00
/** various parameters related to the 3D view */
2019-08-09 23:56:00 +00:00
ld INFDEEP, BOTTOM, HELLSPIKE, LAKE, WALL, FLOOR, STUFF,
SLEV[4], FLATEYE,
LEG0, LEG1, LEG, LEG3, GROIN, GROIN1, GHOST,
BODY, BODY1, BODY2, BODY3,
NECK1, NECK, NECK3, HEAD, HEAD1, HEAD2, HEAD3,
2020-02-26 00:15:30 +00:00
ALEG0, ALEG, ABODY, AHEAD, BIRD, LOWSKY, SKY, HIGH, HIGH2,
SHALLOW;
2019-08-09 23:56:00 +00:00
ld human_height, slev;
ld eyelevel_familiar, eyelevel_human, eyelevel_dog;
#if CAP_SHAPES
hpcshape
shSemiFloorSide[SIDEPARS],
shBFloor[2],
shWave[8][2],
shCircleFloor,
shBarrel,
shWall[2], shMineMark[2], shBigMineMark[2], shFan,
shZebra[5],
shSwitchDisk,
shTower[11],
shEmeraldFloor[6],
shSemiFeatherFloor[2],
shSemiFloor[2], shSemiBFloor[2], shSemiFloorShadow,
shMercuryBridge[2],
shTriheptaSpecial[14],
shCross, shGiantStar[2], shLake, shMirror,
shHalfFloor[6], shHalfMirror[3],
shGem[2], shStar, shFlash, shDisk, shHalfDisk, shDiskT, shDiskS, shDiskM, shDiskSq, shEccentricDisk, shDiskSegment,
shHeptagon, shHeptagram,
2019-08-09 23:56:00 +00:00
shTinyBird, shTinyShark,
2022-08-29 02:06:01 +00:00
shEgg, shSmallEgg,
2022-08-27 19:43:22 +00:00
shRing, shSpikedRing, shTargetRing, shSawRing, shGearRing, shPeaceRing,
2022-09-17 22:00:54 +00:00
shHeptaRing, shSpearRing, shLoveRing, shFrogRing,
2022-08-27 19:43:22 +00:00
shPowerGearRing, shProtectiveRing, shTerraRing, shMoveRing,
shReserved4, shMoonDisk,
2022-08-28 17:25:42 +00:00
shDaisy, shSnowflake, shTriangle, shNecro, shStatue, shKey, shWindArrow,
2019-08-09 23:56:00 +00:00
shGun,
shFigurine, shTreat, shSmallTreat,
2019-08-09 23:56:00 +00:00
shElementalShard,
// shBranch,
2019-08-19 08:33:07 +00:00
shIBranch, shTentacle, shTentacleX, shILeaf[3],
2019-08-09 23:56:00 +00:00
shMovestar,
shWolf, shYeti, shDemon, shGDemon, shEagle, shGargoyleWings, shGargoyleBody,
shFoxTail1, shFoxTail2,
shDogBody, shDogHead, shDogFrontLeg, shDogRearLeg, shDogFrontPaw, shDogRearPaw,
shDogTorso,
shHawk,
shCatBody, shCatLegs, shCatHead, shFamiliarHead, shFamiliarEye,
shWolf1, shWolf2, shWolf3,
shRatEye1, shRatEye2, shRatEye3,
shDogStripes,
shPBody, shSmallPBody, shPSword, shSmallPSword, shPKnife,
2019-08-09 23:56:00 +00:00
shFerocityM, shFerocityF,
shHumanFoot, shHumanLeg, shHumanGroin, shHumanNeck, shSkeletalFoot, shYetiFoot,
shMagicSword, shSmallSword, shMagicShovel, shSeaTentacle, shKrakenHead, shKrakenEye, shKrakenEye2,
2019-08-09 23:56:00 +00:00
shArrow,
shPHead, shPFace, shGolemhead, shHood, shArmor,
shAztecHead, shAztecCap,
shSabre, shTurban1, shTurban2, shVikingHelmet, shRaiderHelmet, shRaiderArmor, shRaiderBody, shRaiderShirt,
shWestHat1, shWestHat2, shGunInHand,
shKnightArmor, shKnightCloak, shWightCloak,
shGhost, shEyes, shSlime, shJelly, shJoint, shWormHead, shSmallWormHead, shTentHead, shShark, shWormSegment, shSmallWormSegment, shWormTail, shSmallWormTail,
shSlimeEyes, shDragonEyes, shSmallDragonEyes, shWormEyes, shSmallWormEyes, shGhostEyes,
shMiniGhost, shSmallEyes, shMiniEyes,
shHedgehogBlade, shSmallHedgehogBlade, shHedgehogBladePlayer,
2019-08-09 23:56:00 +00:00
shWolfBody, shWolfHead, shWolfLegs, shWolfEyes,
shWolfFrontLeg, shWolfRearLeg, shWolfFrontPaw, shWolfRearPaw,
shFemaleBody, shFemaleHair, shFemaleDress, shWitchDress,
shWitchHair, shBeautyHair, shFlowerHair, shFlowerHand, shSuspenders, shTrophy,
shBugBody, shBugArmor, shBugLeg, shBugAntenna,
2022-08-29 04:09:24 +00:00
shPickAxe, shSmallPickAxe, shPike, shFlailBall, shSmallFlailBall, shFlailTrunk, shSmallFlailTrunk, shFlailChain, shHammerHead, shSmallHammerHead,
2019-08-09 23:56:00 +00:00
shBook, shBookCover, shGrail,
shBoatOuter, shBoatInner, shCompass1, shCompass2, shCompass3,
shKnife, shTongue, shFlailMissile, shTrapArrow,
2022-09-15 23:02:27 +00:00
shPirateHook, shSmallPirateHook, shPirateHood, shEyepatch, shPirateX,
2019-08-09 23:56:00 +00:00
// shScratch,
2021-03-21 10:31:07 +00:00
shHeptaMarker, shSnowball, shHugeDisk, shSun, shNightStar, shEuclideanSky,
2019-08-09 23:56:00 +00:00
shSkeletonBody, shSkull, shSkullEyes, shFatBody, shWaterElemental,
shPalaceGate, shFishTail,
shMouse, shMouseLegs, shMouseEyes,
shPrincessDress, shPrinceDress,
shWizardCape1, shWizardCape2,
shBigCarpet1, shBigCarpet2, shBigCarpet3,
2022-08-26 19:34:04 +00:00
shGoatHead, shRose, shRoseItem, shSmallRose, shThorns,
2019-08-09 23:56:00 +00:00
shRatHead, shRatTail, shRatEyes, shRatCape1, shRatCape2,
shWizardHat1, shWizardHat2,
shTortoise[13][6],
shDragonLegs, shDragonTail, shDragonHead, shSmallDragonHead, shDragonSegment, shDragonNostril, shSmallDragonNostril,
2019-08-09 23:56:00 +00:00
shDragonWings,
shSolidBranch, shWeakBranch, shBead0, shBead1,
shBatWings, shBatBody, shBatMouth, shBatFang, shBatEye,
shParticle[16], shAsteroid[8],
shReptile[5][4],
shReptileBody, shReptileHead, shReptileFrontFoot, shReptileRearFoot,
shReptileFrontLeg, shReptileRearLeg, shReptileTail, shReptileEye,
shTrylobite, shTrylobiteHead, shTrylobiteBody,
shTrylobiteFrontLeg, shTrylobiteRearLeg, shTrylobiteFrontClaw, shTrylobiteRearClaw,
shBullBody, shBullHead, shBullHorn, shBullRearHoof, shBullFrontHoof,
2022-08-28 01:21:53 +00:00
shSmallBullHead, shSmallBullHorn,
shTinyBullHead, shTinyBullHorn, shTinyBullBody,
2019-08-09 23:56:00 +00:00
shButterflyBody, shButterflyWing, shGadflyBody, shGadflyWing, shGadflyEye,
shTerraArmor1, shTerraArmor2, shTerraArmor3, shTerraHead, shTerraFace,
shJiangShi, shJiangShiDress, shJiangShiCap1, shJiangShiCap2,
2020-02-26 00:19:49 +00:00
shPikeBody, shPikeEye,
2019-08-09 23:56:00 +00:00
shAsymmetric,
shPBodyOnly, shPBodyArm, shPBodyHand, shPHeadOnly,
2022-08-29 23:14:59 +00:00
shDodeca, shSmallerDodeca,
2022-09-16 19:13:20 +00:00
shLightningBolt, shHumanoid, shHalfHumanoid, shHourglass,
shShield, shSmallFan, shTreeIcon, shLeafIcon;
2019-08-09 23:56:00 +00:00
hpcshape shFrogRearFoot, shFrogFrontFoot, shFrogRearLeg, shFrogFrontLeg, shFrogRearLeg2, shFrogBody, shFrogEye, shFrogStripe, shFrogJumpFoot, shFrogJumpLeg, shSmallFrogRearFoot, shSmallFrogFrontFoot, shSmallFrogRearLeg, shSmallFrogFrontLeg, shSmallFrogRearLeg2, shSmallFrogBody;
2020-02-26 00:19:49 +00:00
2019-08-09 23:56:00 +00:00
hpcshape_animated
shAnimatedEagle, shAnimatedTinyEagle, shAnimatedGadfly, shAnimatedHawk, shAnimatedButterfly,
2021-05-09 00:25:05 +00:00
shAnimatedGargoyle, shAnimatedGargoyle2, shAnimatedBat, shAnimatedBat2;
2021-05-09 00:25:17 +00:00
hpcshape shTinyArrow;
2021-05-09 00:25:05 +00:00
hpcshape shReserved[16];
int orb_inner_ring; //< for shDisk* shapes, the number of vertices in the inner ring
int res1, res2;
2019-08-09 23:56:00 +00:00
2020-01-25 23:48:56 +00:00
map<int, hpcshape> shPipe;
2019-08-09 23:56:00 +00:00
vector<hpcshape> shPlainWall3D, shWireframe3D, shWall3D, shMiniWall3D;
2019-08-21 05:33:24 +00:00
vector<hyperpoint> walltester;
2019-10-22 10:07:38 +00:00
vector<int> wallstart;
vector<transmatrix> raywall;
2019-08-09 23:56:00 +00:00
vector<struct plain_floorshape*> all_plain_floorshapes;
vector<struct escher_floorshape*> all_escher_floorshapes;
plain_floorshape
shFloor,
shMFloor, shMFloor2, shMFloor3, shMFloor4, shFullFloor,
shBigTriangle, shTriheptaFloor, shBigHepta;
escher_floorshape
shStarFloor, shCloudFloor, shCrossFloor, shChargedFloor,
shSStarFloor, shOverFloor, shTriFloor, shFeatherFloor,
shBarrowFloor, shNewFloor, shTrollFloor, shButterflyFloor,
shLavaFloor, shLavaSeabed, shSeabed, shCloudSeabed,
shCaveSeabed, shPalaceFloor, shDemonFloor, shCaveFloor,
shDesertFloor, shPowerFloor, shRoseFloor, shSwitchFloor,
shTurtleFloor, shRedRockFloor[3], shDragonFloor;
ld dlow_table[SIDEPARS], dhi_table[SIDEPARS], dfloor_table[SIDEPARS];
int prehpc;
2022-04-25 17:53:36 +00:00
/** list of points in all shapes */
2019-08-09 23:56:00 +00:00
vector<hyperpoint> hpc;
2022-04-25 17:53:36 +00:00
/** what shape are we currently creating */
hpcshape *last;
/** is the current shape already started? first = not yet */
2019-08-09 23:56:00 +00:00
bool first;
2022-04-25 17:53:36 +00:00
/** starting point of the current shape, can be ultraideal */
hyperpoint starting_point;
/** first ideal point of the current shape */
hyperpoint starting_ideal;
/** last added point of the current shape, can be ultraideal */
hyperpoint last_point;
/** last ideal point of the current shape */
hyperpoint last_ideal;
2019-08-09 23:56:00 +00:00
bool validsidepar[SIDEPARS];
vector<glvertex> ourshape;
#endif
hpcshape shFullCross[2];
int SD3, SD6, SD7, S12, S14, S21, S28, S42, S36, S84;
ld S_step;
2019-08-09 23:56:00 +00:00
vector<pair<int, cell*>> walloffsets;
2019-08-09 23:56:00 +00:00
vector<array<int, 3>> symmetriesAt;
struct cellrotation_t {
transmatrix M;
vector<int> mapping;
int inverse_id;
};
vector<cellrotation_t> cellrotations;
2019-10-12 09:22:13 +00:00
2019-08-09 23:56:00 +00:00
#ifndef SCALETUNER
static constexpr
#endif
double bscale7 = 1, brot7 = 0, bscale6 = 1, brot6 = 0;
vector<hpcshape*> allshapes;
transmatrix shadowmulmatrix;
map<usershapelayer*, hpcshape> ushr;
void prepare_basics();
void prepare_compute3();
void prepare_shapes();
void prepare_usershapes();
void hpcpush(hyperpoint h);
2022-04-25 17:53:36 +00:00
void hpc_connect_ideal(hyperpoint a, hyperpoint b);
2019-08-09 23:56:00 +00:00
void hpcsquare(hyperpoint h1, hyperpoint h2, hyperpoint h3, hyperpoint h4);
void chasmifyPoly(double fac, double fac2, int k);
void shift(hpcshape& sh, double dx, double dy, double dz);
void initPolyForGL();
void extra_vertices();
transmatrix ddi(int a, ld x);
void drawTentacle(hpcshape &h, ld rad, ld var, ld divby);
hyperpoint hpxyzsc(double x, double y, double z);
hyperpoint turtlevertex(int u, double x, double y, double z);
void bshape(hpcshape& sh, PPR prio);
void finishshape();
void bshape(hpcshape& sh, PPR prio, double shzoom, int shapeid, double bonus = 0, flagtype flags = 0);
void copyshape(hpcshape& sh, hpcshape& orig, PPR prio);
void zoomShape(hpcshape& old, hpcshape& newsh, double factor, PPR prio);
void pushShape(usershapelayer& ds);
void make_sidewalls();
void procedural_shapes();
void make_wall(int id, const vector<hyperpoint> vertices, vector<ld> weights = equal_weights);
void reserve_wall3d(int i);
void compute_cornerbonus();
2019-08-09 23:56:00 +00:00
void create_wall3d();
void configure_floorshapes();
void init_floorshapes();
void bshape2(hpcshape& sh, PPR prio, int shapeid, struct matrixlist& m);
2019-12-27 22:01:55 +00:00
void bshape_regular(floorshape &fsh, int id, int sides, ld shift, ld size, cell *model);
2019-08-09 23:56:00 +00:00
void generate_floorshapes_for(int id, cell *c, int siid, int sidir);
void generate_floorshapes();
void make_floor_textures_here();
void finish_apeirogon(hyperpoint center);
2019-08-09 23:56:00 +00:00
vector<hyperpoint> get_shape(hpcshape sh);
void add_cone(ld z0, const vector<hyperpoint>& vh, ld z1);
void add_prism_sync(ld z0, vector<hyperpoint> vh0, ld z1, vector<hyperpoint> vh1);
void add_prism(ld z0, vector<hyperpoint> vh0, ld z1, vector<hyperpoint> vh1);
void shift_last(ld z);
void shift_shape(hpcshape& sh, ld z);
void shift_shape_orthogonally(hpcshape& sh, ld z);
void add_texture(hpcshape& sh);
void make_ha_3d(hpcshape& sh, bool isarmor, ld scale);
void make_humanoid_3d(hpcshape& sh);
void addtri(array<hyperpoint, 3> hs, int kind);
void make_armor_3d(hpcshape& sh, int kind = 1);
void make_foot_3d(hpcshape& sh);
void make_head_only();
void make_head_3d(hpcshape& sh);
void make_paw_3d(hpcshape& sh, hpcshape& legsh);
void make_abody_3d(hpcshape& sh, ld tail);
void make_ahead_3d(hpcshape& sh);
void make_skeletal(hpcshape& sh, ld push = 0);
void make_revolution(hpcshape& sh, int mx = 180, ld push = 0);
void make_revolution_cut(hpcshape &sh, int each = 180, ld push = 0, ld width = 99);
void clone_shape(hpcshape& sh, hpcshape& target);
void animate_bird(hpcshape& orig, hpcshape_animated& animated, ld body);
void slimetriangle(hyperpoint a, hyperpoint b, hyperpoint c, ld rad, int lev);
void balltriangle(hyperpoint a, hyperpoint b, hyperpoint c, ld rad, int lev);
void make_ball(hpcshape& sh, ld rad, int lev);
void make_star(hpcshape& sh, ld rad);
void make_euclidean_sky();
void adjust_eye(hpcshape& eye, hpcshape head, ld shift_eye, ld shift_head, int q, ld zoom=1);
void shift_last_straight(ld z);
void queueball(const transmatrix& V, ld rad, color_t col, eItem what);
void make_shadow(hpcshape& sh);
void make_3d_models();
/* Goldberg parameters */
#if CAP_GP
struct gpdata_t {
vector<array<array<array<transmatrix, 6>, GOLDBERG_LIMIT>, GOLDBERG_LIMIT>> Tf;
2019-08-09 23:56:00 +00:00
transmatrix corners;
ld alpha;
int area;
int pshid[3][8][GOLDBERG_LIMIT][GOLDBERG_LIMIT][8];
2020-07-12 18:43:58 +00:00
int nextid;
2019-08-09 23:56:00 +00:00
};
shared_ptr<gpdata_t> gpdata = nullptr;
2019-08-09 23:56:00 +00:00
#endif
2021-12-11 22:28:05 +00:00
shared_ptr<expansion_analyzer> expansion = nullptr;
2019-08-09 23:56:00 +00:00
int state = 0;
int usershape_state = 0;
/** contains the texture point coordinates for 3D models */
basic_textureinfo models_texture;
geometry_information() { last = NULL; use_count = 0; }
2019-08-09 23:56:00 +00:00
void require_basics() { if(state & 1) return; state |= 1; prepare_basics(); }
void require_shapes() { if(state & 2) return; state |= 2; prepare_shapes(); }
void require_usershapes() { if(usershape_state == usershape_changes) return; usershape_state = usershape_changes; prepare_usershapes(); }
int timestamp;
2020-01-25 23:48:56 +00:00
2022-07-14 09:28:24 +00:00
hpcshape& generate_pipe(ld length, ld width, ePipeEnd endtype = ePipeEnd::sharp);
2021-03-31 08:30:03 +00:00
map<string, unique_ptr<gi_extension>> ext;
/** prevent from being destroyed */
int use_count;
2019-08-09 23:56:00 +00:00
};
#endif
EX subcellshape& get_hsh() {
if(!cgi.heptshape) cgi.heptshape = (unique_ptr<subcellshape>) (new subcellshape);
return *cgi.heptshape;
}
EX void add_wall(int i, const vector<hyperpoint>& h) {
auto& f = get_hsh().faces;
if(isize(f) <= i) f.resize(i+1);
f[i] = h;
}
/** values of hcrossf and hexf for the standard geometry. Since polygons are
* usually drawn in this geometry, the scale in other geometries is usually
* based on comparing these values to the values in the other geometry.
*/
2017-10-27 18:07:58 +00:00
2019-08-24 09:55:45 +00:00
#if HDR
static const ld hcrossf7 = 0.620672, hexf7 = 0.378077, tessf7 = 1.090550, hexhexdist7 = 0.566256;
#endif
2015-08-08 13:57:52 +00:00
EX bool scale_used() { return (shmup::on && geometry == gNormal && BITRUNCATED) ? (cheater || autocheat) : true; }
EX bool is_subcube_based(eVariation var) {
2021-07-13 13:12:03 +00:00
return among(var, eVariation::subcubes, eVariation::dual_subcubes, eVariation::bch, eVariation::bch_oct);
}
2021-07-08 22:07:35 +00:00
EX bool is_reg3_variation(eVariation var) {
return var == eVariation::coxeter;
}
void geometry_information::prepare_basics() {
2015-08-08 13:57:52 +00:00
DEBBI(DF_INIT | DF_POLY | DF_GEOM, ("prepare_basics"));
2017-10-27 18:07:58 +00:00
hexshift = 0;
2016-08-26 09:58:03 +00:00
ld ALPHA = TAU / S7;
2019-12-14 11:18:24 +00:00
2017-10-28 08:04:28 +00:00
ld fmin, fmax;
2020-05-31 01:30:14 +00:00
ld s3, beta;
heptshape = nullptr;
2017-10-28 08:04:28 +00:00
xp_order = 0;
2022-12-08 18:38:06 +00:00
if(arcm::in() && !mproduct)
ginf[gArchimedean].cclass = gcHyperbolic;
2020-07-12 18:52:32 +00:00
dynamicval<eVariation> gv(variation, variation);
bool inv = INVERSE;
if(INVERSE) {
variation = gp::variation_for(gp::param);
println(hlog, "bitruncated = ", BITRUNCATED);
}
2018-08-17 11:29:00 +00:00
2022-12-08 18:38:06 +00:00
if(mhybrid) {
2019-08-18 11:05:47 +00:00
auto t = this;
2022-12-08 18:38:06 +00:00
ld d = mproduct ? 1 : 2;
hybrid::in_underlying_geometry([&] {
t->rhexf = cgi.rhexf / d;
t->hexf = cgi.hexf / d;
t->crossf = cgi.crossf / d;
t->hcrossf = cgi.crossf / d;
t->tessf = cgi.tessf / d;
t->hexvdist = cgi.hexvdist / d;
t->hexhexdist = hdist(xpush0(cgi.hcrossf), xspinpush0(TAU/S7, cgi.hcrossf)) / d;
2019-08-18 11:05:47 +00:00
t->base_distlimit = cgi.base_distlimit-1;
});
goto hybrid_finish;
2019-08-18 11:05:47 +00:00
}
2019-08-24 09:55:45 +00:00
2022-12-08 18:38:06 +00:00
if(embedded_plane) geom3::light_flip(true);
if((sphere || hyperbolic) && WDIM == 3 && !bt::in()) {
rhexf = hexf = 0.378077;
crossf = hcrossf = 0.620672;
tessf = 1.090550;
hexhexdist = 0.566256;
goto finish;
}
2020-05-31 01:30:14 +00:00
s3 = S3;
2020-05-31 14:18:44 +00:00
if(fake::in() && !arcm::in()) s3 = fake::around;
2020-05-31 01:30:14 +00:00
beta = (S3 >= OINF && !fake::in()) ? 0 : TAU/s3;
2016-08-26 09:58:03 +00:00
2020-05-31 01:30:14 +00:00
tessf = euclid ? 1 : edge_of_triangle_with_angles(beta, M_PI/S7, M_PI/S7);
2015-08-08 13:57:52 +00:00
if(elliptic && S7 == 4 && !fake::in()) tessf = 90._deg;
2015-08-08 13:57:52 +00:00
hcrossf = euclid ? tessf / 2 / sin(M_PI/s3) : edge_of_triangle_with_angles(90._deg, M_PI/S7, beta/2);
2020-09-11 09:22:26 +00:00
if(S3 >= OINF) hcrossf = 10;
crossf = BITRUNCATED ? hcrossf : tessf;
2015-08-08 13:57:52 +00:00
fmin = 0, fmax = tessf;
for(int p=0; p<100; p++) {
ld f = (fmin+fmax) / 2;
2018-08-19 14:28:36 +00:00
hyperpoint H = xpush0(f);
hyperpoint H1 = spin(TAU/S7) * H;
2018-08-19 14:28:36 +00:00
hyperpoint H2 = xpush0(tessf-f);
2015-08-08 13:57:52 +00:00
ld v1 = intval(H, H1), v2 = intval(H, H2);
2020-05-31 01:30:14 +00:00
if(fake::in() && WDIM == 2) {
hexvdist = hdist(xpush0(f), xspinpush0(ALPHA/2, hcrossf));
v2 = hdist(
spin(90._deg/S3) * xpush0(hexvdist),
spin(-90._deg/S3) * xpush0(hexvdist)
2020-05-31 01:30:14 +00:00
);
v1 = hdist(
spin(M_PI/S7) * xpush0(f),
spin(-M_PI/S7) * xpush0(f)
);
}
2015-08-08 13:57:52 +00:00
if(v1 < v2) fmin = f; else fmax = f;
}
hexf = fmin;
rhexf = BITRUNCATED ? hexf : hcrossf;
edgelen = hdist(xpush0(rhexf), xspinpush0(TAU/S7, rhexf));
2017-10-29 16:12:40 +00:00
2019-11-27 00:01:20 +00:00
if(BITRUNCATED && !(S7&1))
2017-10-28 08:04:28 +00:00
hexshift = ALPHA/2 + ALPHA * ((S7-1)/2) + M_PI;
2019-11-27 00:01:20 +00:00
2017-10-28 08:04:28 +00:00
finish:
2017-10-28 23:57:34 +00:00
2018-08-19 14:28:36 +00:00
hexvdist = hdist(xpush0(hexf), xspinpush0(ALPHA/2, hcrossf));
2017-10-29 16:12:40 +00:00
2020-05-31 01:30:14 +00:00
hexhexdist = fake::in() ?
2 * hdist0(mid(xspinpush0(M_PI/S6, hexvdist), xspinpush0(-M_PI/S6, hexvdist)))
: hdist(xpush0(crossf), xspinpush0(TAU/S7, crossf));
2020-05-31 01:30:14 +00:00
2019-05-12 23:57:40 +00:00
DEBB(DF_GEOM | DF_POLY,
(format("S7=%d S6=%d hexf = " LDF" hcross = " LDF" tessf = " LDF" hexshift = " LDF " hexhex = " LDF " hexv = " LDF "\n", S7, S6, hexf, hcrossf, tessf, hexshift,
hexhexdist, hexvdist)));
2017-10-29 16:12:40 +00:00
base_distlimit = ginf[geometry].distlimit[!BITRUNCATED];
2018-08-28 17:05:57 +00:00
2019-02-17 17:28:20 +00:00
#if CAP_GP
2020-07-12 18:52:32 +00:00
gp::compute_geometry(inv);
2019-02-17 17:28:20 +00:00
#endif
#if CAP_IRR
2018-07-16 18:05:23 +00:00
irr::compute_geometry();
2019-02-17 17:28:20 +00:00
#endif
#if CAP_ARCM
if(arcm::in()) {
2020-05-31 14:18:44 +00:00
auto& ac = arcm::current_or_fake();
if(fake::in()) ac = arcm::current;
ac.compute_geometry();
crossf = hcrossf7 * ac.scale();
hexvdist = ac.scale() * .5;
rhexf = ac.scale() * .5;
edgelen = ac.edgelength;
2018-08-28 17:05:57 +00:00
}
2019-02-17 17:28:20 +00:00
#endif
#if CAP_BT
if(bt::in()) hexvdist = rhexf = 1, tessf = 1, scalefactor = 1, crossf = hcrossf7;
if(geometry == gHoroRec || kite::in() || sol || nil || nih) hexvdist = rhexf = .5, tessf = .5, scalefactor = .5, crossf = hcrossf7/2;
if(bt::in()) scalefactor *= min<ld>(vid.binary_width, 1), crossf *= min<ld>(vid.binary_width, 1);
#endif
2020-04-10 08:52:24 +00:00
#if MAXMDIM >= 4
2020-04-05 08:53:34 +00:00
if(reg3::in()) reg3::generate();
if(euc::in(3)) euc::generate();
#if CAP_SOLV
else if(sn::in()) sn::create_faces();
#endif
#if CAP_BT
else if(bt::in()) bt::create_faces();
#endif
else if(nil) nilv::create_faces();
2020-04-10 08:52:24 +00:00
#endif
hybrid_finish:
2019-08-18 11:05:47 +00:00
2018-08-28 17:05:57 +00:00
scalefactor = crossf / hcrossf7;
orbsize = crossf;
2020-05-31 01:30:14 +00:00
if(fake::in() && WDIM == 2) {
auto& u = *fake::underlying_cgip;
geometry = fake::underlying;
2020-06-03 13:11:20 +00:00
ld orig = xpush0(u.hcrossf)[0] / xpush0(u.hcrossf)[GDIM];
2020-05-31 01:30:14 +00:00
geometry = gFake;
2020-06-03 13:11:20 +00:00
ld our = xpush0(hcrossf)[0] / xpush0(hcrossf)[GDIM];
2020-05-31 01:30:14 +00:00
fake::scale = our / orig;
// if(debugflags & DF_GEOM)
}
if(fake::in() && WDIM == 3) {
2020-05-16 09:22:09 +00:00
auto& u = fake::underlying_cgip;
crossf = u->crossf * fake::scale;
scalefactor = u->scalefactor * fake::scale;
orbsize = u->orbsize * fake::scale;
hexf = u->hexf * fake::scale;
rhexf = u->rhexf * fake::scale;
hexvdist = u->hexvdist * fake::scale;
2020-05-31 01:30:14 +00:00
hcrossf = u->hcrossf * fake::scale;
2020-05-16 09:22:09 +00:00
}
2020-05-30 18:00:09 +00:00
if(arb::in()) {
2020-06-02 00:29:31 +00:00
auto csc = arb::current_or_slided().cscale;
2020-05-30 18:00:09 +00:00
scalefactor = csc;
hcrossf = crossf = orbsize = hcrossf7 * csc;
hexf = rhexf = hexvdist = csc * arb::current_or_slided().floor_scale;
base_distlimit = arb::current.range;
2020-05-30 18:00:09 +00:00
}
2021-07-06 23:48:20 +00:00
2022-02-17 20:00:10 +00:00
#if MAXMDIM >= 4
if(is_subcube_based(variation)) {
2021-07-06 23:48:20 +00:00
scalefactor /= reg3::subcube_count;
orbsize /= reg3::subcube_count;
}
2022-02-17 20:00:10 +00:00
#endif
2020-05-16 09:22:09 +00:00
2022-12-08 18:38:06 +00:00
if(geom3::euc_in_hyp()) {
scalefactor *= exp(-vid.depth);
}
2022-12-13 22:26:44 +00:00
if(geom3::euc_in_noniso()) scalefactor *= geom3::euclid_embed_scale;
2022-12-08 18:38:06 +00:00
if(geom3::sph_in_euc()) scalefactor *= (1 + vid.depth);
if(geom3::sph_in_hyp()) scalefactor *= sinh(1 + vid.depth);
if(scale_used()) {
scalefactor *= vid.creature_scale;
orbsize *= vid.creature_scale;
}
2018-08-28 17:05:57 +00:00
zhexf = BITRUNCATED ? hexf : crossf* .55;
if(scale_used()) zhexf *= vid.creature_scale;
2019-05-15 16:43:40 +00:00
if(WDIM == 2 && GDIM == 3) zhexf *= 1.5, orbsize *= 1.2;
2018-08-28 17:05:57 +00:00
2022-10-06 10:26:17 +00:00
floorrad0 = hexvdist* (GDIM == 3 ? 1 : 1 - 0.08 * global_boundary_ratio);
floorrad1 = rhexf * (GDIM == 3 ? 1 : 1 - 0.06 * global_boundary_ratio);
2018-08-28 17:05:57 +00:00
if(euc::in(2,4)) {
2018-08-28 17:05:57 +00:00
if(!BITRUNCATED)
floorrad0 = floorrad1 = rhexf * (GDIM == 3 ? 1 : .94);
2018-08-28 17:05:57 +00:00
else
floorrad0 = hexvdist * (GDIM == 3 ? 1 : .9),
floorrad1 = rhexf * (GDIM == 3 ? 1 : .8);
2018-08-28 17:05:57 +00:00
}
2019-08-18 19:16:27 +00:00
plevel = vid.plevel_factor * scalefactor;
2019-08-26 12:16:55 +00:00
single_step = 1;
2022-12-08 18:38:06 +00:00
if(mhybrid && !mproduct) {
2020-10-15 14:33:52 +00:00
#if CAP_ARCM
2019-08-27 16:59:26 +00:00
if(hybrid::underlying == gArchimedean)
arcm::current.get_step_values(psl_steps, single_step);
2020-10-15 14:33:52 +00:00
#else
if(0) ;
#endif
2019-08-26 13:39:37 +00:00
else {
single_step = S3 * S7 - 2 * S7 - 2 * S3;
psl_steps = 2 * S7;
if(BITRUNCATED) psl_steps *= S3;
if(inv) psl_steps = 2 * S3;
if(single_step < 0) single_step = -single_step;
2019-08-26 13:39:37 +00:00
}
DEBB(DF_GEOM | DF_POLY, ("steps = ", psl_steps, " / ", single_step));
plevel = M_PI * single_step / psl_steps;
2019-08-26 12:16:55 +00:00
}
2019-08-18 19:16:27 +00:00
set_sibling_limit();
2022-12-08 18:38:06 +00:00
geom3::light_flip(false);
#if CAP_BT && MAXMDIM >= 4
if(bt::in()) bt::build_tmatrix();
#endif
prepare_compute3();
if(hyperbolic && &currfp != &fieldpattern::fp_invalid)
currfp.analyze();
2022-12-08 18:38:06 +00:00
heptmove.resize(S7);
hexmove.resize(S7);
invhexmove.resize(S7);
for(int d=0; d<S7; d++)
heptmove[d] = spin(-d * ALPHA) * lxpush(tessf) * spin(M_PI);
for(int d=0; d<S7; d++)
hexmove[d] = spin(hexshift-d * ALPHA) * lxpush(-crossf)* spin(M_PI);
for(int d=0; d<S7; d++) invhexmove[d] = iso_inverse(hexmove[d]);
gp::prepare_matrices(inv);
#if CAP_SOLV
if(asonov::in()) {
asonov::prepare();
asonov::prepare_walls();
}
#endif
2015-08-08 13:57:52 +00:00
}
EX transmatrix xspinpush(ld dir, ld dist) {
2022-12-08 18:38:06 +00:00
if(WDIM == 2 && GDIM == 3) {
geom3::light_flip(true);
transmatrix T = spin(dir) * xpush(dist) * spin(-dir);
geom3::light_flip(false);
swapmatrix(T);
return T;
}
else if(euclid)
2019-02-26 13:34:37 +00:00
return eupush(cos(dir) * dist, -sin(dir) * dist);
2017-03-23 10:53:57 +00:00
else
return spin(dir) * xpush(dist) * spin(-dir);
2015-08-08 13:57:52 +00:00
}
2019-08-09 20:07:03 +00:00
EX purehookset hooks_swapdim;
EX namespace geom3 {
2017-03-23 10:53:57 +00:00
// Here we convert between the following parameters:
// abslev: level below the plane
// lev: level above the world (abslev = depth-lev)
// projection: projection parameter
// factor: zoom factor
2015-08-08 13:57:52 +00:00
2021-05-23 13:57:39 +00:00
EX ld abslev_to_projection(ld abslev) {
if(sphere || euclid) return vid.camera+abslev;
return tanh(abslev) / tanh(vid.camera);
2015-08-08 13:57:52 +00:00
}
2017-03-23 10:53:57 +00:00
ld projection_to_abslev(ld proj) {
if(sphere || euclid) return proj-vid.camera;
2017-03-23 10:53:57 +00:00
// tanh(abslev) / tanh(camera) = proj
return atanh(proj * tanh(vid.camera));
2015-08-08 13:57:52 +00:00
}
2017-03-23 10:53:57 +00:00
ld lev_to_projection(ld lev) {
return abslev_to_projection(vid.depth - lev);
2015-08-08 13:57:52 +00:00
}
2017-03-23 10:53:57 +00:00
ld projection_to_factor(ld proj) {
return lev_to_projection(0) / proj;
}
EX ld factor_to_projection(ld fac) {
2017-03-23 10:53:57 +00:00
return lev_to_projection(0) / fac;
}
EX ld lev_to_factor(ld lev) {
2022-12-08 18:38:06 +00:00
if(mproduct) return -lev;
2019-05-08 16:33:08 +00:00
if(WDIM == 3) return lev;
if(GDIM == 3) return vid.depth - lev;
2017-03-23 10:53:57 +00:00
return projection_to_factor(lev_to_projection(lev));
}
EX ld factor_to_lev(ld fac) {
2022-12-08 18:38:06 +00:00
if(mproduct) return -fac;
2019-08-15 13:05:43 +00:00
if(GDIM == 3) return fac;
return vid.depth - projection_to_abslev(factor_to_projection(fac));
2017-03-23 10:53:57 +00:00
}
EX void do_auto_eye() {
2019-06-01 17:58:07 +00:00
if(!vid.auto_eye) return;
auto& cs = getcs();
if(cs.charid < 4)
vid.eye = cgi.eyelevel_human;
else if(cs.charid < 8)
vid.eye = cgi.eyelevel_dog;
else if(cs.charid == 8)
vid.eye = cgi.eyelevel_familiar;
}
2017-03-23 10:53:57 +00:00
// how should we scale at level lev
EX ld scale_at_lev(ld lev) {
2017-03-23 10:53:57 +00:00
if(sphere || euclid) return 1;
return cosh(vid.depth - lev);
2017-03-23 10:53:57 +00:00
}
EX string invalid;
2017-03-23 10:53:57 +00:00
EX ld actual_wall_height() {
2022-12-08 18:38:06 +00:00
if(mhybrid) return cgi.plevel;
2019-02-17 17:28:20 +00:00
#if CAP_GP
if(GOLDBERG && vid.gp_autoscale_heights)
return vid.wall_height * min<ld>(4 / hypot_d(2, gp::next), 1);
2019-02-17 17:28:20 +00:00
#endif
return vid.wall_height;
2018-04-23 10:34:14 +00:00
}
EX }
2018-04-23 10:34:14 +00:00
void geometry_information::prepare_compute3() {
using namespace geom3;
2019-05-12 23:57:40 +00:00
DEBBI(DF_INIT | DF_POLY | DF_GEOM, ("geom3::compute"));
// tanh(depth) / tanh(camera) == pconf.alpha
2017-03-23 10:53:57 +00:00
invalid = "";
2022-12-08 18:38:06 +00:00
if(GDIM == 3 || flipped) ;
else if(vid.tc_alpha < vid.tc_depth && vid.tc_alpha < vid.tc_camera)
pconf.alpha = tan_auto(vid.depth) / tan_auto(vid.camera);
else if(vid.tc_depth < vid.tc_alpha && vid.tc_depth < vid.tc_camera) {
ld v = pconf.alpha * tan_auto(vid.camera);
if(hyperbolic && (v<1e-6-12 || v>1-1e-12)) invalid = XLAT("cannot adjust depth"), vid.depth = vid.camera;
else vid.depth = atan_auto(v);
2017-03-23 10:53:57 +00:00
}
else {
ld v = tan_auto(vid.depth) / pconf.alpha;
if(hyperbolic && (v<1e-12-1 || v>1-1e-12)) invalid = XLAT("cannot adjust camera"), vid.camera = vid.depth;
else vid.camera = atan_auto(v);
2017-03-23 10:53:57 +00:00
}
if(fabs(pconf.alpha) < 1e-6) invalid = XLAT("does not work with perfect Klein");
2017-03-23 10:53:57 +00:00
if(invalid != "") {
INFDEEP = .7;
BOTTOM = .8;
HELLSPIKE = .85;
LAKE = .9;
2019-05-08 16:33:08 +00:00
FLOOR = 1;
2017-03-23 10:53:57 +00:00
WALL = 1.25;
SLEV[0] = 1;
SLEV[1] = 1.08;
SLEV[2] = 1.16;
SLEV[3] = 1.24;
FLATEYE = 1.03;
LEG1 = 1.025;
LEG = 1.05;
LEG3 = 1.075;
GROIN = 1.09;
GROIN1 = 1.105;
GHOST = 1.1;
BODY = 1.15;
BODY1 = 1.151;
BODY2 = 1.152;
BODY3 = 1.153;
2017-03-23 10:53:57 +00:00
NECK1 = 1.16;
NECK = 1.17;
NECK3 = 1.18;
2019-02-27 00:15:40 +00:00
HEAD = 1.188;
HEAD1= 1.189;
HEAD2= 1.190;
2019-04-20 23:00:15 +00:00
HEAD3= 1.191;
2017-03-23 10:53:57 +00:00
ABODY = 1.08;
AHEAD = 1.12;
BIRD = 1.20;
}
else {
INFDEEP = GDIM == 3 ? (sphere ? 90._deg : +5) : (euclid || sphere) ? 0.01 : lev_to_projection(0) * tanh(vid.camera);
2018-04-23 10:34:14 +00:00
ld wh = actual_wall_height();
WALL = lev_to_factor(wh);
2019-05-08 16:33:08 +00:00
FLOOR = lev_to_factor(0);
2017-03-23 10:53:57 +00:00
human_height = vid.human_wall_ratio * wh;
if(WDIM == 3) human_height = scalefactor * vid.height_width / 2;
2022-12-08 18:38:06 +00:00
if(mhybrid) human_height = min(human_height, cgi.plevel * .9);
2019-05-08 16:33:08 +00:00
ld reduce = (WDIM == 3 ? human_height / 2 : 0);
LEG0 = lev_to_factor(human_height * .0 - reduce);
LEG1 = lev_to_factor(human_height * .1 - reduce);
LEG = lev_to_factor(human_height * .2 - reduce);
LEG3 = lev_to_factor(human_height * .3 - reduce);
GROIN = lev_to_factor(human_height * .4 - reduce);
GROIN1= lev_to_factor(human_height * .5 - reduce);
BODY = lev_to_factor(human_height * .6 - reduce);
BODY1 = lev_to_factor(human_height * .61 - reduce);
BODY2 = lev_to_factor(human_height * .62 - reduce);
BODY3 = lev_to_factor(human_height * .63 - reduce);
NECK1 = lev_to_factor(human_height * .7 - reduce);
NECK = lev_to_factor(human_height * .8 - reduce);
NECK3 = lev_to_factor(human_height * .9 - reduce);
2019-04-20 23:00:15 +00:00
HEAD = lev_to_factor(human_height * .97 - reduce);
HEAD1 = lev_to_factor(human_height * .98 - reduce);
HEAD2 = lev_to_factor(human_height * .99 - reduce);
HEAD3 = lev_to_factor(human_height - reduce);
2017-03-23 10:53:57 +00:00
2019-08-15 13:05:43 +00:00
reduce = (GDIM == 3 ? human_height * .3 : 0);
2022-12-08 20:20:17 +00:00
int sgn = vid.wall_height > 0 ? 1 : -1;
2022-12-13 22:26:44 +00:00
ld ees = geom3::euc_in_noniso() ? geom3::euclid_embed_scale : 1;
2022-12-08 20:20:17 +00:00
2022-12-13 18:04:43 +00:00
STUFF = lev_to_factor(0) - sgn * max(orbsize * ees * 0.3, zhexf * ees * .6);
2019-05-08 16:33:08 +00:00
ABODY = lev_to_factor(human_height * .4 - reduce);
ALEG0 = lev_to_factor(human_height * .0 - reduce);
ALEG = lev_to_factor(human_height * .2 - reduce);
AHEAD = lev_to_factor(human_height * .6 - reduce);
BIRD = lev_to_factor(WDIM == 3 ? 0 : (vid.human_wall_ratio+1)/2 * wh * .8);
2019-05-12 18:06:24 +00:00
GHOST = lev_to_factor(WDIM == 3 ? 0 : human_height * .5);
2017-03-23 10:53:57 +00:00
FLATEYE = lev_to_factor(human_height * .15);
slev = vid.rock_wall_ratio * wh / 3;
2017-03-23 10:53:57 +00:00
for(int s=0; s<=3; s++)
SLEV[s] = lev_to_factor(vid.rock_wall_ratio * wh * s/3);
2022-12-08 21:15:57 +00:00
LAKE = lev_to_factor(sgn * -vid.lake_top);
SHALLOW = lev_to_factor(sgn * -.4);
HELLSPIKE = lev_to_factor(sgn * -(vid.lake_top+vid.lake_bottom)/2);
BOTTOM = lev_to_factor(sgn * -vid.lake_bottom);
LOWSKY = lev_to_factor(2 * wh);
2019-05-24 23:36:49 +00:00
HIGH = LOWSKY;
HIGH2 = lev_to_factor(3 * wh);
2022-12-08 20:20:17 +00:00
SKY = LOWSKY - sgn * 5;
2022-12-08 18:38:06 +00:00
if(geom3::mgclass() == gcSphere && geom3::ggclass() != gcSphere) {
ld max_high = lerp(-FLOOR, -1, 0.8);
ld max_high2 = lerp(-FLOOR, -1, 0.9);
if(HIGH < max_high) HIGH = max_high;
if(HIGH2 < max_high2) HIGH2 = max_high2;
if(LOWSKY < max_high) LOWSKY = max_high;
if(SKY < max_high) SKY = max_high;
if(vid.wall_height < 0) {
SKY = -3 * vid.wall_height;
LOWSKY = 1.75 * SKY;
}
2022-12-08 21:15:57 +00:00
if(SHALLOW < max_high) SHALLOW = max_high;
if(LAKE < max_high) LAKE = max_high;
if(BOTTOM < max_high2) BOTTOM = max_high2;
if(HELLSPIKE < max_high) HELLSPIKE = max_high;
if(sgn < 0) INFDEEP = -1;
2022-12-08 18:38:06 +00:00
}
2022-12-13 18:04:43 +00:00
if(geom3::euc_in_hyp() && sgn < 0) INFDEEP = FLOOR - 5;
2017-03-23 10:53:57 +00:00
}
}
2019-08-09 19:00:52 +00:00
EX namespace geom3 {
2022-12-08 18:38:06 +00:00
#if HDR
enum eSpatialEmbedding {
2022-12-15 11:48:25 +00:00
seNone,
2022-12-08 18:38:06 +00:00
seDefault,
seLowerCurvature,
seMuchLowerCurvature,
2022-12-13 18:04:43 +00:00
seProduct,
2022-12-13 22:26:44 +00:00
seNil,
seSol, seNIH, seSolN,
seNIH_inv
2022-12-08 18:38:06 +00:00
};
#endif
EX vector<pair<string, string>> spatial_embedding_options = {
2022-12-15 12:11:23 +00:00
{"2D engine", "Use HyperRogue's 2D engine to simulate same curvature. Works well in top-down and third-person perspective."},
{"same curvature", "Embed as an equidistant surface in the 3D version of the same geometry."},
{"lower curvature", "Embed as a surface in a space of lower curvature."},
{"much lower curvature", "Embed sphere as a sphere in hyperbolic space."},
2022-12-13 18:04:43 +00:00
{"product", "Add one extra dimension in the Euclidean way."},
2022-12-13 22:26:44 +00:00
{"Nil", "Embed into Nil. Works only with Euclidean. You need to set the variation to Pure."},
{"Sol", "Embed into Sol. Works only with Euclidean. You need to set the variation to Pure."},
{"stretched hyperbolic", "Embed into stretched hyperbolic geometry. Works only with Euclidean. You need to set the variation to Pure."},
{"stretched Sol", "Embed into stretched Sol geometry. Works only with Euclidean. You need to set the variation to Pure."},
2022-12-08 18:38:06 +00:00
};
2022-12-15 11:48:25 +00:00
EX eSpatialEmbedding spatial_embedding = seDefault;
2022-12-13 18:04:43 +00:00
EX ld euclid_embed_scale = 1;
2022-12-15 11:48:25 +00:00
EX bool auto_configure = true;
2022-12-15 12:11:23 +00:00
EX bool flat_embedding = false;
EX bool inverted_embedding = false;
2022-12-15 11:48:25 +00:00
2022-12-15 12:11:23 +00:00
EX bool supports_flat() { return spatial_embedding == seDefault; }
EX bool supports_invert() { return among(spatial_embedding, seDefault, seLowerCurvature, seMuchLowerCurvature, seNil, seSol, seNIH, seSolN); }
2022-12-08 18:38:06 +00:00
EX vector<geometryinfo> ginf_backup;
EX eGeometryClass mgclass() {
return (embedded_plane ? ginf_backup : ginf)[geometry].g.kind;
}
EX eGeometryClass ggclass() {
return (flipped ? ginf_backup : ginf)[geometry].g.kind;
}
EX bool euc_in_hyp() {
return ggclass() == gcHyperbolic && mgclass() == gcEuclid;
}
2022-12-13 18:04:43 +00:00
EX bool euc_in_nil() {
return ggclass() == gcNil && mgclass() == gcEuclid;
}
2022-12-13 22:26:44 +00:00
EX bool euc_in_solnih() {
return among(ggclass(), gcSol, gcNIH, gcSolN) && mgclass() == gcEuclid;
}
EX bool hyp_in_solnih() {
return among(ggclass(), gcSol, gcNIH, gcSolN) && mgclass() == gcHyperbolic;
}
2022-12-13 22:26:44 +00:00
EX bool euc_in_noniso() {
2022-12-15 18:21:16 +00:00
return among(ggclass(), gcNil, gcSol, gcNIH, gcSolN) && mgclass() == gcEuclid;
2022-12-13 22:26:44 +00:00
}
2022-12-08 18:38:06 +00:00
EX bool sph_in_euc() {
return ggclass() == gcEuclid && mgclass() == gcSphere;
}
EX bool sph_in_hyp() {
return ggclass() == gcHyperbolic && mgclass() == gcSphere;
}
2022-12-15 20:03:20 +00:00
EX bool sph_in_low() {
return mgclass() == gcSphere && among(ggclass(), gcHyperbolic, gcEuclid);
}
2022-12-08 18:38:06 +00:00
EX bool in_product() {
return ggclass() == gcProduct;
}
EX bool same_in_same() {
return mgclass() == ggclass();
}
EX bool flipped;
EX void light_flip(bool f) {
if(f != flipped) {
swap(ginf[geometry].g, geom3::ginf_backup[geometry].g);
swap(ginf[geometry].flags, geom3::ginf_backup[geometry].flags);
flipped = f;
}
}
2022-12-08 18:38:06 +00:00
#if HDR
template<class T> auto in_flipped(const T& f) -> decltype(f()) {
light_flip(true);
finalizer ff([] { light_flip(false); });
return f();
}
2022-12-15 22:13:00 +00:00
template<class T> auto in_not_flipped(const T& f) -> decltype(f()) {
light_flip(false);
finalizer ff([] { light_flip(true); });
return f();
}
2022-12-08 18:38:06 +00:00
#define IPF(x) geom3::in_flipped([&] { return (x); })
#endif
EX void apply_always3() {
if(!vid.always3 && !ginf_backup.empty()) {
ginf = ginf_backup;
ginf_backup.clear();
}
if(vid.always3 && ginf_backup.empty()) {
ginf_backup = ginf;
for(geometryinfo& gi: ginf) {
auto &g = gi.g;
if(vid.always3 && g.gameplay_dimension == 2 && g.graphical_dimension == 2) {
g.graphical_dimension++;
g.homogeneous_dimension++;
g.sig[3] = g.sig[2];
g.sig[2] = g.sig[1];
if(spatial_embedding == seProduct && g.kind != gcEuclid) {
g.kind = gcProduct;
g.homogeneous_dimension--;
g.sig[2] = g.sig[3];
}
2022-12-15 12:11:23 +00:00
if(spatial_embedding == seLowerCurvature) {
2022-12-08 18:38:06 +00:00
if(g.kind == gcEuclid) g = ginf[gSpace534].g;
if(g.kind == gcSphere) g = ginf[gCubeTiling].g;
g.gameplay_dimension = 2;
}
2022-12-15 12:11:23 +00:00
if(spatial_embedding == seMuchLowerCurvature) {
2022-12-08 18:38:06 +00:00
g = ginf[gSpace534].g;
g.gameplay_dimension = 2;
}
2022-12-13 18:04:43 +00:00
bool ieuclid = g.kind == gcEuclid;
if(spatial_embedding == seNil && ieuclid) {
2022-12-13 18:04:43 +00:00
g = ginf[gNil].g;
g.gameplay_dimension = 2;
}
2022-12-13 22:26:44 +00:00
bool ieuc_or_binary = ieuclid || (gi.flags & qBINARY);
if(spatial_embedding == seSol && ieuc_or_binary) {
2022-12-13 22:26:44 +00:00
g = ginf[gSol].g;
g.gameplay_dimension = 2;
}
if(spatial_embedding == seNIH && ieuc_or_binary) {
2022-12-13 22:26:44 +00:00
g = ginf[gNIH].g;
g.gameplay_dimension = 2;
}
if(spatial_embedding == seSolN && ieuc_or_binary) {
2022-12-13 22:26:44 +00:00
g = ginf[gSolN].g;
g.gameplay_dimension = 2;
}
2022-12-08 18:38:06 +00:00
}
}
}
}
#if MAXMDIM >= 4
2019-08-09 19:00:52 +00:00
EX void switch_always3() {
2019-05-28 23:09:38 +00:00
if(dual::split(switch_always3)) return;
2020-10-15 14:33:52 +00:00
#if CAP_GL && CAP_RUG
if(rug::rugged) rug::close();
2020-07-03 12:42:33 +00:00
#endif
vid.always3 = !vid.always3;
apply_always3();
swapmatrix(View);
callhooks(hooks_swapdim);
}
#endif
2019-08-09 19:00:52 +00:00
EX void switch_tpp() {
2019-05-28 23:09:38 +00:00
if(dual::split(switch_fpp)) return;
if(pmodel == mdDisk && pconf.camera_angle) {
vid.yshift = 0;
pconf.camera_angle = 0;
pconf.xposition = 0;
pconf.yposition = 0;
pconf.scale = 1;
vid.fixed_facing = false;
}
else {
vid.yshift = -0.3;
pconf.camera_angle = -45;
pconf.scale = 18/16. * vid.xres / vid.yres / multi::players;
pconf.xposition = 0;
pconf.yposition = -0.9;
vid.fixed_facing = true;
vid.fixed_facing_dir = 90;
}
}
2019-08-09 19:00:52 +00:00
EX void switch_fpp() {
#if MAXMDIM >= 4
2020-10-15 14:33:52 +00:00
#if CAP_GL && CAP_RUG
if(rug::rugged) rug::close();
2020-07-03 12:42:33 +00:00
#endif
2019-05-28 23:09:38 +00:00
if(dual::split(switch_fpp)) return;
2019-05-28 23:20:25 +00:00
check_cgi(); cgi.require_basics();
2020-09-16 03:57:05 +00:00
View = iso_inverse(models::rotmatrix()) * View;
if(!vid.always3) {
vid.always3 = true;
apply_always3();
2019-05-28 23:20:25 +00:00
ld ms = min<ld>(cgi.scalefactor, 1);
2022-12-08 18:38:06 +00:00
vid.depth = ms;
vid.wall_height = 1.5 * ms;
2022-12-08 18:38:06 +00:00
if(sphere && same_in_same()) {
vid.depth = 30 * degree;
vid.wall_height = 60 * degree;
}
vid.human_wall_ratio = 0.8;
2022-12-08 18:38:06 +00:00
if(mgclass() == gcEuclid && allowIncreasedSight() && vid.use_smart_range == 0) {
2019-05-28 23:20:25 +00:00
genrange_bonus = gamerange_bonus = sightrange_bonus = cgi.base_distlimit * 3/2;
}
vid.camera = 0;
2022-12-08 18:38:06 +00:00
vid.eye = 0;
if(sph_in_euc() || sph_in_hyp()) {
vid.depth = 0;
vid.wall_height = -1;
vid.eye = 0.5;
2022-12-15 12:11:23 +00:00
if(inverted_embedding) {
2022-12-08 18:38:06 +00:00
vid.wall_height = 1.4;
vid.eye = -0.2;
vid.depth = 0.5;
}
}
2022-12-15 12:11:23 +00:00
if(spatial_embedding == seDefault && flat_embedding) {
2022-12-08 18:38:06 +00:00
vid.eye -= vid.depth / 2;
vid.depth = 0;
}
2022-12-15 12:11:23 +00:00
if(spatial_embedding == seDefault && !flat_embedding && inverted_embedding) {
2022-12-08 18:38:06 +00:00
vid.eye -= vid.depth * 1.5;
vid.depth *= -1;
}
2022-12-15 12:11:23 +00:00
if((euc_in_hyp() || euc_in_noniso()) && inverted_embedding) {
2022-12-13 22:26:44 +00:00
vid.wall_height *= -1;
vid.eye = 2 * vid.depth;
}
2022-12-08 20:36:22 +00:00
if(msphere && spatial_embedding == seProduct) {
vid.depth = 0;
vid.wall_height = 2;
vid.eye = -2;
}
2022-12-13 18:04:43 +00:00
if(pmodel == mdDisk) pmodel = nonisotropic ? mdGeodesic : mdPerspective;
swapmatrix(View);
swapmatrix(current_display->which_copy);
callhooks(hooks_swapdim);
for(auto m: allmaps) m->on_dim_change();
2020-05-29 08:47:18 +00:00
if(cgflags & qIDEAL && vid.texture_step < 32)
vid.texture_step = 32;
#if CAP_RACING
racing::player_relative = true;
#endif
2022-12-08 18:38:06 +00:00
check_cgi();
cgi.prepare_basics();
}
else {
vid.always3 = false;
apply_always3();
vid.wall_height = .3;
vid.human_wall_ratio = .7;
vid.camera = 1;
vid.depth = 1;
if(pmodel == mdPerspective) pmodel = mdDisk;
swapmatrix(View);
swapmatrix(current_display->which_copy);
callhooks(hooks_swapdim);
for(auto m: allmaps) m->on_dim_change();
}
View = models::rotmatrix() * View;
#endif
}
2019-08-09 19:00:52 +00:00
EX }
2015-08-08 13:57:52 +00:00
2019-08-09 20:37:11 +00:00
EX geometry_information *cgip;
EX map<string, geometry_information> cgis;
#if HDR
#define cgi (*cgip)
#endif
EX int last_texture_step;
2019-06-18 14:01:28 +00:00
2019-06-18 14:16:09 +00:00
int ntimestamp;
2021-03-21 10:33:25 +00:00
EX hookset<void(string&)> hooks_cgi_string;
EX string cgi_string() {
string s;
auto V = [&] (string a, string b) { s += a; s += ": "; s += b; s += "; "; };
V("GEO", its(int(geometry)));
V("VAR", its(int(variation)));
2022-04-24 20:09:54 +00:00
if(arb::in()) {
2020-06-02 00:29:31 +00:00
for(auto& sl: arb::current.sliders)
V("AS", fts(sl.current));
2022-04-24 20:09:54 +00:00
for(auto& sl: arb::current.intsliders)
V("AS", its(sl.current));
2020-06-02 00:29:31 +00:00
}
2020-05-15 09:46:26 +00:00
if(fake::in()) {
2020-05-31 01:30:14 +00:00
if(hyperbolic) V("H", fts(fake::around));
if(euclid) V("E", fts(fake::around));
if(sphere) V("S", fts(fake::around));
2020-05-15 09:46:26 +00:00
V("G", FPIU(cgi_string()));
2020-05-28 11:44:02 +00:00
return s;
2020-05-15 09:46:26 +00:00
}
2020-07-12 18:52:32 +00:00
if(GOLDBERG_INV) V("GP", its(gp::param.first) + "," + its(gp::param.second));
if(IRREGULAR) V("IRR", its(irr::irrid));
2022-02-17 20:00:10 +00:00
#if MAXMDIM >= 4
if(is_subcube_based(variation)) V("SC", its(reg3::subcube_count));
if(variation == eVariation::coxeter) V("COX", its(reg3::coxeter_param));
2022-02-17 20:00:10 +00:00
#endif
2020-10-15 14:33:52 +00:00
#if CAP_ARCM
if(arcm::in()) V("ARCM", arcm::current.symbol);
2020-10-15 14:33:52 +00:00
#endif
2019-12-27 21:59:02 +00:00
if(arb::in()) V("ARB", its(arb::current.order));
2022-05-01 09:18:39 +00:00
if(arb::in()) V("AP", its(arb::apeirogon_simplified_display));
2022-09-14 20:53:39 +00:00
if(arb::in()) V("F", its(arb::extended_football));
2022-10-06 10:26:17 +00:00
V("BR", fts(global_boundary_ratio));
if(cryst) V("CRYSTAL", its(ginf[gCrystal].sides) + its(ginf[gCrystal].vertex));
if(bt::in() || GDIM == 3) V("WQ", its(vid.texture_step));
2022-12-08 18:38:06 +00:00
if(mhybrid) {
2020-10-18 12:37:55 +00:00
V("U", PIU(cgi_string()));
}
2019-08-27 07:18:47 +00:00
2022-12-08 18:38:06 +00:00
if(mproduct) V("PL", fts(vid.plevel_factor));
2019-11-13 19:48:38 +00:00
2019-11-17 12:35:18 +00:00
if(geometry == gFieldQuotient) { V("S3=", its(S3)); V("S7=", its(S7)); }
2019-11-23 19:39:38 +00:00
if(nil) V("NIL", its(S7));
2019-11-17 12:35:18 +00:00
if(bt::in()) V("BT", fts(vid.binary_width));
2021-03-21 11:50:51 +00:00
if(nil) V("NILW", fts(nilv::nilwidth));
if(GDIM == 2) {
V("CAMERA", fts(vid.camera));
}
if(WDIM == 2) {
V("WH", fts(vid.wall_height));
V("HW", fts(vid.human_wall_ratio));
V("RW", fts(vid.rock_wall_ratio));
V("DEPTH", fts(vid.depth));
V("ASH", ONOFF(vid.gp_autoscale_heights));
V("LT", fts(vid.lake_top));
V("LB", fts(vid.lake_bottom));
2021-10-15 20:38:24 +00:00
if(GDIM == 3 && vid.pseudohedral)
2021-10-15 20:36:43 +00:00
V("PS", fts(vid.depth_bonus));
}
V("3D", ONOFF(vid.always3));
2022-12-09 00:58:01 +00:00
if(embedded_plane) V("X:", its(geom3::ggclass()));
2022-12-16 00:26:11 +00:00
if(embedded_plane && meuclid) V("XS:", fts(geom3::euclid_embed_scale));
if(scale_used()) V("CS", fts(vid.creature_scale));
if(WDIM == 3) V("HTW", fts(vid.height_width));
V("LQ", its(vid.linequality));
2021-03-21 10:33:25 +00:00
callhooks(hooks_cgi_string, s);
return s;
}
2022-05-09 12:53:41 +00:00
#if MAXMDIM >= 4 && CAP_RAY
2022-02-17 20:00:10 +00:00
#define IFINTRA(x,y) x
#else
#define IFINTRA(x,y) y
#endif
EX void check_cgi() {
string s = cgi_string();
cgip = &cgis[s];
2019-06-18 14:16:09 +00:00
cgi.timestamp = ++ntimestamp;
2022-12-08 18:38:06 +00:00
if(mhybrid) hybrid::underlying_cgip->timestamp = ntimestamp;
2020-05-15 09:46:26 +00:00
if(fake::in()) fake::underlying_cgip->timestamp = ntimestamp;
2022-05-06 10:40:48 +00:00
#if CAP_ARCM
if(arcm::alt_cgip) arcm::alt_cgip->timestamp = ntimestamp;
2022-05-06 10:40:48 +00:00
#endif
2019-06-18 14:01:28 +00:00
int limit = 4;
for(auto& t: cgis) if(t.second.use_count) limit++;
if(isize(cgis) > limit) {
2019-06-18 14:01:28 +00:00
vector<pair<int, string>> timestamps;
for(auto& t: cgis) if(!t.second.use_count) timestamps.emplace_back(-t.second.timestamp, t.first);
2019-06-18 14:01:28 +00:00
sort(timestamps.begin(), timestamps.end());
2019-06-18 14:16:09 +00:00
while(isize(timestamps) > 4) {
DEBB(DF_GEOM, ("erasing geometry ", timestamps.back().second));
2019-06-18 14:01:28 +00:00
cgis.erase(timestamps.back().second);
timestamps.pop_back();
}
}
if(floor_textures && last_texture_step != vid.texture_step) {
println(hlog, "changed ", last_texture_step, " to ", vid.texture_step);
delete floor_textures;
floor_textures = NULL;
}
2020-07-03 12:48:36 +00:00
#if MAXMDIM >= 4 && CAP_GL
2019-08-15 13:05:43 +00:00
if(!floor_textures && GDIM == 3 && (cgi.state & 2))
2019-06-18 14:01:28 +00:00
make_floor_textures();
2019-07-12 21:17:00 +00:00
#endif
2019-06-18 14:01:28 +00:00
2015-08-08 13:57:52 +00:00
}
2019-06-18 14:01:28 +00:00
void clear_cgis() {
printf("clear_cgis\n");
for(auto& p: cgis) if(&p.second != &cgi) { cgis.erase(p.first); return; }
}
auto ah_clear_geo = addHook(hooks_clear_cache, 0, clear_cgis);
}