// HyperRogue // Copyright (C) 2011-2019 Zeno Rogue, see 'hyper.cpp' for details /** \file 3d-models.cpp * \brief This file contains the routines to convert HyperRogue's old vector graphics into 3D models */ #include "hyper.h" #include "earcut.hpp" namespace hr { ld eyepos; #if MAXMDIM >= 4 #define S (cgi.scalefactor / 0.805578) #define SH (cgi.scalefactor / 0.805578 * vid.height_width / 1.5) #define revZ ((WDIM == 2 || hybri) ? -1 : 1) hyperpoint shcenter; EX hyperpoint front_leg, rear_leg; EX transmatrix front_leg_move, rear_leg_move, front_leg_move_inverse, rear_leg_move_inverse; EX ld leg_length; vector<hyperpoint> geometry_information::get_shape(hpcshape sh) { vector<hyperpoint> res; for(int i=sh.s; i<sh.e-1; i++) res.push_back(hpc[i]); return res; } EX hyperpoint normalize_flat(hyperpoint h) { if(prod) return product_decompose(h).second; if(sl2) h = slr::translate(h) * zpush0(-atan2(h[2], h[3])); return normalize(h); } hyperpoint get_center(const vector<hyperpoint>& vh) { hyperpoint h = Hypc; for(auto h1: vh) h = h + h1; return normalize_flat(h); } EX ld zc(ld z) { if(WDIM == 2 && GDIM == 3) return geom3::lev_to_factor(cgi.human_height * z); return cgi.human_height * (z - 0.5) * revZ; } void geometry_information::add_cone(ld z0, const vector<hyperpoint>& vh, ld z1) { last->flags |= POLY_TRIANGLES; for(int i=0; i<isize(vh); i++) { hpcpush(zpush(z0) * vh[i]); hpcpush(zpush(z0) * vh[(i+1) % isize(vh)]); hpcpush(zpush(z1) * shcenter); } } void geometry_information::add_prism_sync(ld z0, vector<hyperpoint> vh0, ld z1, vector<hyperpoint> vh1) { last->flags |= POLY_TRIANGLES; for(int i=0; i<isize(vh0); i++) { int i1 = (i+1) % isize(vh0); hpcpush(zpush(z0) * vh0[i]); hpcpush(zpush(z1) * vh1[i]); hpcpush(zpush(z0) * vh0[i1]); hpcpush(zpush(z1) * vh1[i]); hpcpush(zpush(z0) * vh0[i1]); hpcpush(zpush(z1) * vh1[i1]); } } void geometry_information::add_prism(ld z0, vector<hyperpoint> vh0, ld z1, vector<hyperpoint> vh1) { last->flags |= POLY_TRIANGLES; struct mixed { ld angle; int owner; hyperpoint h; mixed(ld a, int o, hyperpoint _h) : angle(a), owner(o), h(_h) {} }; transmatrix T0 = gpushxto0(get_center(vh0)); transmatrix T1 = gpushxto0(get_center(vh1)); vector<mixed> pairs; for(auto h: vh0) pairs.emplace_back(atan2(T0*h), 0, h); for(auto h: vh1) pairs.emplace_back(atan2(T1*h), 1, h); sort(pairs.begin(), pairs.end(), [&] (const mixed p, const mixed q) { return p.angle < q.angle; }); hyperpoint lasts[2]; for(auto pp: pairs) lasts[pp.owner] = pp.h; for(auto pp: pairs) { int id = pp.owner; hpcpush(zpush(z0) * lasts[0]); hpcpush(zpush(z1) * lasts[1]); hpcpush(zpush(id == 0 ? z0 : z1) * pp.h); lasts[id] = pp.h; } } void geometry_information::shift_last(ld z) { for(int i=last->s; i<isize(hpc); i++) hpc[i] = zshift(hpc[i], z); } void geometry_information::shift_shape(hpcshape& sh, ld z) { for(int i=sh.s; i<sh.e; i++) hpc[i] = zshift(hpc[i], z); } void geometry_information::shift_shape_orthogonally(hpcshape& sh, ld z) { for(int i=sh.s; i<sh.e; i++) hpc[i] = orthogonal_move(hpc[i], z); } extern renderbuffer *floor_textures; void geometry_information::add_texture(hpcshape& sh) { if(!floor_textures) return; auto& utt = models_texture; sh.tinf = &utt; sh.texture_offset = isize(utt.tvertices); for(int i=sh.s; i<isize(hpc); i++) { hyperpoint h = hpc[i]; if(prod) h = product::inverse_exp(h); ld rad = hypot_d(3, h); ld factor = 0.50 + (0.17 * h[2] + 0.13 * h[1] + 0.15 * h[0]) / rad; utt.tvertices.push_back(glhr::makevertex(0, factor, 0)); } } vector<hyperpoint> scaleshape(const vector<hyperpoint>& vh, ld s) { vector<hyperpoint> res; for(hyperpoint h: vh) res.push_back(normalize_flat(h * s + shcenter * (1-s))); return res; } ld get_zlevel(hyperpoint h) { if(prod) return zlevel(h); if(sl2) return atan2(h[2], h[3]); return asin_auto(h[2]); } void geometry_information::make_ha_3d(hpcshape& sh, bool isarmor, ld scale) { shcenter = C0; auto groin = get_shape(shHumanGroin); auto body = get_shape(shPBodyOnly); auto neck = get_shape(shHumanNeck); auto hand = get_shape(shPBodyHand); auto arm = get_shape(shPBodyArm); groin = scaleshape(groin, scale); neck = scaleshape(neck, scale); auto fullbody = get_shape(sh); auto body7 = body[7]; auto body26 = body[26]; body.clear(); bool foundplus = false, foundminus = false; for(hyperpoint h: fullbody) { if(h[1] > 0.14 * S) { if(foundplus) ; else foundplus = true, body.push_back(body7); } else if(h[1] < -0.14 * S) { if(foundminus) ; else foundminus = true, body.push_back(body26); } else body.push_back(h); } auto arm8 = arm[8]; bool armused = false; arm.clear(); for(hyperpoint h: fullbody) { if(h[1] < 0.08 * S) ; else if(h[0] > -0.03 * S) { if(armused) ; else armused = true, arm.push_back(arm8); } else arm.push_back(h); } auto hand0 = hand[0]; hand.clear(); hand.push_back(hand0); for(hyperpoint h: fullbody) { if(h[1] + h[0] > 0.13 * S) hand.push_back(h); } bshape(sh, PPR::MONSTER_BODY); add_cone(zc(0.4), groin, zc(0.36)); add_prism_sync(zc(0.4), groin, zc(0.6), groin); add_prism(zc(0.6), groin, zc(0.7), body); add_prism(zc(0.7), body, zc(0.8), neck); add_cone(zc(0.8), neck, zc(0.83)); int at0 = isize(hpc); ld h = human_height; if(isize(arm) > 3) { shcenter = get_center(arm); int arm0 = isize(hpc); add_prism_sync(BODY - h*.03, arm, BODY + h*.03, arm); add_cone(BODY + h*.03, arm, BODY + h*.05); add_cone(BODY - h*.03, arm, BODY - h*.05); int arm1 = isize(hpc); for(int i=arm0; i<arm1; i++) { hyperpoint h = hpc[i]; ld zl = get_zlevel(h); h = zpush(-zl) * h; ld rad = hdist0(h); rad = (rad - 0.1124*S) / (0.2804*S - 0.1124*S); rad = 1 - rad; rad *= zc(0.7) - BODY; hpc[i] = zpush(rad) * hpc[i]; } } // 0.2804 - keep // 0.1124 - move if(isize(hand) > 3) { shcenter = get_center(hand); add_cone(BODY, hand, BODY + 0.05 * human_height); add_cone(BODY, hand, BODY - 0.05 * human_height); } int at1 = isize(hpc); for(int i=at0; i<at1; i++) hpc.push_back(Mirror * hpc[i]); add_texture(sh); shift_last(-BODY); } void geometry_information::make_humanoid_3d(hpcshape& sh) { make_ha_3d(sh, false, 1); } void geometry_information::addtri(array<hyperpoint, 3> hs, int kind) { ld ds[3]; ds[0] = hdist(hs[0], hs[1]); ds[1] = hdist(hs[1], hs[2]); ds[2] = hdist(hs[2], hs[0]); ld maxds = 0; for(int i=0; i<3; i++) maxds = max(ds[i], maxds) - 1e-3; if(maxds > 0.02*S) for(int i=0; i<3; i++) { int j = (i+1) % 3; int k = (j+1) % 3; if(hdist(hs[i], hs[j]) > maxds) { auto hm = mid(hs[i], hs[j]); addtri(make_array(hm, hs[i], hs[k]), kind); addtri(make_array(hm, hs[j], hs[k]), kind); return; } } if(kind) { array<hyperpoint, 3> ht; ld hsh[3]; ld shi[3]; bool ok = true; ld zzes[3]; for(int s=0; s<3; s++) { hs[s] = normalize_flat(hs[s]); hyperpoint h = hs[s]; ld zz = zc(0.78); hsh[s] = abs(h[1]); zz -= h[1] * h[1] / 0.14 / 0.14 * 0.01 / S / S * SH; zz -= h[0] * h[0] / 0.10 / 0.10 * 0.01 / S / S * SH; if(abs(h[1]) > 0.14*S) ok = false, zz -= revZ * (abs(h[1])/S - 0.14) * SH; if(abs(h[0]) > 0.08*S) ok = false, zz -= revZ * (abs(h[0])/S - 0.08) * (abs(h[0])/S - 0.08) * 25 * SH; h = normalize_flat(h); if(!prod || kind != 1) ht[s] = zpush(zz) * h; else ht[s] = h; if(hsh[s] < 0.1*S) shi[s] = 0.5; else if(hsh[s] < 0.12*S) shi[s] = 0.1 + 0.4 * (hsh[s]/S - 0.1) / (0.12 - 0.1); else shi[s] = 0.1; zzes[s] = zz; } if(ok && kind == 1) { array<array<hyperpoint, 3>, 6> htx; for(int i=0; i<6; i++) htx[i] = ht; for(int i=0; i<3; i++) { htx[0][i][0] *= 0.7; htx[0][i][1] *= 0.7; htx[1][i][0] *= 1.2; htx[1][i][1] *= 1.7; htx[2][i][1] *= 1.7; htx[4][i][0] = htx[4][i][0] * 0.4 + scalefactor * 0.1; htx[5][i][0] = htx[5][i][0] * 0.3 + scalefactor * 0.1; if(!prod) for(int a=0; a<6; a++) htx[a][i] = hpxy3(htx[a][i][0], htx[a][i][1], htx[a][i][2]); else for(int a=0; a<6; a++) htx[a][i] = zpush(zzes[i]) * hpxy(htx[a][i][0], htx[a][i][1]); } ld levels[6] = {0, 0.125, 0.125, 0.250, 0.375, 0.5}; for(int a=0; a<6; a++) for(int i=0; i<3; i++) htx[a][i] = zpush(-min(shi[i], levels[a]) * human_height * revZ) * htx[a][i]; hpcpush(htx[0][0]); hpcpush(htx[0][1]); hpcpush(htx[0][2]); for(int a=0; a<5; a++) for(int i=0; i<3; i++) { int j = (i+1) % 3; int b = a+1; hpcpush(htx[a][i]); hpcpush(htx[a][j]); hpcpush(htx[b][i]); hpcpush(htx[a][j]); hpcpush(htx[b][i]); hpcpush(htx[b][j]); } } else hpcpush(ht[0]), hpcpush(ht[1]), hpcpush(ht[2]); } else { for(int s=0; s<3; s++) { hyperpoint h = hs[s]; ld zz = zc(eyepos); if(h[0] < -0.05*S) zz += revZ * (h[0]/S + 0.05) * SH; if(hdist0(h) <= 0.0501*S) { zz += revZ * sqrt(0.0026 - pow(hdist0(h)/S, 2)) * SH; } hpcpush(zpush(zz) * h); } } } void disable(hpcshape& sh) { sh.s = sh.e = 0; } void geometry_information::make_armor_3d(hpcshape& sh, int kind) { if(BADMODEL) { disable(sh); return; } auto body = get_shape(sh); vector<vector<array<ld, 2> >> pts(2); for(hyperpoint h: body) { array<ld, 2> p; p[0] = h[0] / h[LDIM]; p[1] = h[1] / h[LDIM]; pts[0].emplace_back(p); } bshape(sh, sh.prio); vector<int> indices = mapbox::earcut<int> (pts); last->flags |= POLY_TRIANGLES; last->flags |= POLY_TRIANGLES; for(int k=0; k<isize(indices); k+=3) { addtri(make_array(body[indices[k]], body[indices[k+1]], body[indices[k+2]]), kind); } add_texture(sh); if(&sh == &shHood || &sh == &shWightCloak || &sh == &shArmor) shift_last(-HEAD); else shift_last(-BODY); } void geometry_information::make_foot_3d(hpcshape& sh) { auto foot = get_shape(sh); auto leg = get_shape(shHumanLeg); auto leg5 = scaleshape(leg, 0.8); bshape(sh, PPR::MONSTER_BODY); shcenter = get_center(leg); add_cone(zc(0), foot, zc(0)); add_prism(zc(0), foot, zc(0.1), leg); add_prism_sync(zc(0.1), leg, zc(0.4), leg5); add_cone(zc(0.4), leg5, zc(0.45)); add_texture(sh); // shift_last(-LEG0); for(int i=last->s; i<isize(hpc); i++) hpc[i] = cpush(0, -0.0125*S) * hpc[i]; } void geometry_information::make_head_only() { auto addpt = [this] (int d, int u) { hpcpush(zpush(zc(eyepos) + 0.06 * SH * sin(u * degree)) * xspinpush0(d * degree, 0.05 * S * cos(u * degree))); }; bshape(shPHeadOnly, shPHeadOnly.prio); last->flags |= POLY_TRIANGLES; for(int d=0; d<360; d+=(BADMODEL ? 60 : 30)) for(int u=-90; u<=90; u+=(BADMODEL ? 90 : 30)) { addpt(d, u); addpt(d+30, u); addpt(d, u+30); addpt(d+30, u+30); addpt(d+30, u); addpt(d, u+30); } add_texture(shPHeadOnly); shift_last(-HEAD - revZ * 0.01 * SH); } void geometry_information::make_head_3d(hpcshape& sh) { auto head = get_shape(sh); vector<vector<array<ld, 2> >> pts(2); for(hyperpoint h: head) { array<ld, 2> p; p[0] = h[0] / h[LDIM]; p[1] = h[1] / h[LDIM]; pts[0].emplace_back(p); } array<ld, 2> zero = make_array<ld>(0,0); pts[1].emplace_back(zero); head.push_back(C0); bshape(sh, sh.prio); vector<int> indices = mapbox::earcut<int> (pts); last->flags |= POLY_TRIANGLES; for(int k=0; k<isize(indices); k+=3) { addtri(make_array(head[indices[k]], head[indices[k+1]], head[indices[k+2]]), 0); } add_texture(sh); shift_last(-HEAD); } void geometry_information::make_paw_3d(hpcshape& sh, hpcshape& legsh) { auto foot = get_shape(sh); auto leg = get_shape(legsh); bshape(sh, PPR::MONSTER_BODY); shcenter = get_center(leg); add_cone(zc(0), foot, zc(0)); add_prism(zc(0), foot, zc(0.1), leg); add_prism_sync(zc(0.1), leg, zc(0.4), leg); add_cone(zc(0.4), leg, zc(0.45)); add_texture(sh); } void geometry_information::make_abody_3d(hpcshape& sh, ld tail) { auto body = get_shape(sh); shcenter = get_center(body); vector<hyperpoint> notail; ld minx = 9; for(hyperpoint h: body) minx = min(minx, h[0]); for(hyperpoint h: body) if(h[0] >= minx + tail) notail.push_back(h); auto body8 = scaleshape(notail, 0.8); bshape(sh, PPR::MONSTER_BODY); add_prism(zc(0.4), body8, zc(0.45), body); add_prism(zc(0.45), body, zc(0.5), notail); add_prism_sync(zc(0.6), body8, zc(0.5), notail); add_cone(zc(0.4), body8, zc(0.36)); add_cone(zc(0.6), body8, zc(0.64)); add_texture(sh); if(GDIM == 3 && WDIM == 2) shift_last(-ABODY); } void geometry_information::make_ahead_3d(hpcshape& sh) { auto body = get_shape(sh); shcenter = get_center(body); auto body8 = scaleshape(body, 0.5); bshape(sh, PPR::MONSTER_BODY); add_prism_sync(zc(0.4), body8, zc(0.5), body); add_prism_sync(zc(0.6), body8, zc(0.5), body); add_cone(zc(0.4), body8, zc(0.36)); add_cone(zc(0.6), body8, zc(0.64)); add_texture(sh); } void geometry_information::make_skeletal(hpcshape& sh, ld push) { auto body = get_shape(sh); shcenter = get_center(body); bshape(sh, PPR::MONSTER_BODY); add_prism_sync(zc(0.48), body, zc(0.5), body); add_prism_sync(zc(0.52), body, zc(0.5), body); add_cone(zc(0.48), body, zc(0.47)); add_cone(zc(0.52), body, zc(0.53)); add_texture(sh); shift_last(-push); } hyperpoint yzspin(ld alpha, hyperpoint h) { if(prod) return product::direct_exp(cspin(1, 2, alpha) * product::inverse_exp(h)); else return cspin(1, 2, alpha) * h; } void geometry_information::make_revolution(hpcshape& sh, int mx, ld push) { auto body = get_shape(sh); bshape(sh, PPR::MONSTER_BODY); int step = (BADMODEL ? 60 : (mx == 360 ? 24 : 10)); for(int i=0; i<isize(body); i++) { hyperpoint h0 = body[i]; hyperpoint h1 = body[(i+1) % isize(body)]; for(int s=0; s<mx; s+=step) { hpcpush(yzspin(s * degree, h0)); hpcpush(yzspin(s * degree, h1)); hpcpush(yzspin((s+step) * degree, h0)); hpcpush(yzspin(s * degree, h1)); hpcpush(yzspin((s+step) * degree, h0)); hpcpush(yzspin((s+step) * degree, h1)); } } last->flags |= POLY_TRIANGLES; add_texture(sh); shift_last(-push); } void geometry_information::make_revolution_cut(hpcshape &sh, int each, ld push, ld width) { auto body = get_shape(sh); body.resize(isize(body) / 2); ld fx = body[0][0]; ld lx = body.back()[0]; body.insert(body.begin(), hpxy(fx + (fx-lx) * 1e-3, 0)); body.push_back(hpxy(lx + (lx-fx) * 1e-3, 0)); int n = isize(body); auto gbody = body; int it = 0; vector<int> nextid(n); vector<int> lastid(n); vector<bool> stillin(n, true); for(int i=0; i<n; i++) nextid[i] = i+1; for(int i=0; i<n; i++) lastid[i] = i-1; nextid[n-1] = n-1; lastid[0] = 0; while(true) { it++; int cand = -1; ld cv = 0; for(int i=1; i<n-1; i++) if(stillin[i]) { if((gbody[i][0] < gbody[lastid[i]][0] && gbody[i][0] < gbody[nextid[i]][0]) || (gbody[i][0] > gbody[lastid[i]][0] && gbody[i][0] > gbody[nextid[i]][0]) || abs(gbody[i][1]) > width) if(abs(gbody[i][1]) > cv) cand = i, cv = abs(gbody[i][1]); } if(cand == -1) break; int i = cand; lastid[nextid[i]] = lastid[i]; nextid[lastid[i]] = nextid[i]; stillin[i] = false; } for(int i=n-1; i>=0; i--) if(!stillin[i] && !stillin[nextid[i]]) nextid[i] = nextid[nextid[i]]; for(int i=0; i<n; i++) if(!stillin[i] && !stillin[lastid[i]]) lastid[i] = lastid[lastid[i]]; for(int i=0; i<n; i++) { if(!stillin[i]) gbody[i] = normalize_flat(gbody[lastid[i]] * (i - lastid[i]) + gbody[nextid[i]] * (nextid[i] - i)); } bshape(sh, PPR::MONSTER_BODY); int step = (BADMODEL ? 60 : 10); for(int i=0; i<n; i++) { for(int s=0; s<360; s+=step) { auto& tbody = BADMODEL ? gbody : (s % each ? gbody : body); auto& nbody = BADMODEL ? gbody : ((s+step) % each ? gbody : body); int i1 = (i+1) % isize(body); hyperpoint h0 = tbody[i]; hyperpoint h1 = tbody[i1]; hyperpoint hs0 = nbody[i]; hyperpoint hs1 = nbody[i1]; hpcpush(yzspin(s * degree, h0)); hpcpush(yzspin(s * degree, h1)); hpcpush(yzspin((s+step) * degree, hs0)); hpcpush(yzspin(s * degree, h1)); hpcpush(yzspin((s+step) * degree, hs0)); hpcpush(yzspin((s+step) * degree, hs1)); } } last->flags |= POLY_TRIANGLES; add_texture(sh); shift_last(-push); if(&sh == &shDogTorso) { finishshape(); shDogStripes = shDogTorso; add_texture(shDogStripes); auto& utt = models_texture; int a = (6 * 360 / step); for(int i=0; i<shDogStripes.e - shDogStripes.s; i++) if(i % (2 * a) < a) utt.tvertices[i + shDogStripes.texture_offset][1] /= 4; } } void geometry_information::clone_shape(hpcshape& sh, hpcshape& target) { target = sh; target.s = isize(hpc); for(int i=sh.s; i<sh.e; i++) hpc.push_back(hpc[i]); target.e = isize(hpc); } void geometry_information::animate_bird(hpcshape& orig, hpcshape_animated& animated, ld body) { for(int i=0; i<=WINGS; i++) { auto& tgt = animated[i]; clone_shape(orig, tgt); ld alpha = cos(180. * degree * i / WINGS) * 30 * degree; for(int i=tgt.s; i<tgt.e; i++) { if(abs(hpc[i][1]) > body) { ld off = hpc[i][1] > 0 ? body : -body; hpc[i][2] += abs(hpc[i][1] - off) * sin(alpha); hpc[i][1] = off + (hpc[i][1] - off) * cos(alpha); hpc[i] = normalize(hpc[i]); } } } // for(int i=0; i<30; i++) shift_shape(animated[i], BIRD); // shift_shape(orig, BIRD); } void geometry_information::slimetriangle(hyperpoint a, hyperpoint b, hyperpoint c, ld rad, int lev) { dynamicval<int> d(vid.texture_step, 8); texture_order([&] (ld x, ld y) { ld z = 1-x-y; ld r = scalefactor * hcrossf7 * (0 + pow(max(x,max(y,z)), .3) * 0.8) * (hybri ? .5 : 1); hyperpoint h = direct_exp(tangent_length(a*x+b*y+c*z, r), 10); hpcpush(h); }); } void geometry_information::balltriangle(hyperpoint a, hyperpoint b, hyperpoint c, ld rad, int lev) { if(lev == 0) { hpcpush(direct_exp(a, 10)); hpcpush(direct_exp(b, 10)); hpcpush(direct_exp(c, 10)); } else { auto midpoint = [&] (hyperpoint h1, hyperpoint h2) { return tangent_length(h1+h2, rad); }; hyperpoint cx = midpoint(a, b); hyperpoint ax = midpoint(b, c); hyperpoint bx = midpoint(c, a); balltriangle(ax, bx, cx, rad, lev-1); balltriangle(ax, bx, c , rad, lev-1); balltriangle(ax, b , cx, rad, lev-1); balltriangle(a , bx, cx, rad, lev-1); } } void geometry_information::make_ball(hpcshape& sh, ld rad, int lev) { bshape(sh, sh.prio); sh.flags |= POLY_TRIANGLES; hyperpoint tip = xtangent(rad); hyperpoint atip = xtangent(-rad); ld z = 63.43 * degree; for(int i=0; i<5; i++) { hyperpoint a = cspin(1, 2, (72 * i ) * degree) * spin(z) * tip; hyperpoint b = cspin(1, 2, (72 * i-72) * degree) * spin(z) * tip; hyperpoint c = cspin(1, 2, (72 * i+36) * degree) * spin(M_PI-z) * tip; hyperpoint d = cspin(1, 2, (72 * i-36) * degree) * spin(M_PI-z) * tip; balltriangle(tip, a, b, rad, lev); balltriangle(a, b, c, rad, lev); balltriangle(b, c, d, rad, lev); balltriangle(c, d, atip, rad, lev); } add_texture(sh); } void geometry_information::hpcsquare(hyperpoint h1, hyperpoint h2, hyperpoint h3, hyperpoint h4) { last->flags |= POLY_TRIANGLES; hpcpush(h1); hpcpush(h2); hpcpush(h3); hpcpush(h2); hpcpush(h3); hpcpush(h4); } void geometry_information::make_star(hpcshape& sh, ld rad) { bshape(sh, sh.prio); rad = sinh(rad); sh.flags |= POLY_TRIANGLES | POLY_INTENSE; int steps = (BADMODEL ? 8 : 64); for(int a=0; a<steps; a++) { ld z0 = (a-steps/2)*2.0/steps; ld z1 = (a-steps/2-1)*2.0/steps; ld r0 = sqrt(1 - z0*z0) * rad; ld r1 = sqrt(1 - z1*z1) * rad; z0 *= rad; z1 *= rad; for(int b=0; b<360; b+=(BADMODEL?60:15)) { ld b0 = b * degree; ld b1 = (b+15) * degree; hpcsquare( hpxy3(r0 * cos(b0), r0 * sin(b0), z0), hpxy3(r0 * cos(b1), r0 * sin(b1), z0), hpxy3(r1 * cos(b0), r1 * sin(b0), z1), hpxy3(r1 * cos(b1), r1 * sin(b1), z1) ); } } } void geometry_information::make_euclidean_sky() { bshape(cgi.shEuclideanSky, PPR::EUCLIDEAN_SKY); for(int x=-20; x<20; x++) for(int y=-20; y<20; y++) hpcsquare( zpush(cgi.WALL) * hpxy(x, y), zpush(cgi.WALL) * hpxy(x, y+1), zpush(cgi.WALL) * hpxy(x+1, y), zpush(cgi.WALL) * hpxy(x+1, y+1) ); } /** res[0] and res[1] place H on the plane, while res[2] is the altitude */ hyperpoint psmin(hyperpoint H) { if(prod) { auto d = product_decompose(H); d.second[2] = d.first; return d.second; } hyperpoint res; res[2] = asin_auto(H[2]); ld cs = pow(cos_auto(res[2]), 2); ld r = sqrt(cs+H[0]*H[0]+H[1]*H[1]); res[0] = H[0] / r; res[1] = H[1] / r; return res; } void geometry_information::adjust_eye(hpcshape& eye, hpcshape head, ld shift_eye, ld shift_head, int q, ld zoom) { hyperpoint center = Hypc; for(int i=eye.s; i<eye.e; i++) if(q == 1 || hpc[i][1] > 0) center += hpc[i]; center = normalize_flat(center); // center /= (eye.e - eye.s); ld rad = 0; for(int i=eye.s; i<eye.e; i++) if(q == 1 || hpc[i][1] > 0) rad += hdist(center, hpc[i]); rad /= (eye.e - eye.s); hyperpoint pscenter = psmin(center); ld pos = 0; int qty = 0, qtyall = 0; vector<hyperpoint> pss; for(int i=head.s; i<head.e; i++) pss.push_back(psmin(zpush(shift_head) * hpc[i])); ld zmid = 0; for(hyperpoint& h: pss) zmid += h[2]; zmid /= isize(pss); ld mindist = 1e9; for(int i=0; i<isize(pss); i+=3) if(pss[i][2] < zmid || (WDIM == 3 && !prod)) { ld d = sqhypot_d(2, pss[i]-pscenter) + sqhypot_d(2, pss[i+1]-pscenter) + sqhypot_d(2, pss[i+2]-pscenter); if(d < mindist) mindist = d, pos = min(min(pss[i][2], pss[i+1][2]), pss[i+2][2]), qty++; qtyall++; } if(&eye == &shSkullEyes) cgi.eyelevel_human = pos = zc(eyepos) - 0.06 * SH * 0.05; if(&eye == &shWolf1) cgi.eyelevel_dog = pos; if(&eye == &shFamiliarEye) cgi.eyelevel_familiar = pos; make_ball(eye, rad, 0); transmatrix T = zpush(-shift_eye) * rgpushxto0(center) * zpush(pos); for(int i=eye.s; i<isize(hpc); i++) hpc[i] = T * hpc[i]; int s = isize(hpc); if(&eye == &shSkullEyes) for(int i=eye.s; i<s; i++) hpc[i] = xpush(0.07 * scalefactor) * hpc[i]; if(q == 2) for(int i=eye.s; i<s; i++) { hpcpush(MirrorY * hpc[i]); auto& utt = models_texture; utt.tvertices.push_back(utt.tvertices[i - eye.s + eye.texture_offset]); } finishshape(); // eye.prio = PPR::SUPERLINE; } void geometry_information::shift_last_straight(ld z) { for(int i=last->s; i<isize(hpc); i++) hpc[i] = zpush(z) * hpc[i]; } EX void queueball(const transmatrix& V, ld rad, color_t col, eItem what) { if(what == itOrbSpeed) { transmatrix V1 = V * cspin(1, 2, M_PI/2); ld tt = ptick(100); for(int t=0; t<5; t++) { for(int a=-50; a<50; a++) curvepoint(V1 * cspin(0, 2, a * M_PI/100.) * cspin(0, 1, t * 72 * degree + tt + a*2*M_PI/50.) * xpush0(rad)); queuecurve(col, 0, PPR::LINE); } return; } ld z = 63.43 * degree; transmatrix V1 = V * cspin(0, 2, M_PI/2); if(what == itOrbShield) V1 = V * cspin(0, 1, ptick(500)); if(what == itOrbFlash) V1 = V * cspin(0, 1, ptick(1500)); if(what == itOrbShield) V1 = V * cspin(1, 2, ptick(1000)); if(what == itOrbFlash) V1 = V * cspin(1, 2, ptick(750)); if(what == itDiamond) V1 = V * cspin(1, 2, ptick(200)); if(what == itRuby) V1 = V * cspin(1, 2, ptick(300)); auto line = [&] (transmatrix A, transmatrix B) { hyperpoint h0 = A * xpush0(1); hyperpoint h1 = B * xpush0(1); for(int i=0; i<=8; i++) curvepoint(V1 * rspintox(normalize(h0*(8-i) + h1*i)) * xpush0(rad)); queuecurve(col, 0, PPR::LINE); }; for(int i=0; i<5; i++) { auto a = cspin(1, 2, (72 * i ) * degree) * spin(z); auto b = cspin(1, 2, (72 * i-72) * degree) * spin(z); auto c = cspin(1, 2, (72 * i+36) * degree) * spin(M_PI-z); auto d = cspin(1, 2, (72 * i-36) * degree) * spin(M_PI-z); line(Id, a); line(a, b); line(a, c); line(a, d); line(d, c); line(c, spin(M_PI)); } } void geometry_information::make_shadow(hpcshape& sh) { sh.shs = isize(hpc); for(int i=sh.s; i < sh.e; i++) hpcpush(orthogonal_move(hpc[i], FLOOR - human_height / 100)); sh.she = isize(hpc); } void geometry_information::make_3d_models() { if(GDIM == 2 || noGUI) return; eyepos = WDIM == 2 ? 0.875 : 0.925; DEBBI(DF_POLY, ("make_3d_models")); shcenter = C0; if(floor_textures) { auto& utt = models_texture; utt.tvertices.clear(); utt.texture_id = floor_textures->renderedTexture; } if(WDIM == 2) { DEBB(DF_POLY, ("shadows")); for(hpcshape* sh: {&shBatWings, &shBugBody, &shBullBody, &shButterflyWing, &shCatBody, &shDogBody, &shDogTorso, &shEagle, &shFemaleBody, &shFlailMissile, &shGadflyWing, &shGargoyleWings, &shHawk, &shJiangShi, &shKnife, &shPBody, &shPHead, &shRaiderBody, &shReptileBody, &shSkeletonBody, &shTongue, &shTrapArrow, &shTrylobite, &shWaterElemental, &shWolfBody, &shYeti, &shWormHead, &shWormHead, &shDragonHead, &shDragonSegment, &shDragonTail, &shTentacleX, &shTentHead, &shILeaf[0], &shILeaf[1], &shWormSegment, &shSmallWormSegment }) make_shadow(*sh); for(int i=0; i<8; i++) make_shadow(shAsteroid[i]); } DEBB(DF_POLY, ("humanoids")); make_humanoid_3d(shPBody); make_humanoid_3d(shYeti); make_humanoid_3d(shFemaleBody); make_humanoid_3d(shRaiderBody); make_humanoid_3d(shSkeletonBody); make_humanoid_3d(shFatBody); make_humanoid_3d(shWaterElemental); make_humanoid_3d(shJiangShi); // shFatBody = shPBody; // shFemaleBody = shPBody; // shRaiderBody = shPBody; // shJiangShi = shPBody; DEBB(DF_POLY, ("heads")); make_head_3d(shFemaleHair); make_head_3d(shPHead); make_head_3d(shTurban1); make_head_3d(shTurban2); make_head_3d(shAztecHead); make_head_3d(shAztecCap); make_head_3d(shVikingHelmet); make_head_3d(shRaiderHelmet); make_head_3d(shWestHat1); make_head_3d(shWestHat2); make_head_3d(shWitchHair); make_head_3d(shBeautyHair); make_head_3d(shFlowerHair); make_head_3d(shGolemhead); make_head_3d(shPirateHood); make_head_3d(shEyepatch); make_head_3d(shSkull); make_head_3d(shDemon); make_head_3d(shGoatHead); make_head_3d(shJiangShiCap1); make_head_3d(shJiangShiCap2); make_head_3d(shTerraHead); DEBB(DF_POLY, ("armors")); make_armor_3d(shKnightArmor); make_armor_3d(shKnightCloak, 2); make_armor_3d(shPrinceDress); make_armor_3d(shPrincessDress, 2); make_armor_3d(shTerraArmor1); make_armor_3d(shTerraArmor2); make_armor_3d(shTerraArmor3); make_armor_3d(shSuspenders); make_armor_3d(shJiangShiDress); make_armor_3d(shFemaleDress); make_armor_3d(shWightCloak, 2); make_armor_3d(shRaiderArmor); make_armor_3d(shRaiderShirt); make_armor_3d(shArmor); make_armor_3d(shRatCape2, 2); make_armor_3d(shHood, 2); DEBB(DF_POLY, ("feet and paws")); make_foot_3d(shHumanFoot); make_foot_3d(shYetiFoot); make_skeletal(shSkeletalFoot, WDIM == 2 ? zc(0.5) + human_height/40 - FLOOR : 0); hyperpoint front_leg = Hypc; hyperpoint rear_leg = Hypc; for(int i=shDogFrontPaw.s; i<shDogFrontPaw.e; i++) front_leg += hpc[i]; for(int i=shDogRearPaw.s; i<shDogRearPaw.e; i++) rear_leg += hpc[i]; front_leg = normalize(front_leg); rear_leg = normalize(rear_leg); front_leg_move = zpush(zc(0.4)) * rgpushxto0(front_leg); front_leg_move_inverse = inverse(front_leg_move); rear_leg_move = zpush(zc(0.4)) * rgpushxto0(rear_leg); rear_leg_move_inverse = inverse(rear_leg_move); leg_length = zc(0.4) - zc(0); make_paw_3d(shWolfFrontPaw, shWolfFrontLeg); make_paw_3d(shWolfRearPaw, shWolfRearLeg); make_paw_3d(shDogFrontPaw, shDogFrontLeg); make_paw_3d(shDogRearPaw, shDogRearLeg); DEBB(DF_POLY, ("revolution")); // make_abody_3d(shWolfBody, 0.01); // make_ahead_3d(shWolfHead); // make_ahead_3d(shFamiliarHead); ld g = WDIM == 2 ? ABODY - zc(0.4) : 0; make_revolution_cut(shWolfBody, 30, g, 0.01*S); make_revolution_cut(shWolfHead, 180, AHEAD - ABODY +g); make_revolution_cut(shRatHead, 180, AHEAD - ABODY +g, 0.04*scalefactor); make_revolution_cut(shRatCape1, 180, AHEAD - ABODY +g); make_revolution_cut(shFamiliarHead, 30, AHEAD - ABODY +g); // make_abody_3d(shDogTorso, 0.01); make_revolution_cut(shDogTorso, 30, +g); make_revolution_cut(shDogHead, 180, AHEAD - ABODY +g); // make_ahead_3d(shDogHead); // make_abody_3d(shCatBody, 0.05); // make_ahead_3d(shCatHead); make_revolution_cut(shCatBody, 30, +g); make_revolution_cut(shCatHead, 180, AHEAD - ABODY +g, 0.055 * scalefactor); make_paw_3d(shReptileFrontFoot, shReptileFrontLeg); make_paw_3d(shReptileRearFoot, shReptileRearLeg); make_abody_3d(shReptileBody, -1); // make_ahead_3d(shReptileHead); make_revolution_cut(shReptileHead, 180, AHEAD - ABODY+g); make_paw_3d(shBullFrontHoof, shBullFrontHoof); make_paw_3d(shBullRearHoof, shBullRearHoof); // make_abody_3d(shBullBody, 0.05); // make_ahead_3d(shBullHead); // make_ahead_3d(shBullHorn); make_revolution_cut(shBullBody, 180, +g); make_revolution_cut(shBullHead, 60, AHEAD - ABODY +g); shift_shape(shBullHorn, -g-(AHEAD - ABODY)); // make_revolution_cut(shBullHorn, 180, AHEAD - ABODY); make_paw_3d(shTrylobiteFrontClaw, shTrylobiteFrontLeg); make_paw_3d(shTrylobiteRearClaw, shTrylobiteRearLeg); make_abody_3d(shTrylobiteBody, 0); // make_ahead_3d(shTrylobiteHead); make_revolution_cut(shTrylobiteHead, 180, AHEAD - ABODY +g); make_revolution_cut(shShark, 180, WDIM == 2 ? -FLOOR : 0); make_revolution_cut(shGhost, 60, GHOST + g); make_revolution_cut(shEagle, 180, 0, 0.05*S); make_revolution_cut(shHawk, 180, 0, 0.05*S); make_revolution_cut(shTinyBird, 180, 0, 0.025 * S); make_revolution_cut(shTinyShark, 90); make_revolution_cut(shMiniGhost, 60); make_revolution_cut(shGargoyleWings, 180, 0, 0.05*S); make_revolution_cut(shGargoyleBody, 180, 0, 0.05*S); make_revolution_cut(shGadflyWing, 180, 0, 0.05*S); make_revolution_cut(shBatWings, 180, 0, 0.05*S); make_revolution_cut(shBatBody, 180, 0, 0.05*S); make_revolution_cut(shMouse, 180, -FLOOR); shift_shape(shMouseLegs, FLOOR - human_height / 200); make_revolution_cut(shJelly, 60); make_revolution(shFoxTail1); make_revolution(shFoxTail2); make_revolution(shGadflyBody, 180, 0); for(int i=0; i<8; i++) make_revolution(shAsteroid[i], 360); make_revolution_cut(shBugLeg, 60); make_revolution(shBugArmor, 180, ABODY); make_revolution_cut(shBugAntenna, 90, ABODY); make_revolution_cut(shButterflyBody, 180, 0); make_revolution_cut(shButterflyWing, 180, 0, 0.05*S); finishshape(); DEBB(DF_POLY, ("animatebirds")); animate_bird(shEagle, shAnimatedEagle, 0.05*S); animate_bird(shTinyBird, shAnimatedTinyEagle, 0.05*S/2); animate_bird(shButterflyWing, shAnimatedButterfly, 0); animate_bird(shGadflyWing, shAnimatedGadfly, 0); animate_bird(shHawk, shAnimatedHawk, 0.05*S); animate_bird(shGargoyleWings, shAnimatedGargoyle, 0.05*S); animate_bird(shGargoyleBody, shAnimatedGargoyle2, 0.05*S); animate_bird(shBatWings, shAnimatedBat, 0.05*S); animate_bird(shBatBody, shAnimatedBat2, 0.05*S); DEBB(DF_POLY, ("disablers")); disable(shWolfRearLeg); disable(shWolfFrontLeg); disable(shDogRearLeg); disable(shDogFrontLeg); disable(shReptileFrontLeg); disable(shReptileRearLeg); disable(shTrylobiteFrontLeg); disable(shTrylobiteRearLeg); disable(shPFace); disable(shJiangShi); make_revolution_cut(shDragonSegment, 60, g); make_revolution_cut(shDragonHead, 60, g); make_revolution_cut(shDragonTail, 60, g); make_revolution_cut(shWormSegment, 60, g); make_revolution_cut(shSmallWormSegment, 60, g); make_revolution_cut(shWormHead, 60, g); make_revolution_cut(shWormTail, 60, g); make_revolution_cut(shSmallWormTail, 60, g); make_revolution_cut(shTentHead, 60, g); make_revolution_cut(shKrakenHead, 60, -FLOOR); make_revolution_cut(shSeaTentacle, 60, -FLOOR); make_revolution_cut(shDragonLegs, 60, g); make_revolution_cut(shDragonWings, 60, g); disable(shDragonNostril); make_head_only(); DEBB(DF_POLY, ("balls")); make_ball(shDisk, orbsize*.2, 2); make_ball(shHeptaMarker, zhexf*.2, 1); make_ball(shSnowball, zhexf*.1, 0); if(euclid) { make_ball(shSun, 0.5, 2); make_euclidean_sky(); } else make_star(shSun, 3); make_star(shNightStar, 0.75); if(WDIM == 2) { for(int i=0; i<3; i++) { clone_shape(shHalfFloor[i], shHalfFloor[i+3]); shift_shape_orthogonally(shHalfFloor[i], FLOOR - human_height * .007); shift_shape_orthogonally(shHalfFloor[i+3], WALL + human_height * .007); } } shift_shape(shBoatOuter, FLOOR); shift_shape(shBoatInner, (FLOOR+LAKE)/2); for(int i=0; i<14; i++) shift_shape(shTriheptaSpecial[i], FLOOR); shift_shape_orthogonally(shBigCarpet1, FLOOR - human_height * 1/40); shift_shape_orthogonally(shBigCarpet2, FLOOR - human_height * 2/40); shift_shape_orthogonally(shBigCarpet3, FLOOR - human_height * 3/40); for(int a=0; a<5; a++) for(int b=0; b<4; b++) shift_shape(shReptile[a][b], FLOOR - human_height * min(b, 2) / 40); shift_shape(shMineMark[0], FLOOR - human_height * 1/40); shift_shape(shMineMark[1], FLOOR - human_height * 1/40); for(int a=0; a<5; a++) shift_shape(shZebra[a], FLOOR); auto tortz = [] (int t) { if(t == 0) return 2; else if(t < 8) return 3; else if(t == 13) return 2; else return 1; }; for(int t=0; t<13; t++) for(int u=0; u<4; u++) shift_shape(shTortoise[t][u], FLOOR - human_height * tortz(t) / 120); make_revolution_cut(shStatue, 60); shift_shape(shThorns, FLOOR - human_height * 1/40); clone_shape(shRose, shRoseItem); shift_shape(shRose, FLOOR - human_height * 1/20); DEBB(DF_POLY, ("slime")); bshape(shSlime, PPR::MONSTER_BODY); hyperpoint tip = xtangent(1); hyperpoint atip = xtangent(-1); ld z = 63.43 * degree; for(int i=0; i<5; i++) { auto a = cspin(1, 2, (72 * i ) * degree) * spin(z) * xtangent(1); auto b = cspin(1, 2, (72 * i-72) * degree) * spin(z) * xtangent(1); auto c = cspin(1, 2, (72 * i+36) * degree) * spin(M_PI-z) * xtangent(1); auto d = cspin(1, 2, (72 * i-36) * degree) * spin(M_PI-z) * xtangent(1); slimetriangle(tip, a, b, 1, 0); slimetriangle(a, b, c, 1, 0); slimetriangle(b, c, d, 1, 0); slimetriangle(c, d, atip, 1, 0); } last->flags |= POLY_TRIANGLES; add_texture(*last); if(WDIM == 2) shift_last_straight(FLOOR); finishshape(); shJelly = shSlime; shift_shape(shMagicSword, ABODY); shift_shape(shMagicShovel, ABODY); DEBB(DF_POLY, ("eyes")); adjust_eye(shSlimeEyes, shSlime, FLATEYE, 0, 2, 2); adjust_eye(shGhostEyes, shGhost, GHOST, GHOST, 2, WDIM == 2 ? 2 : 4); adjust_eye(shMiniEyes, shMiniGhost, GHOST, GHOST, 2, 2); adjust_eye(shWormEyes, shWormHead, 0, 0, 2, 4); adjust_eye(shDragonEyes, shDragonHead, 0, 0, 2, 4); adjust_eye(shKrakenEye, shKrakenHead, 0, 0, 1); adjust_eye(shKrakenEye2, shKrakenEye, 0, 0, 1, 2); shRatEye1 = shWolf1; shRatEye2 = shWolf2; shRatEye3 = shWolf3; adjust_eye(shWolf1, shDogHead, AHEAD, AHEAD, 1); adjust_eye(shWolf2, shDogHead, AHEAD, AHEAD, 1); for(int i=0; i<shWolf1.e-shWolf1.s; i++) hpc[shWolf2.s+i] = MirrorY * hpc[shWolf1.s+i]; adjust_eye(shWolf3, shDogHead, AHEAD, AHEAD, 1); adjust_eye(shFamiliarEye, shWolfHead, AHEAD, AHEAD, 1); adjust_eye(shRatEye1, shRatHead, AHEAD, AHEAD, 1); adjust_eye(shRatEye2, shRatHead, AHEAD, AHEAD, 1); for(int i=shRatEye3.s; i<shRatEye3.e; i++) hpc[i] = xpush(-scalefactor * 0.02) * hpc[i]; adjust_eye(shRatEye3, shRatHead, AHEAD, AHEAD, 1); adjust_eye(shWolfEyes, shWolfHead, AHEAD, AHEAD, 1); adjust_eye(shReptileEye, shReptileHead, AHEAD, AHEAD, 1); adjust_eye(shGadflyEye, shGadflyBody, 0, 0, 1); adjust_eye(shSkullEyes, shPHeadOnly, HEAD1, HEAD, 2, 2); shSkullEyes.tinf = NULL; adjust_eye(shMouseEyes, shMouse, FLOOR, FLOOR, 2, 1); shift_shape(shRatTail, zc(0.5) - LEG); for(int i=shRatTail.s; i<shRatTail.e; i++) hpc[i] = xpush(-scalefactor * 0.1) * hpc[i]; shift_shape(shSemiFloorShadow, FLOOR - human_height / 100); shift_shape(shSemiFloor[0], WALL); bshape(shPalaceGate, PPR::WALL); for(int i=0; i<4; i++) { ld x = -0.219482 + (0.135153 + 0.219482) * (i+.5) / 4; for(int j=0; j<12; j++) { hyperpoint left = xpush(x) * xspinpush0(j * 30 * degree, 0.02); hyperpoint right = xpush(x) * xspinpush0((j+1) * 30 * degree, 0.02); hpcpush(orthogonal_move(left, FLOOR)); hpcpush(orthogonal_move(right, FLOOR)); hpcpush(orthogonal_move(left, WALL)); hpcpush(orthogonal_move(right, FLOOR)); hpcpush(orthogonal_move(left, WALL)); hpcpush(orthogonal_move(right, WALL)); } } shPalaceGate.flags |= POLY_TRIANGLES; for(int i=0; i<3; i++) { array<hpcshape*,3> sh = make_array(&shBigCarpet1, &shBigCarpet2, &shBigCarpet3); bshape(*sh[i], PPR::GFLOORa); for(int t=0; t<S7; t++) { dynamicval<int> dv(vid.texture_step, 16); texture_order([&] (ld x, ld y) { ld z = 1-x-y; ld rad = 2.1 - i * 0.2; hyperpoint hx = ddi(t*12, -zhexf*rad) * C0; hyperpoint hy = ddi(t*12+12, -zhexf*rad) * C0; hyperpoint hz = C0; hyperpoint h = hx * x + hy * y + hz * z; h = mid(h, h); h = orthogonal_move(h, FLOOR - human_height * (i+2.5) / 100 * (x+y+1)/2); hpcpush(h); }); } sh[i]->flags |= POLY_TRIANGLES; } finishshape(); } #undef S #undef SH #undef revZ #endif }