mirror of
https://github.com/zenorogue/hyperrogue.git
synced 2024-12-30 12:00:35 +00:00
262 lines
7.9 KiB
C++
262 lines
7.9 KiB
C++
#include "rogueviz.h"
|
|
|
|
// Copyright (C) 2011-2019 Zeno Rogue, see 'hyper.cpp' for details
|
|
|
|
/*
|
|
|
|
3D engines often represent rotations as unit quaternions. These form a three-dimensional elliptic space. What if we could fly through it?
|
|
|
|
Lower left corner shows the rotation matching the current camera position. Beams in the rotation space correspond to bumps on the sphere.
|
|
|
|
Run like:
|
|
[hyper] -geo 2 -rotspace -rot_uscale 0.25 -qtm (space of rotations of the sphere)
|
|
[hyper] -rotspace -rot_uscale 0.25 -qtm (space of rotations of the hyperbolic plane, slow)
|
|
[hyper] -geo Nil -qtm (Nil, which is not a space of rotations, but similar; rendered in a similar way)
|
|
|
|
*/
|
|
|
|
namespace hr {
|
|
|
|
namespace hybrid { extern hrmap *pmap; }
|
|
|
|
namespace qtm {
|
|
|
|
int qmode;
|
|
|
|
color_t rainbow_color_at(hyperpoint h) {
|
|
ld sat = 1 - 1 / h[2];
|
|
ld hue = atan2(h[0], h[1]) / TAU;
|
|
return rainbow_color(sat, hue);
|
|
}
|
|
|
|
void set_cell(cell *c) {
|
|
if(mhybrid) {
|
|
cell *c1 = hybrid::get_where(c).first;
|
|
if(c1->land != laHive) hybrid::in_underlying_geometry([&] { set_cell(c1); });
|
|
c->land = c1->land;
|
|
c->wall = c1->wall;
|
|
c->landparam = c1->landparam;
|
|
c->item = itNone;
|
|
c->monst = moNone;
|
|
if(qmode == 1) {
|
|
if(hybrid::get_where(c).second == 0)
|
|
c->landparam = 0xFFFFFF;
|
|
}
|
|
if(qmode == 2) {
|
|
if(hybrid::get_where(c).second != 0)
|
|
c->wall = waNone;
|
|
}
|
|
}
|
|
else {
|
|
if(c->land == laHive) return;
|
|
color_t col;
|
|
if(hyperbolic) {
|
|
hyperpoint h = calc_relative_matrix(c, currentmap->gamestart(), C0) * C0;
|
|
col = rainbow_color_at(h);
|
|
}
|
|
else if(nil) {
|
|
part(col, 0) = 128 + c->master->zebraval * 50;
|
|
part(col, 1) = 128 + c->master->emeraldval * 50;
|
|
part(col, 2) = 255;
|
|
}
|
|
else {
|
|
hyperpoint h = calc_relative_matrix(c, currentmap->gamestart(), C0) * C0;
|
|
part(col, 0) = (h[0] * 127.5 + 127.5);
|
|
part(col, 1) = (h[1] * 127.5 + 127.5);
|
|
part(col, 2) = (h[2] * 127.5 + 127.5);
|
|
}
|
|
c->landparam = col;
|
|
c->land = laHive;
|
|
c->wall = (nil ? (c->master->zebraval & c->master->emeraldval & 1) : pseudohept(c)) ? waWaxWall : waNone;
|
|
c->item = itNone;
|
|
c->monst = moNone;
|
|
}
|
|
}
|
|
|
|
bool may_set_cell(cell *c, int d, cell *from) {
|
|
set_cell(c);
|
|
return false;
|
|
}
|
|
|
|
void enable() {
|
|
rogueviz::rv_hook(hooks_cellgen, 100, may_set_cell);
|
|
}
|
|
|
|
int args() {
|
|
using namespace arg;
|
|
|
|
if(0) ;
|
|
else if(argis("-qtm-stripe")) {
|
|
qmode = 1;
|
|
}
|
|
else if(argis("-qtm-no-stripe")) {
|
|
qmode = 0;
|
|
}
|
|
else if(argis("-qtm-stripe-only")) {
|
|
qmode = 2;
|
|
}
|
|
else if(argis("-qtm")) {
|
|
PHASEFROM(2);
|
|
enable();
|
|
}
|
|
|
|
else if(argis("-spheredemo")) {
|
|
start_game();
|
|
auto c = currentmap->allcells();
|
|
for(cell* cx: c) cx->wall = waNone, cx->item = itNone, cx->land = laCanvas, cx->landparam = 0;
|
|
c[1]->wall = waPalace;
|
|
c[1]->land = laPalace;
|
|
int N = isize(c);
|
|
|
|
int i = 1+N/4;
|
|
int j = 1+2*N/4 + 4;
|
|
int k = 1+3*N/4;
|
|
j %= N;
|
|
|
|
c[i]->wall = waIcewall;
|
|
c[i]->land = laIce;
|
|
|
|
c[j]->wall = waBigTree;
|
|
c[j]->land = laDryForest;
|
|
|
|
c[k]->wall = waWaxWall;
|
|
c[k]->landparam = 0xFF0000;
|
|
}
|
|
|
|
else if(argis("-two-way")) {
|
|
start_game();
|
|
cwt.at->move(0)->wall = waWaxWall;
|
|
cwt.at->move(0)->landparam = 0xFF0000;
|
|
|
|
cwt.at->move(6)->wall = waWaxWall;
|
|
cwt.at->move(6)->landparam = 0xFFFF40;
|
|
}
|
|
|
|
else if(argis("-one-center")) {
|
|
start_game();
|
|
cwt.at->wall = waWaxWall;
|
|
cwt.at->landparam = 0xFFD500;
|
|
}
|
|
|
|
else if(argis("-one-line")) {
|
|
start_game();
|
|
cell *c = cwt.at;
|
|
int i = 0;
|
|
do {
|
|
c = c->cmove(0);
|
|
i++;
|
|
}
|
|
while(c != cwt.at);
|
|
for(int j=0; j<i; j++) {
|
|
c->wall = waWaxWall;
|
|
c->landparam = rainbow_color(1, j * 1. / i);
|
|
c = c->cmove(0);
|
|
}
|
|
}
|
|
else if(argis("-two-line")) {
|
|
start_game();
|
|
cell *c = cwt.at;
|
|
int i = 0;
|
|
do {
|
|
c = c->cmove(0);
|
|
i++;
|
|
}
|
|
while(c != cwt.at);
|
|
for(int j=0; j<i; j++) {
|
|
c->wall = waWaxWall;
|
|
c->landparam = rainbow_color(1, j * 1. / i);
|
|
c = c->cmove(0);
|
|
}
|
|
vector<cell*> a;
|
|
for(cell *x: currentmap->allcells()) {
|
|
bool good = true;
|
|
forCellCM(y, x) if(y->wall == waWaxWall) good = false;
|
|
if(good) a.push_back(x);
|
|
}
|
|
for(cell *x: a) {
|
|
x->wall = waWaxWall;
|
|
x->landparam = 0xFFFFFF;
|
|
}
|
|
vector<cell*> b;
|
|
for(cell *x: a) {
|
|
bool good = false;
|
|
forCellCM(y, x) if(y->wall != waWaxWall) good = true;
|
|
if(good) b.push_back(x);
|
|
}
|
|
for(cell *x: b) x->wall = waNone;
|
|
}
|
|
|
|
else return 1;
|
|
return 0;
|
|
}
|
|
|
|
auto hooks =
|
|
addHook(hooks_args, 100, args)
|
|
+ addHook_rvslides(180, [] (string s, vector<tour::slide>& v) {
|
|
if(s != "mixed") return;
|
|
using namespace tour;
|
|
for(int m: {1,2}) {
|
|
string ex = m == 1 ? " (A)" : " (B)";
|
|
string bonus = m == 2 ? "\n\nTo make stretching more interesting, only the 'stripes' are filled now." : "";
|
|
v.push_back(slide{
|
|
"rotations/isometries of 2-sphere"+ex, 10, LEGAL::NONE | QUICKGEO,
|
|
"3D engines often represent rotations as unit quaternions. These form a 3D elliptic space. What if we could fly through this space of rotations?\n\n"
|
|
"Rotation matching the current camera position shown in the corner. Beams in the rotation space correspond to bumps on the sphere.\n\n"
|
|
"You can also obtain a different geometry (Berger sphere) by stretching along the fibers. Press 5 to stretch."+bonus
|
|
,
|
|
[m] (presmode mode) {
|
|
setCanvas(mode, '0');
|
|
slide_url(mode, 't', "Twitter link", "https://twitter.com/ZenoRogue/status/1166723332840001536");
|
|
slide_url(mode, 's', "stretched Twitter link", "https://twitter.com/ZenoRogue/status/1258035231996682244");
|
|
if(mode == pmStart) {
|
|
set_geometry(gSphere);
|
|
set_variation(eVariation::bitruncated);
|
|
set_geometry(gRotSpace);
|
|
slide_backup(rots::underlying_scale, .25);
|
|
slide_backup(qmode, m);
|
|
#if CAP_RAY
|
|
slide_backup(ray::max_cells, 32768);
|
|
slide_backup(ray::fixed_map, true);
|
|
#endif
|
|
slide_backup(camera_speed, .1);
|
|
enable();
|
|
start_game();
|
|
}
|
|
rogueviz::pres::non_game_slide_scroll(mode);
|
|
if(mode == pmKey)
|
|
edit_stretch();
|
|
}});
|
|
v.push_back(slide{
|
|
"rotations/isometries of the hyperbolic plane"+ex, 10, LEGAL::NONE | QUICKGEO,
|
|
"The hyperbolic analog of the previous slide. The space is PSL(2,R) now, which has a non-isotropic geometry. It can be represented using split quaternions.\n\n"
|
|
"Again, press 5 to stretch."+bonus
|
|
,
|
|
[m] (presmode mode) {
|
|
setCanvas(mode, '0');
|
|
slide_url(mode, 's', "stretched Twitter link", "https://twitter.com/ZenoRogue/status/1259143275115687936");
|
|
if(mode == pmStart) {
|
|
set_geometry(gKleinQuartic);
|
|
set_variation(eVariation::bitruncated);
|
|
set_geometry(gRotSpace);
|
|
slide_backup(rots::underlying_scale, .25);
|
|
slide_backup(qmode, m);
|
|
#if CAP_RAY
|
|
slide_backup(ray::max_cells, 32768);
|
|
slide_backup(ray::fixed_map, true);
|
|
slide_backup(ray::want_use, 2);
|
|
#endif
|
|
slide_backup(camera_speed, .1);
|
|
enable();
|
|
start_game();
|
|
}
|
|
rogueviz::pres::non_game_slide_scroll(mode);
|
|
if(mode == pmKey)
|
|
edit_stretch();
|
|
}});
|
|
}
|
|
});
|
|
|
|
}
|
|
|
|
}
|