// Hyperbolic Rogue - Floor Shapes // Copyright (C) 2011-2019 Zeno Rogue, see 'hyper.cpp' for details /** \file floorshapes.cpp * \brief Adjusting the floor shapes to various geometries. */ #include "hyper.h" namespace hr { #if CAP_SHAPES #if HDR struct qfloorinfo { transmatrix spin; const struct hpcshape *shape; floorshape *fshape; struct textureinfo *tinf; int usershape; }; extern qfloorinfo qfi; #endif EX vector floor_texture_vertices; EX vector floor_texture_map; EX struct renderbuffer *floor_textures; EX basic_textureinfo* get_floor_texture_vertices(int index) { if(noGUI || !vid.usingGL) return nullptr; return &floor_texture_vertices[index]; } /* 0: generate no floorshapes; 1: generate only plain floorshapes; 2: generate all */ EX int floorshapes_level = 2; EX ld global_boundary_ratio = 1; void geometry_information::init_floorshapes() { if(floorshapes_level == 0) return; all_escher_floorshapes.clear(); all_plain_floorshapes = { &shFloor, &shMFloor, &shMFloor2, &shMFloor3, &shMFloor4, &shFullFloor, &shBigTriangle, &shTriheptaFloor, &shBigHepta }; for(auto s: all_plain_floorshapes) s->is_plain = true; auto init_escher = [this] (escher_floorshape& sh, int s0, int s1, int noft, int s2) { if(floorshapes_level == 1) return; sh.shapeid0 = s0; sh.shapeid1 = s1; sh.noftype = noft; sh.shapeid2 = s2; sh.scale = 1; sh.is_plain = false; all_escher_floorshapes.push_back(&sh); }; init_escher(shStarFloor, 1, 2, 0, 0); init_escher(shCloudFloor, 3, 4, 0, 0); init_escher(shCrossFloor, 5, 6, 2, 54); init_escher(shChargedFloor, 7, 385, 1, 10); init_escher(shSStarFloor, 11, 12, 0, 0); init_escher(shOverFloor, 13, 15, 1, 14); init_escher(shTriFloor, 17, 18, 0, 385); init_escher(shFeatherFloor, 19, 21, 1, 20); init_escher(shBarrowFloor, 23, 24, 1, 25); init_escher(shNewFloor, 26, 27, 2, 54); init_escher(shTrollFloor, 28, 29, 0, 0); init_escher(shButterflyFloor, 325, 326, 1, 178); init_escher(shLavaFloor, 359, 360, 1, 178); init_escher(shLavaSeabed, 386, 387, 1, 178); init_escher(shSeabed, 334, 335, 0, 0); init_escher(shCloudSeabed, 336, 337, 0, 0); init_escher(shCaveSeabed, 338, 339, 2, 54); init_escher(shPalaceFloor, 45, 46, 0, 385); init_escher(shDemonFloor, 51, 50, 1, 178); init_escher(shCaveFloor, 52, 53, 2, 54); init_escher(shDesertFloor, 55, 56, 0, 4); init_escher(shPowerFloor, 57, 58, 0, 12); /* dragon */ init_escher(shRoseFloor, 174, 175, 1, 173); init_escher(shSwitchFloor, 377, 378, 1, 379); init_escher(shTurtleFloor, 176, 177, 1, 178); for(int i: {0,1,2}) init_escher(shRedRockFloor[i], 55, 56, 0, 0); init_escher(shDragonFloor, 181, 182, 2, 183); /* dragon */ int ids = 0; for(auto sh: all_plain_floorshapes) sh->id = ids++; for(auto sh: all_escher_floorshapes) sh->id = ids++; } /** matrixitem::second[2][2] == APEIROGONAL_INVALID is used to denote a matrix that uses fake apeirogon vertices and thus should not be used */ const ld APEIROGONAL_INVALID = -2; typedef pair> matrixitem; struct mesher { eGeometry g; int sym; ld bspi; hyperpoint lcorner, rcorner, mfar[2], vfar[4]; }; mesher msh(eGeometry g, int sym, ld main, ld v0, ld v1, ld bspi, ld scale) { main *= scale; v0 *= scale; v1 *= scale; mesher m; m.sym = sym; m.bspi = bspi; dynamicval dg(geometry, g); hyperpoint rot = xpush(v0) * xspinpush0(M_PI - M_PI/sym, main); hyperpoint bnlfar = xpush(v0) * spin180() * rspintox(rot) * rspintox(rot) * rspintox(rot) * xpush0(hdist0(rot)); hyperpoint bnrfar = xpush(v0) * spin180() * spintox(rot) * spintox(rot) * spintox(rot) * xpush0(hdist0(rot)); m.lcorner = xspinpush0 (bspi - M_PI/sym, main); m.rcorner = xspinpush0 (bspi + M_PI/sym, main); m.mfar[0] = xspinpush0 (bspi, v0); m.mfar[1] = xspinpush0 (bspi, v1); m.vfar[0] = spin(bspi) * bnlfar; m.vfar[2] = spin(bspi) * bnrfar; m.vfar[1] = spin(-TAU/sym) * m.vfar[2]; m.vfar[3] = spin(+TAU/sym) * m.vfar[0]; return m; } struct matrixlist { mesher o, n; vector v; }; matrixitem genitem(const transmatrix& m1, const transmatrix& m2, int nsym) { matrixitem mi; mi.first = m1; mi.second.resize(nsym); for(int i=0; i= OINF || (cgflags & qIDEAL); } EX hyperpoint may_kleinize(hyperpoint h) { if(do_kleinize()) return kleinize(h); else return h; } void addmatrix(matrixlist& matrices, hyperpoint o0, hyperpoint o1, hyperpoint o2, hyperpoint n0, hyperpoint n1, hyperpoint n2, int d, int osym, int nsym) { if(do_kleinize()) o0 = kleinize(o0), o1 = kleinize(o1), o2 = kleinize(o2), n0 = kleinize(n0), n1 = kleinize(n1), n2 = kleinize(n2); matrices.v.push_back(genitem(inverse(spin(TAU*d/osym)*build_matrix(o0, o1, o2,C02)), spin(TAU*d/nsym)*build_matrix(n0, n1, n2,C02), nsym)); } matrixlist hex_matrices, hept_matrices; void generate_matrices(matrixlist& matrices, const mesher& o, const mesher& n) { matrices.v.clear(); matrices.o = o; matrices.n = n; for(int d=0; d lst; for(int i=0; i dg(geometry, gNormal); lst.push_back(hpxy(polydata[whereis+2*i], polydata[whereis+2*i+1])); } if(sym == 2) for(int i=qty-1; i>=0; i--) { dynamicval dg(geometry, gNormal); lst.push_back(hpxy(polydata[whereis+2*i], -polydata[whereis+2*i+1])); } hyperpoint lstmid = hpxyz(0,0,0); for(auto pp: lst) lstmid += pp; transmatrix T = spin(-m.o.bspi); while((spin(TAU / rots) * T* lstmid)[0] < (T*lstmid)[0]) T = spin(TAU / rots) * T; while((spin(-TAU / rots) * T* lstmid)[0] < (T*lstmid)[0]) T = spin(-TAU / rots) * T; T = spin(m.o.bspi) * T; for(auto &pp: lst) pp = T * pp; if(osym % rots && rots % osym && (debugflags & DF_GEOM)) printf("warning: rotation oddity (shapeid %d, osym=%d rots=%d)\n", shapeid, osym, rots); if(rots > osym && rots % osym == 0) { int rep = rots / osym; int s = lst.size(); for(int i=0; i tail, head; for(int r=0; r -1e-5 && z[1] > -1e-5 && z[2] > -1e-5) { if(m.second[r][2][2] == APEIROGONAL_INVALID) invalid++; nh = m.second[r] * z, mapped++; } } if(mapped == 0 && (debugflags & DF_GEOM)) printf("warning: not mapped (shapeid %d)\n", shapeid); if(invalid) { apeirogonal = true; for(auto h: head) tail.push_back(h); head.clear(); } if(!invalid) head.push_back(nh); } } for(auto& h: head) hpcpush(h); for(auto& h: tail) hpcpush(h); if(!apeirogonal) hpcpush(starting_point); } template void sizeto(T& t, int n) { if(isize(t) <= n) t.resize(n+1); } template void sizeto(T& t, int n, const U& val) { if(isize(t) <= n) t.resize(n+1, val); } void geometry_information::bshape_regular(floorshape &fsh, int id, int sides, ld shift, ld size, cell *c) { sizeto(fsh.b, id); sizeto(fsh.shadow, id); #if CAP_BT if(bt::in()) { const int STEP = vid.texture_step; for(int t=0; t<2; t++) { if(t == 0) bshape(fsh.b[id], fsh.prio); if(t == 1) bshape(fsh.shadow[id], fsh.prio); int STEP1 = STEP; if((embedded_plane || geom3::flipped) && t == 0) STEP1 = 1; for(int i=0; is]); } for(int k=0; ktype) fsh.gpside[k].resize(c->type); for(int i=0; itype; i++) { sizeto(fsh.gpside[k][i], id); bshape(fsh.gpside[k][i][id], PPR::LAKEWALL); hyperpoint h0 = bt::get_corner_horo_coordinates(c, i) * size; hyperpoint h1 = bt::get_corner_horo_coordinates(c, i+1) * size; hyperpoint hd = (h1 - h0) / STEP; for(int j=0; j<=STEP; j++) hpcpush(iddspin_side(c, i) * bt::get_horopoint(h0 + hd * j)); chasmifyPoly(dlow_table[k], dhi_table[k], k); } } return; } #endif bshape(fsh.b[id], fsh.prio); for(int t=0; t<=sides; t++) hpcpush(xspinpush0(t * TAU / sides + shift * S_step, size)); bshape(fsh.shadow[id], fsh.prio); for(int t=0; t<=sides; t++) hpcpush(xspinpush0(t * TAU / sides + shift * S_step, size * SHADMUL)); for(int k=0; kis_euc_in_noniso()) { fsh.gpside[k].resize(c->type); for(int i=0; itype; i++) { sizeto(fsh.gpside[k][i], id); bshape(fsh.gpside[k][i][id], PPR::LAKEWALL); hpcpush(xspinpush0(M_PI - i * TAU / sides + shift * S_step, size)); hpcpush(xspinpush0(M_PI - (i + 1) * TAU / sides + shift * S_step, size)); chasmifyPoly(dlow_table[k], dhi_table[k], k); } } } } #if CAP_IRR namespace irr { void generate_floorshapes(); } #endif void geometry_information::finish_apeirogon(hyperpoint center) { last->flags |= POLY_APEIROGONAL; last->she = isize(hpc); if(arb::apeirogon_simplified_display) { hyperpoint p = towards_inf(last_point, center, ideal_limit); hyperpoint q = towards_inf(starting_point, center, ideal_limit); hpc.push_back(p); hpc_connect_ideal(p, q); hpc.push_back(q); } else { hpcpush(center); hpcpush(starting_point); } } hyperpoint get_circumscribed_corner(cell *c, int t, hyperpoint h) { hyperpoint h0 = currentmap->adjmod(c, t) * h; hyperpoint h1 = currentmap->adjmod(c, t-1) * h; transmatrix T; array hs = {h, h0, h1}; set_column(T, 3, C03); hyperpoint res = C03; for(int i=0; i<3; i++) { hyperpoint ahs = hs[i]; if(hyperbolic) ahs[3] *= -1; set_column(T, i, ahs); res[i] = dot_d(4, hs[i], ahs); } T = transpose(T); return inverse(T) * res; } // !siid equals pseudohept(c) void geometry_information::generate_floorshapes_for(int id, cell *c, int siid, int sidir) { DEBBI(DF_POLY, ("generate_floorshapes_for ", id)); for(auto pfsh: all_plain_floorshapes) { auto& fsh = *pfsh; if(STDVAR && (standard_tiling() || bt::in())) { ld hexside = fsh.rad0, heptside = fsh.rad1; for(int k=0; k= OINF && !(S7 & 1)) td = S42 * 1. / S7; int b = 0; if(S3 == 4 && BITRUNCATED) b += S14; if(id) bshape_regular(fsh, id, S7, td, heptside, c); else if(PURE) { if(&fsh == &shTriheptaFloor) bshape_regular(fsh, 0, S7/2, 0, hexside, c); else if(&fsh == &shBigTriangle) bshape_regular(fsh, 0, S7/2, S12, hexside, c); else bshape_regular(fsh, 0, S7, td, heptside, c); } else if(&fsh == &shBigTriangle) bshape_regular(fsh, 0, S3, b+S14, hexside, c); else if(&fsh == &shTriheptaFloor) bshape_regular(fsh, 0, S3, b, hexside, c); else bshape_regular(fsh, 0, S6, S7, hexside, c); continue; } // special ld sca = 3 * shFullFloor.rad0 / fsh.rad0; vector cornerlist; int cor = c->type; bool apeirogonal = false; if(&fsh == &shTriheptaFloor) { if(!siid) { for(int i=0; ic.fix(ri); ld val = 3 + 0.1 * global_boundary_ratio; cornerlist.push_back(mid(get_corner_position(c, ri, val), get_corner_position(c, c->c.fix(ri+1), val))); } } } else if(&fsh == &shBigTriangle) { ld val = 1 - 0.06 * global_boundary_ratio; if(!siid) { for(int i=0; ic.fix(ri); hyperpoint nc = nearcorner(c, ri); cornerlist.push_back(mid_at(hpxy(0,0), nc, val)); } } } else if(&fsh == &shBigHepta) { ld val = 1 - 0.06 * global_boundary_ratio; if(!siid) { for(int i=0; i actual; for(int j=0; j 1e-6 && dist < min_dist) min_dist = dist; } auto &ac = arb::current_or_slided(); ld dist = min_dist * (1 - 3 / sca) * (arb::in() ? ac.boundary_ratio : 1); ld area = 0; for(int j=0; jmaster); sh = &ac.shapes[id]; apeirogonal = sh->apeirogonal; } for(int j=0; jvertices.back()); continue; } } auto T = gpushxto0(current); last = T * last; next = T * next; hyperpoint a = rspintox(last) * ypush0(dist); hyperpoint b = rspintox(last) * xpush(hdist0(last)) * ypush0(dist); hyperpoint c = rspintox(next) * ypush0(-dist); hyperpoint d = rspintox(next) * xpush(hdist0(next)) * ypush0(-dist); hyperpoint h = linecross(a, b, c, d); cornerlist.push_back(rgpushxto0(current) * h); } } else { for(int j=0; jtype; bool apeirogonal = arb::is_apeirogonal(c); m.n.sym = cor; int v = sidir+siid; for(auto& mvi: m.v) mvi.second.resize(cor); for(int ii=0; ii<2; ii++) { int i = 0; for(int d=0; d= cor-2) { for(int j: {0,1,2,3}) m.v[i+j].second[cid][2][2] = APEIROGONAL_INVALID; continue; } hyperpoint nlcorner = get_corner_position(c, dcidv1, 3 / fsh.scale * (ii ? 1/SHADMUL : 1)); hyperpoint nrcorner = get_corner_position(c, dcidv2, 3 / fsh.scale * (ii ? 1/SHADMUL : 1)); hyperpoint nfar = nearcorner(c, dcidv1); hyperpoint nlfar = farcorner(c, dcidv1, 0); hyperpoint nrfar = farcorner(c, dcidv1, 1); m.v[i].second[cid] = build_matrix(center, nlcorner, nrcorner,C02); m.v[i+1].second[cid] = build_matrix(nfar, nlcorner, nrcorner,C02); m.v[i+2].second[cid] = build_matrix(nfar, nlcorner, nlfar,C02); m.v[i+3].second[cid] = build_matrix(nfar, nrcorner, nrfar,C02); } i += 4; } if(i != isize(m.v)) printf("warning: i=%d sm=%d\n", i, isize(m.v)); bshape2((ii?fsh.shadow:fsh.b)[id], fsh.prio, (fsh.shapeid2 && geosupport_football() < 2) ? fsh.shapeid2 : siid?fsh.shapeid0:fsh.shapeid1, m); if(apeirogonal && !first) { int id = arb::id_of(c->master); auto &ac = arb::current_or_slided(); auto& sh = ac.shapes[id]; hpcpush(arb::get_adj(arb::current_or_slided(), id, cor-2, id, cor-1, false) * starting_point); finish_apeirogon(sh.vertices.back()); } } } } #if MAXMDIM >= 4 if(embedded_plane) { finishshape(); for(auto pfsh: all_plain_floorshapes) { auto& fsh = *pfsh; for(int i=fsh.shadow[id].s; iflags |= POLY_TRIANGLES; last->tinf = get_floor_texture_vertices(fsh.id); last->texture_offset = 0; if(1) { int s = fsh.b[id].s; int e = fsh.b[id].e-1; if(vid.pseudohedral == phInscribed) { hyperpoint ctr = Hypc; for(int t=0; torthogonal_move(hpc[s+t], dfloor_table[k])); ctr = normalize(ctr); for(int t=0; torthogonal_move(hpc[s+t], dfloor_table[k])) - ctr; hyperpoint v2 = kleinize(cgi.emb->orthogonal_move(hpc[s+t+1], dfloor_table[k])) - ctr; texture_order([&] (ld x, ld y) { hpcpush(normalize(ctr + v1 * x + v2 * y)); }); } } if(vid.pseudohedral == phCircumscribed) { vector hs(c->type); hyperpoint z = Hypc; z[2] = dfloor_table[k]; hyperpoint ctr = cgi.emb->logical_to_actual(z); for(int t=0; ttype; t++) hs[t] = get_circumscribed_corner(c, t, ctr); // for(int t=0; ttype; t++) hs[t] = xspinpush0(t * TAU / c->type, 0.2); // kleinize(get_circumscribed_corner(c, t, ctr)); for(int t=0; ttype; t++) { hyperpoint v1 = hs[t] - ctr; hyperpoint v2 = atmod(hs, t+1) - ctr; texture_order([&] (ld x, ld y) { hpcpush(normalize(ctr + v1 * x + v2 * y)); }); } /* also affect the plain floor */ bshape(fsh.b[id], PPR::FLOOR); for(hyperpoint& h: hs) hpcpush(h); hpcpush(hs[0]); } if(vid.pseudohedral == phOFF) for(int t=0; tactual_to_logical(hpc[s+t]); hyperpoint v2 = cgi.emb->actual_to_logical(hpc[s+t+1]); texture_order([&] (ld x, ld y) { hyperpoint a = v1 * x + v2 * y; a[2] = dfloor_table[k]; auto c = cgi.emb->logical_to_actual(a); cgi.hpcpush(c); }); } } finishshape(); ensure_vertex_number(fsh.levels[k][id]); } for(int co=0; co<2; co++) { sizeto(fsh.cone[co], id); bshape(fsh.cone[co][id], fsh.prio); last->flags |= POLY_TRIANGLES; last->tinf = get_floor_texture_vertices(fsh.id); last->texture_offset = 0; ld h = (FLOOR - WALL) / (co+1); ld top = co ? (FLOOR + WALL) / 2 : WALL; if(1) { int s = fsh.b[id].s; int e = fsh.b[id].e-1; for(int t=0; tactual_to_logical(hpc[s+t]); hyperpoint v2 = cgi.emb->actual_to_logical(hpc[s+t+1]); texture_order([&] (ld x, ld y) { hyperpoint a = v1 * x + v2 * y; a[2] = top + h * (x+y); hpcpush(cgi.emb->logical_to_actual(a)); }); } } finishshape(); ensure_vertex_number(fsh.cone[co][id]); } for(int l=0; ltype) fsh.gpside[l].resize(c->type); for(auto& gs: fsh.gpside[l]) { for(auto& li: gs) bind_floor_texture(li, fsh.id); } } } for(auto pfsh: all_escher_floorshapes) { auto& fsh = *pfsh; for(int l=0; ltype) fsh.gpside[l].resize(c->type); for(int e=0; etype; e++) { fsh.gpside[l][e] = shFullFloor.gpside[l][e]; for(auto& li: fsh.gpside[l][e]) bind_floor_texture(li, fsh.id); } fsh.cone[0] = shFullFloor.cone[0]; fsh.cone[1] = shFullFloor.cone[1]; for(int c=0; c<2; c++) for(auto& li: fsh.cone[c]) bind_floor_texture(li, fsh.id); } } finishshape(); } #endif } EX hookset hooks_floorshapes; void geometry_information::generate_floorshapes() { DEBBI(DF_POLY, ("generate_floorshapes")); heptagon modelh; cell model; model.master = &modelh; modelh.c7 = &model; model.type = modelh.type = FULL_EDGE; auto mmerge1 = [&] (int i, int j) { model.c.setspin(i, j, false); modelh.c.setspin(i, j, false); }; auto mmerge = [&] (int i, int j) { mmerge1(i, j); mmerge1(j, i); }; for(int i=0; i ncor(approx_nearcorner, true); for(int i=0; i<2; i++) { modelh.c7 = i == 1 ? &model : nullptr; generate_floorshapes_for(i, &model, 0, 0); } } #if CAP_BT else if(kite::in()) { dynamicval ncor(approx_nearcorner, true); for(int i=0; i<2; i++) { modelh.s = hstate(i); /* kite/dart shape */ kite::no_adj = true; generate_floorshapes_for(i, &model, 0, 0); kite::no_adj = false; } } #endif #if CAP_ARCM else if(arcm::in()) { /* will be generated on the fly */ } #endif else if(arb::in()) { auto& c = arb::current; int n = isize(c.shapes); vector models(n); vector modelh(n); for(int i=0; i c(currentmap, bt::new_alt_map(nullptr)); model.type = S6; generate_floorshapes_for(0, &model, 0, 0); model.type = S7; generate_floorshapes_for(1, &model, 1, 0); delete currentmap; } #endif else { static hrmap_standard stdmap; dynamicval c(currentmap, &stdmap); // cell model; model.type = S6; generate_floorshapes_for(0, &model, 0, 0); model.type = S7; generate_floorshapes_for(1, &model, 0, 0); } } #if CAP_GP EX namespace gp { EX void clear_plainshapes() { for(int m=0; m<3; m++) for(int sd=0; sd<8; sd++) for(int i=0; ipshid[m][sd][i][j][k] = -1; cgi.gpdata->nextid = 0; } void build_plainshape(int& id, gp::local_info& li, cell *c0, int siid, int sidir) { cgi.require_shapes(); id = cgi.gpdata->nextid++; bool master = !(li.relative.first||li.relative.second); int cor = master ? S7 : SG6; if(master) li.last_dir = -1; DEBB(DF_GP, (hr::format("last=%d at=%d,%d tot=%d siid=%d sidir=%d cor=%d id=%d\n", li.last_dir, li.relative.first, li.relative.second, li.total_dir, siid, sidir, cor, id))); cgi.generate_floorshapes_for(id, c0, siid, sidir); cgi.finishshape(); cgi.extra_vertices(); } EX int get_plainshape_id(cell *c) { if(li_for != c) { li_for = c; current_li = get_local_info(c); } int siid, sidir; cell *c1 = c; auto f = [&] { if(geosupport_threecolor() == 2) { auto si = patterns::getpatterninfo(c1, patterns::PAT_COLORING, patterns::SPF_NO_SUBCODES); siid = si.id>>2; // if(siid == 2) si.dir++; // if(siid != pattern_threecolor(c)) printf("threecolor mismatch\n"); // if(pattern_threecolor(createMov(c, c->fixd(si.dir))) != (siid+1)%3) printf("threecolor mismatch direction\n"); sidir = c1->c.fix(si.dir); } else if(geosupport_football() == 2) { siid = !pseudohept(c1); sidir = !ishex1(c1); } else if(geosupport_chessboard()) { siid = !chessvalue(c1); sidir = 0; } else { siid = 0; sidir = 0; } }; if(INVERSE && gp::variation_for(gp::param) == eVariation::goldberg) { c1 = gp::get_mapped(c); UIU(f()); } else if(INVERSE) { siid = 0; sidir = 0; } else f(); auto& id = cgi.gpdata->pshid[siid][sidir][current_li.relative.first&GOLDBERG_MASK][current_li.relative.second&GOLDBERG_MASK][gmod(current_li.total_dir, S6)]; if(id == -1 && sphere && isize(cgi.shFloor.b) > 0) { forCellEx(c1, c) if(!gmatrix0.count(c1)) return 0; } if(id == -1) build_plainshape(id, current_li, c, siid, sidir); return id; } EX } #endif qfloorinfo qfi; EX void set_no_floor() { qfi.fshape = NULL; qfi.shape = NULL; qfi.tinf = NULL; qfi.usershape = -1; } EX void set_floor(floorshape& sh) { qfi.fshape = &sh; qfi.shape = NULL; qfi.tinf = NULL; qfi.usershape = -1; } EX void set_floor(hpcshape& sh) { qfi.shape = &sh; qfi.fshape = NULL; qfi.spin = Id; qfi.tinf = NULL; qfi.usershape = -1; } EX void set_floor(const transmatrix& spin, hpcshape& sh) { qfi.shape = &sh; qfi.fshape = NULL; qfi.spin = spin; qfi.usershape = -1; } /** currently only for arcm */ EX void ensure_floorshape_generated(int id, cell *c) { hpcshape nul; nul.s = -1; sizeto(cgi.shFloor.b, id, nul); if(cgi.shFloor.b[id].s == -1) { cgi.require_shapes(); if(BITRUNCATED) cgi.generate_floorshapes_for(id, c, !arcm::pseudohept(c), arcm::pseudohept(c) ? 0 : 1^(id&1)); else if(geosupport_football() == 2) cgi.generate_floorshapes_for(id, c, !arcm::pseudohept(c), id >= 4 ? 1 : 0); else cgi.generate_floorshapes_for(id, c, 0, 0); cgi.finishshape(); cgi.extra_vertices(); } } EX int shvid(cell *c) { return currentmap->shvid(c); } int hrmap_standard::shvid(cell *c) { if(GOLDBERG) return gp::get_plainshape_id(c); #if CAP_IRR else if(IRREGULAR) return irr::cellindex[c]; #endif else if(geosupport_football() == 2) return pseudohept(c); else if(inforder::mixed()) { int t = c->type; static vector computed; if(isize(computed) <= t) computed.resize(t+1); if(!computed[t]) { computed[t] = true; cell model; heptagon modelh; model.type = t; modelh.type = t; S7 = t; for(int i=0; i &shv, color_t col, PPR prio IS(PPR::DEFAULT)) { if(no_wall_rendering) return NULL; if(!c) return &queuepolyat(V, shv[0], col, prio); else if(WDIM == 3) return NULL; else if(currentmap->strict_tree_rules()) return &queuepolyat(V, shv[shvid(c)], col, prio); #if CAP_GP else if(GOLDBERG) { int id = gp::get_plainshape_id(c); if(isize(shv) > id) return &queuepolyat(V, shv[id], col, prio); return NULL; } #endif #if CAP_IRR else if(IRREGULAR) { int id = irr::cellindex[c]; if(id < 0 || id >= isize(shv)) { return NULL; } return &queuepolyat(V, shv[id], col, prio); } #endif #if CAP_ARCM else if(arcm::in()) { int id = shvid(c); ensure_floorshape_generated(id, c); return &queuepolyat(V, shv[id], col, prio); } #endif else if(GOLDBERG && ishex1(c)) return &queuepolyat(V * pispin, shv[0], col, prio); else if(!(S7&1) && PURE && !aperiodic && !a4) { auto si = patterns::getpatterninfo(c, patterns::PAT_COLORING, 0); if(si.id == 8) si.dir++; transmatrix D = applyPatterndir(c, si); return &queuepolyat(V*D, shv[shvid(c)], col, prio); } else return &queuepolyat(V, shv[shvid(c)], col, prio); } EX void draw_floorshape(cell *c, const shiftmatrix& V, const floorshape &fsh, color_t col, PPR prio IS(PPR::DEFAULT)) { if(no_wall_rendering) return; draw_shapevec(c, V, fsh.b, col, prio); } EX void draw_qfi(cell *c, const shiftmatrix& V, color_t col, PPR prio IS(PPR::DEFAULT), vector floorshape::* tab IS(&floorshape::b)) { if(no_wall_rendering) return; if(qfi.shape) queuepolyat(V * qfi.spin, *qfi.shape, col, prio); else if(qfi.usershape >= 0) { mapeditor::drawUserShape(V * qfi.spin, mapeditor::sgFloor, qfi.usershape, col, c); } else if(!qfi.fshape) ; #if CAP_TEXTURE else if(qfi.tinf) { auto& poly = queuetable(V * qfi.spin, qfi.tinf->vertices, isize(qfi.tinf->vertices), texture::config.mesh_color, texture::config.recolor(col), prio == PPR::DEFAULT ? PPR::FLOOR : prio); poly.tinf = qfi.tinf; poly.offset_texture = 0; poly.flags = POLY_INVERSE; } #endif else draw_shapevec(c, V, (qfi.fshape->*tab), col, prio); } EX bool floorshape_debug; EX void viewmat() { if(floorshape_debug) { shiftmatrix V = ggmatrix(cwt.at); for(int i=0; itype; i++) { shiftpoint ci = V * get_corner_position(cwt.at, i); shiftpoint ci1 = V * get_corner_position(cwt.at, (i+1) % cwt.at->type); shiftpoint cn = V * nearcorner(cwt.at, i); shiftpoint cf0 = V * farcorner(cwt.at, i, 0); shiftpoint cf1 = V * farcorner(cwt.at, i, 1); queuestr(ci, 20, its(i), 0x0000FF, 1); if(vid.grid) queuestr(cn, 20, its(i), 0x00FF00, 1); else queuestr(V * currentmap->adj(cwt.at, i) * C0, 20, its(i), 0x00FFFF, 1); queueline(V * C0, ci, 0xFFFFFFFF, 3); queueline(ci, ci1, 0xFFFF00FF, 3); queueline(ci, cn, 0xFF00FFFF, 3); queueline(ci1, cn, 0xFF0000FF, 3); queueline(ci, cf0, 0x00FFFFFF, 3); queueline(cn, cf0, 0x00FF00FF, 3); queueline(cn, cf1, 0x0000FFFF, 3); } } } auto floor_hook = arg::add1("-floordebug", [] { floorshape_debug = true; }); #endif #if MAXMDIM < 4 || !CAP_GL EX void ensure_vertex_number(basic_textureinfo& bti, int qty) {} EX void ensure_vertex_number(hpcshape& sh) {} EX void bind_floor_texture(hpcshape& li, int id) {} #endif #if MAXMDIM >= 4 && CAP_GL EX ld floor_texture_square_size; void draw_shape_for_texture(floorshape* sh) { int id = sh->id; const ld s1 = 1; const ld s3 = 3 * s1; const ld sd = s1/2; ld gx = (id % 8) * s3 - 3.5 * s3; ld gy = (id / 8) * s3 - 3.5 * s3; if(1) { dynamicval v(vid.linewidth, 8); curvepoint(eupush(gx+s1, gy-s1) * C0); curvepoint(eupush(gx+s1, gy+s1) * C0); curvepoint(eupush(gx-s1, gy+s1) * C0); curvepoint(eupush(gx-s1, gy-s1) * C0); curvepoint(eupush(gx+s1, gy-s1) * C0); queuecurve(shiftless(Id), 0x000000FF, 0xFFFFFFFF - 0x1010100 * (sh->pstrength * 24/10), PPR::LAKELEV); } poly_outline = 0xFFFFFFFF - 0x1010100 * (sh->pstrength * 3/2); for(int a=-1; a<=1; a++) for(int b=-1; b<=1; b++) queuepoly(shiftless(eupush(gx+a, gy+b)), sh->b[0], 0xFFFFFFFF); if(sh == &cgi.shCrossFloor) { queuepoly(shiftless(eupush(gx, gy) * spin(45._deg)), cgi.shCross, 0x808080FF); } if(1) { dynamicval v(vid.linewidth, 8); curvepoint(eupush(gx+sd, gy-sd) * C0); curvepoint(eupush(gx+sd, gy+sd) * C0); curvepoint(eupush(gx-sd, gy+sd) * C0); curvepoint(eupush(gx-sd, gy-sd) * C0); curvepoint(eupush(gx+sd, gy-sd) * C0); queuecurve(shiftless(Id), 0x40404000 + sh->fstrength * 192/10, 0, PPR::LINE); } for(int i=0; i<(ISMOBILE ? 10 : 1000); i++) { hyperpoint h1 = hpxy(sd * (6*randd()-3), sd * (6*randd()-3)); hyperpoint h2 = hpxy(sd * (6*randd()-3), sd * (6*randd()-3)); ld d = hdist(h1, h2); hyperpoint h3 = h1 + (h2-h1) /d * min(d, .1); for(int a=0; a<4; a++) { curvepoint(eupush(gx,gy) * eupush(spin(90._deg*a) * h1) * C0); curvepoint(eupush(gx,gy) * eupush(spin(90._deg*a) * h3) * C0); queuecurve(shiftless(Id), 0x10101010, 0, PPR::LINE); } } auto ftv = get_floor_texture_vertices(sh->id); if(ftv) { ftv->tvertices.clear(); ftv->texture_id = floor_textures->renderedTexture; } hyperpoint center = eupush(gx, gy) * C0; hyperpoint v1 = hpxyz3(sd, sd, 0, 0); hyperpoint v2 = hpxyz3(sd, -sd, 0, 0); if(1) { hyperpoint inmodel; applymodel(shiftless(center), inmodel); glvertex tmap; tmap[0] = (1 + inmodel[0] * pconf.scale) / 2; tmap[1] = (1 - inmodel[1] * pconf.scale) / 2; applymodel(shiftless(center + v1), inmodel); tmap[2] = (1 + inmodel[0] * pconf.scale) / 2 - tmap[0]; floor_texture_map[sh->id] = tmap; } auto tvec_at = [&] (ld x, ld y) { hyperpoint h = center + v1 * x + v2 * y; hyperpoint inmodel; applymodel(shiftless(h), inmodel); glvec2 v; v[0] = (1 + inmodel[0] * pconf.scale) / 2; v[1] = (1 - inmodel[1] * pconf.scale) / 2; return v; }; // SL2 needs 6 times more if(ftv) texture_order([&] (ld x, ld y) { auto v = tvec_at(x, y); ftv->tvertices.push_back(glhr::makevertex(v[0], v[1], 0)); }); floor_texture_square_size = 2 * (tvec_at(1, 0)[0] - tvec_at(0, 0)[0]); } /** copy the texture vertices so that there are at least qty of them */ EX void ensure_vertex_number(basic_textureinfo& bti, int qty) { int s = isize(bti.tvertices); if(!s) return; while(isize(bti.tvertices) <= qty) { for(int i=0; i vi(vid, vid); vid.xres = FLOORTEXTURESIZE; vid.yres = FLOORTEXTURESIZE; pconf.scale = 0.125; dynamicval vm(pconf.cam(), Id); pconf.alpha = 1; dynamicval lw(vid.linewidth, 2); floor_textures = new renderbuffer(vid.xres, vid.yres, vid.usingGL); resetbuffer rb; int q = isize(all_escher_floorshapes) + isize(all_plain_floorshapes); floor_texture_vertices.resize(q); floor_texture_map.resize(q); auto cd = current_display; cd->xtop = cd->ytop = 0; cd->xsize = cd->ysize = FLOORTEXTURESIZE; cd->xcenter = cd->ycenter = cd->scrsize = FLOORTEXTURESIZE/2; cd->radius = cd->scrsize * pconf.scale; floor_textures->enable(); #if CAP_VR dynamicval i(vrhr::state, 0); #endif floor_textures->clear(0); // 0xE8E8E8 = 1 // gradient vertices vector gv; gv.emplace_back(-1, -1, 0, 0, 0); gv.emplace_back(+1, -1, 0, 0, 0); gv.emplace_back(+1, +1, 1, 1, 1); gv.emplace_back(-1, -1, 0, 0, 0); gv.emplace_back(+1, +1, 1, 1, 1); gv.emplace_back(-1, +1, 1, 1, 1); #if CAP_RAY dynamicval riu(ray::in_use, false); #endif if(1) { current_display->next_shader_flags = GF_VARCOLOR; dynamicval m(pmodel, mdPixel); current_display->set_all(0,0); glhr::new_projection(); glhr::id_modelview(); glhr::prepare(gv); glhr::set_depthtest(false); glDrawArrays(GL_TRIANGLES, 0, isize(gv)); } shOverFloor.pstrength = 20; shFeatherFloor.pstrength = 40; shFeatherFloor.fstrength = 5; shTrollFloor.pstrength = 25; shCaveFloor.pstrength = 40; shCaveFloor.fstrength = 0; shDesertFloor.pstrength = 30; shDesertFloor.fstrength =10; shRoseFloor.pstrength = 30; shDragonFloor.pstrength = 30; shBarrowFloor.pstrength = 40; // all using Tortoise for(auto v: all_escher_floorshapes) if(v->shapeid2 == 178) v->pstrength = 20; ptds.clear(); for(auto v: all_plain_floorshapes) draw_shape_for_texture(v); for(auto v: all_escher_floorshapes) draw_shape_for_texture(v); drawqueue(); /* SDL_Surface *sdark = floor_textures->render(); IMAGESAVE(sdark, "texture-test.png"); */ rb.reset(); last_texture_step = vid.texture_step; } EX void make_floor_textures() { if(noGUI || !vid.usingGL) return; DEBBI(DF_POLY, ("make_floor_textures")); dynamicval g(geometry, gEuclidSquare); dynamicval gm(pmodel, mdDisk); dynamicval va(variation, eVariation::pure); dynamicval gie(ginf[geometry].g, giEuclid2); dynamicval gief(ginf[geometry].flags, qOPTQ); dynamicval gih(ginf[gNormal].g, giHyperb2); dynamicval gihf(ginf[gNormal].flags, 0); dynamicval a3(vid.always3, false); dynamicval hq(inHighQual, true); dynamicval hd(darken, 0); dynamicval hll(levellines, 0); dynamicval gd(vid.depth, 1); dynamicval gc(vid.camera, 1); dynamicval dcgip(cgip, cgip); dynamicval gvs(vid.stereo_mode, sOFF); dynamicval vgp(global_projection, 0); check_cgi(); cgi.make_floor_textures_here(); /* update texture ID in existing cgi's */ for(auto& c: cgis) c.second.models_texture.texture_id = floor_textures->renderedTexture; } #endif }