1 // compile with:
2 // ./mymake -O3 inner-maps
3 // used in: https://twitter.com/zenorogue/status/1212408644941295619
4
5 // run with:
6 // ./hyper -innner-map -noscr -canvas r -canvas i -wsh 0 -smart 4 -shott 0 -alpha 1
7
8 #include "rogueviz.h"
9
10 namespace hr {
11
12 namespace inner {
13
14 bool out = true;
15
16 EX struct renderbuffer *buf1, *buf2;
17
18 map<cell*, basic_textureinfo> lti;
19
20 ld mdist;
21
22 bool for_klein = false;
23
24 ld alpha = -.7;
25
26 cell *rendercenter;
27 shiftmatrix rendercenterV;
28
29 bool auto_move = false;
30
31 ld texture_alpha = 1;
32 ld inner_alpha = 1;
33 bool dense = false;
34
35 // should do 30x -- temporarily disabled
36
37 ld iterations = 1;
38
39 ld iterations_bank;
40
gentexture()41 void gentexture() {
42 if(out) iterations_bank += iterations;
43 while(iterations_bank >= 1) {
44 iterations_bank -= 1;
45 println(hlog, "# = ", isize(lti));
46 dynamicval<bool> b(out, false);
47 dynamicval<ld> va(pconf.alpha, texture_alpha);
48 if(!buf1) {
49 buf1 = new renderbuffer(rug::texturesize, rug::texturesize, true);
50 buf2 = new renderbuffer(rug::texturesize, rug::texturesize, true);
51 }
52 resetbuffer rb;
53 rug::calcparam_rug();
54 models::configure();
55
56 buf1->enable();
57 buf1->clear(0);
58 buf2->use_as_texture();
59
60 draw_boundary(0);
61 draw_boundary(1);
62
63 draw_model_elements();
64 drawthemap();
65 drawqueue();
66 calcparam();
67
68 rb.reset();
69
70 swap(buf1, buf2);
71
72 rendercenter = centerover;
73 rendercenterV = gmatrix[centerover];
74
75 }
76 }
77
frame()78 void frame() {
79 gentexture();
80 if(auto_move) {
81 color_t col = minf[moReptile].color;
82 drawMonsterType(moReptile, NULL, shiftless(spin(alpha)), col, mdist, col);
83 mapeditor::drawplayer = false;
84 }
85 }
86
87 bool done;
88
89 hpcshape edgeshape;
90
91 hpcshape circ;
92
93 ld crad = dense ? .5 : 1.82;
94 ld ceps = .02;
95
96 ld crad_p;
97
need_redo()98 void need_redo() {
99 done = false;
100 }
101
make_shape()102 void make_shape() {
103 if(done) return;
104 done = true;
105 cgi.bshape(circ, PPR::WALL);
106 circ.flags |= POLY_TRIANGLES;
107 int k = for_klein ? 1 : 2;
108 int maxz = for_klein ? 120 : 30;
109 for(int z=0; z<maxz; z++) {
110 ld ra = (z+0.)/maxz * crad, rb = (z+1.)/maxz * crad;
111 for(int i=0; i<360; i+=k) {
112 ld a = i * degree;
113 ld b = (i+k) * degree;
114
115 cgi.hpcpush(xspinpush0(a, ra));
116 cgi.hpcpush(xspinpush0(a, rb));
117 cgi.hpcpush(xspinpush0(b, rb));
118
119 if(z) {
120 cgi.hpcpush(xspinpush0(a, ra));
121 cgi.hpcpush(xspinpush0(b, ra));
122 cgi.hpcpush(xspinpush0(b, rb));
123 }
124 }
125 }
126 cgi.finishshape();
127 println(hlog, "circ vertices = ", circ.e - circ.s);
128
129 cgi.bshape(edgeshape, PPR::WALL);
130 for(int i=0; i<=360; i+=k) {
131 ld a = i * degree;
132 cgi.hpcpush(xspinpush0(a, crad - ceps));
133 }
134 for(int i=0; i<=360; i+=k) {
135 ld a = i * degree;
136 cgi.hpcpush(xspinpush0(a, crad + ceps));
137 }
138 cgi.finishshape();
139
140 cgi.extra_vertices();
141
142 hyperpoint ph = xspinpush0(0, crad);
143 ph /= (inner_alpha+ph[2]);
144 crad_p = ph[0];
145 }
146
147 set<cell*> gs;
148
render(cell * c,const shiftmatrix & V)149 bool render(cell *c, const shiftmatrix& V) {
150 if(!buf1 || !buf2 || !rendercenter) return false;
151 make_shape();
152 if(false && !gs.count(c)) {
153 gs.insert(c);
154 c->mondir = hrand(c->type);
155 dont_face_pc = true;
156 switch(hrand(100)) {
157 case 0:
158 c->monst = moOrangeDog;
159 break;
160 case 1:
161 c->item = itHell;
162 break;
163 case 2:
164 c->monst = moSalamander;
165 break;
166 case 3:
167 c->monst = moButterfly;
168 break;
169 case 4:
170 c->monst = moSparrowhawk;
171 break;
172 }
173 }
174 if(dense ? pseudohept(c) : cdist50(c) == 0) {
175 dynamicval<color_t> po(poly_outline, 0x000000FF);
176 auto& p = lti[c];
177 p.texture_id = buf2->renderedTexture;
178 p.tvertices.clear();
179
180 /*inner_alpha = anyshiftclick ? 1 : 0;
181 hyperpoint ph = xspinpush0(0, crad);
182 ph /= (inner_alpha+ph[2]);
183 crad_p = ph[0];
184 */
185
186 for(int i=circ.s; i<circ.e; i++) {
187 hyperpoint h = cgi.hpc[i];
188 // hyperboloid to Poincare
189 h /= (inner_alpha + h[2]);
190 // scale up
191 h /= crad_p;
192 h *= .99999999;
193 // Poincare to hyperboloid
194
195 h = perspective_to_space(h, inner_alpha, cgclass);
196
197 /*
198 ld hr = sqhypot_d(2, h);
199 ld hz = (1 + hr) / (1 - hr);
200 h[0] *= (hz+1);
201 h[1] *= (hz+1);
202 h[2] = hz;
203 */
204
205 // move according to V
206 h = rendercenterV.T * inverse(ggmatrix(rendercenter).T) * V.T * h;
207 // texture coordinates
208 // hyperpoint scr;
209 // dynamicval<ld> b(pconf.alpha, anyshiftclick ? pconf.alpha : texture_alpha);
210 // applymodel(h, scr);
211 hyperpoint scr = h / (texture_alpha + h[2]);
212 p.tvertices.push_back(glhr::makevertex(.5 + .5 * scr[0], .5 - .5 * scr[1], 0));
213 }
214 queuepoly(V, circ, 0xFFFFFFFF).tinf = &p;
215 queuepoly(V, edgeshape, darkena(c->landparam, 0, 0xFF));
216 }
217 return false;
218 }
219
shift()220 void shift() {
221 if(auto_move) {
222 centerover = currentmap->gamestart();
223 mdist = (ticks % 10000) * 6 / 10000.;
224 View = spin(alpha) * xpush(-mdist) * spin(-alpha);
225 anims::moved();
226 centerover->monst = moNone;
227 forCellEx(c1, centerover) c1->monst = moNone;
228 }
229 }
230
show()231 void show() {
232 cmode = sm::SIDE | sm::MAYDARK;
233 gamescreen(0);
234 dialog::init(XLAT("inner maps"), 0xFFFFFFFF, 150, 0);
235 add_edit(texture_alpha);
236 add_edit(inner_alpha);
237 add_edit(dense);
238 add_edit(crad);
239 add_edit(ceps);
240 add_edit(auto_move);
241 add_edit(iterations);
242 dialog::addBack();
243 dialog::display();
244 }
245
enable()246 void enable() {
247 using rogueviz::rv_hook;
248 rv_hook(hooks_frame, 100, frame);
249 rv_hook(hooks_drawcell, 100, render);
250 rv_hook(anims::hooks_anim, 100, shift);
251 rv_hook(hooks_o_key, 80, [] (o_funcs& v) { v.push_back(named_dialog("inner maps", show)); });
252 }
253
254 auto hook = arg::add3("-inner-map", enable)
__anonae1ccc810202null255 + addHook(hooks_configfile, 100, [] {
256 param_f(texture_alpha, "inner_talpha")
257 ->editable(0, 5, .1, "texture projection distance", "", 't');
258 param_f(inner_alpha, "inner_ialpha")
259 ->editable(0, 5, .1, "inner projection distance", "", 'i')
260 ->set_reaction(need_redo);
261 param_b(dense, "inner_dense")
262 ->editable("densely packed maps", 'd');
263 param_f(crad, "inner_crad")
264 ->editable(0, 10, .1, "radii of the inner maps", "", 'r')
265 ->set_reaction(need_redo);
266 param_f(ceps, "inner_ceps")
267 ->editable(0, 0.1, .001, "map frame size", "", 'f')
268 ->set_reaction(need_redo);
269 param_b(auto_move, "auto_move")
270 ->editable("animate", 'a');
271 param_f(iterations, "inner_iterations")
272 ->editable(0, 30, 0.2, "iterations per frame",
273 "How many times per frame should we re-render the map",
274 'i')
275 ->set_reaction(need_redo);
276 })
__anonae1ccc810302(string s, vector<tour::slide>& v) 277 + addHook_rvslides(52, [] (string s, vector<tour::slide>& v) {
278 if(s != "projections") return;
279 using namespace tour;
280
281 v.push_back(slide{
282 "projections/hyperbolic to hyperbolic", 10, LEGAL::NONE | QUICKGEO,
283
284 "We can also project a hyperbolic plane to a hyperbolic plane of different curvature.\n\n"
285 "Creatures living in the hyperbolic world may use the native Poincaré model to get conformal, circular, and totally useless maps of their whole world.\n\n"
286
287 "Press 'o' to change the settings."
288 ,
289 [] (presmode mode) {
290 slide_url(mode, 't', "Twitter link (with description)", "https://twitter.com/zenorogue/status/1212408644941295619");
291 setCanvas(mode, 'r');
292
293 if(mode == pmStart) {
294 if(!shmup::on) restart_game(rg::shmup);
295 slide_backup(mapeditor::drawplayer, true);
296 enable();
297 start_game();
298 }
299 }});
300 });
301
302 }
303 }
304