3d:: converting the old vector graphics into 3D models
This commit is contained in:
parent
cb2e870a99
commit
596b4ce786
|
@ -0,0 +1,793 @@
|
||||||
|
// HyperRogue
|
||||||
|
// This file contains the routines to convert HyperRogue's old vector graphics into 3D models
|
||||||
|
|
||||||
|
// Copyright (C) 2011-2019 Zeno Rogue, see 'hyper.cpp' for details
|
||||||
|
|
||||||
|
#include "earcut.hpp"
|
||||||
|
|
||||||
|
namespace hr {
|
||||||
|
|
||||||
|
#define S (scalefactor / 0.805578)
|
||||||
|
#define SH (scalefactor / 0.805578 * geom3::height_width / 1.5)
|
||||||
|
|
||||||
|
hyperpoint shcenter;
|
||||||
|
|
||||||
|
vector<hyperpoint> get_shape(hpcshape sh) {
|
||||||
|
vector<hyperpoint> res;
|
||||||
|
for(int i=sh.s; i<sh.e-1; i++) res.push_back(hpc[i]);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
hyperpoint get_center(const vector<hyperpoint>& vh) {
|
||||||
|
hyperpoint h = Hypc;
|
||||||
|
using namespace hyperpoint_vec;
|
||||||
|
for(auto h1: vh) h = h + h1;
|
||||||
|
return normalize(h);
|
||||||
|
}
|
||||||
|
|
||||||
|
ld zc(ld z) { return geom3::human_height * (z - 0.5); }
|
||||||
|
|
||||||
|
transmatrix zpush(ld z) {
|
||||||
|
return cpush(2, z);
|
||||||
|
}
|
||||||
|
|
||||||
|
void 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 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 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 shift_last(ld z) {
|
||||||
|
for(int i=last->s; i<isize(hpc); i++) hpc[i] = cpush(2, z) * hpc[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
void shift_shape(hpcshape& sh, ld z) {
|
||||||
|
for(int i=sh.s; i<sh.e; i++) hpc[i] = cpush(2, z) * hpc[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
extern
|
||||||
|
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[3], shHalfMirror[3],
|
||||||
|
shGem[2], shStar, shDisk, shDiskT, shDiskS, shDiskM, shDiskSq, shRing,
|
||||||
|
shTinyBird, shTinyShark,
|
||||||
|
shEgg,
|
||||||
|
shSpikedRing, shTargetRing, shSawRing, shGearRing, shPeaceRing, shHeptaRing,
|
||||||
|
shSpearRing, shLoveRing,
|
||||||
|
shDaisy, shTriangle, shNecro, shStatue, shKey, shWindArrow,
|
||||||
|
shGun,
|
||||||
|
shFigurine, shTreat,
|
||||||
|
shElementalShard,
|
||||||
|
// shBranch,
|
||||||
|
shIBranch, shTentacle, shTentacleX, shILeaf[2],
|
||||||
|
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,
|
||||||
|
shDogStripes,
|
||||||
|
shPBody, shPSword, shPKnife,
|
||||||
|
shFerocityM, shFerocityF,
|
||||||
|
shHumanFoot, shHumanLeg, shHumanGroin, shHumanNeck, shSkeletalFoot, shYetiFoot,
|
||||||
|
shMagicSword, shMagicShovel, shSeaTentacle, shKrakenHead, shKrakenEye, shKrakenEye2,
|
||||||
|
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, shTentHead, shShark, shWormSegment, shSmallWormSegment, shWormTail, shSmallWormTail,
|
||||||
|
shMiniGhost, shMiniEyes,
|
||||||
|
shHedgehogBlade, shHedgehogBladePlayer,
|
||||||
|
shWolfBody, shWolfHead, shWolfLegs, shWolfEyes,
|
||||||
|
shWolfFrontLeg, shWolfRearLeg, shWolfFrontPaw, shWolfRearPaw,
|
||||||
|
shFemaleBody, shFemaleHair, shFemaleDress, shWitchDress,
|
||||||
|
shWitchHair, shBeautyHair, shFlowerHair, shFlowerHand, shSuspenders, shTrophy,
|
||||||
|
shBugBody, shBugArmor, shBugLeg, shBugAntenna,
|
||||||
|
shPickAxe, shPike, shFlailBall, shFlailTrunk, shFlailChain, shHammerHead,
|
||||||
|
shBook, shBookCover, shGrail,
|
||||||
|
shBoatOuter, shBoatInner, shCompass1, shCompass2, shCompass3,
|
||||||
|
shKnife, shTongue, shFlailMissile, shTrapArrow,
|
||||||
|
shPirateHook, shPirateHood, shEyepatch, shPirateX,
|
||||||
|
// shScratch,
|
||||||
|
shHeptaMarker, shSnowball,
|
||||||
|
shSkeletonBody, shSkull, shSkullEyes, shFatBody, shWaterElemental,
|
||||||
|
shPalaceGate, shFishTail,
|
||||||
|
shMouse, shMouseLegs, shMouseEyes,
|
||||||
|
shPrincessDress, shPrinceDress,
|
||||||
|
shWizardCape1, shWizardCape2,
|
||||||
|
shBigCarpet1, shBigCarpet2, shBigCarpet3,
|
||||||
|
shGoatHead, shRose, shThorns,
|
||||||
|
shRatHead, shRatTail, shRatEyes, shRatCape1, shRatCape2,
|
||||||
|
shWizardHat1, shWizardHat2,
|
||||||
|
shTortoise[13][6],
|
||||||
|
shDragonLegs, shDragonTail, shDragonHead, shDragonSegment, shDragonNostril,
|
||||||
|
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,
|
||||||
|
|
||||||
|
shButterflyBody, shButterflyWing, shGadflyBody, shGadflyWing, shGadflyEye,
|
||||||
|
|
||||||
|
shTerraArmor1, shTerraArmor2, shTerraArmor3, shTerraHead, shTerraFace,
|
||||||
|
shJiangShi, shJiangShiDress, shJiangShiCap1, shJiangShiCap2,
|
||||||
|
|
||||||
|
shAsymmetric,
|
||||||
|
|
||||||
|
shPBodyOnly, shPBodyArm, shPBodyHand, shPHeadOnly,
|
||||||
|
|
||||||
|
shDodeca;
|
||||||
|
|
||||||
|
|
||||||
|
extern renderbuffer *floor_textures;
|
||||||
|
|
||||||
|
basic_textureinfo models_texture;
|
||||||
|
|
||||||
|
void 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];
|
||||||
|
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;
|
||||||
|
using namespace hyperpoint_vec;
|
||||||
|
for(hyperpoint h: vh) res.push_back(normalize(h * s + shcenter * (1-s)));
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
void 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 = geom3::human_height;
|
||||||
|
|
||||||
|
if(isize(arm) > 3) {
|
||||||
|
shcenter = get_center(arm);
|
||||||
|
int arm0 = isize(hpc);
|
||||||
|
add_prism_sync(geom3::BODY - h*.03, arm, geom3::BODY + h*.03, arm);
|
||||||
|
add_cone(geom3::BODY + h*.03, arm, geom3::BODY + h*.05);
|
||||||
|
add_cone(geom3::BODY - h*.03, arm, geom3::BODY - h*.05);
|
||||||
|
int arm1 = isize(hpc);
|
||||||
|
for(int i=arm0; i<arm1; i++) {
|
||||||
|
hyperpoint h = hpc[i];
|
||||||
|
ld zl = asinh(h[2]);
|
||||||
|
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) - geom3::BODY;
|
||||||
|
hpc[i] = zpush(rad) * hpc[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 0.2804 - keep
|
||||||
|
// 0.1124 - move
|
||||||
|
|
||||||
|
if(isize(hand) > 3) {
|
||||||
|
shcenter = get_center(hand);
|
||||||
|
add_cone(geom3::BODY, hand, geom3::BODY + 0.05 * geom3::human_height);
|
||||||
|
add_cone(geom3::BODY, hand, geom3::BODY - 0.05 * geom3::human_height);
|
||||||
|
}
|
||||||
|
|
||||||
|
int at1 = isize(hpc);
|
||||||
|
for(int i=at0; i<at1; i++) hpc.push_back(Mirror * hpc[i]);
|
||||||
|
|
||||||
|
add_texture(shPBody);
|
||||||
|
shift_last(-geom3::BODY);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
void make_humanoid_3d(hpcshape& sh) { make_ha_3d(sh, false, 0.90); }
|
||||||
|
void make_armor_3d(hpcshape& sh, ld scale = 1) { make_ha_3d(sh, true, scale); }
|
||||||
|
*/
|
||||||
|
|
||||||
|
void make_humanoid_3d(hpcshape& sh) { make_ha_3d(sh, false, 1); }
|
||||||
|
|
||||||
|
void 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) {
|
||||||
|
hyperpoint ht[3];
|
||||||
|
ld hsh[3];
|
||||||
|
ld shi[3];
|
||||||
|
bool ok = true;
|
||||||
|
for(int s=0; s<3; s++) {
|
||||||
|
hs[s] = normalize(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 -= (abs(h[1])/S - 0.14) * SH;
|
||||||
|
if(abs(h[0]) > 0.08*S) ok = false, zz -= (abs(h[0])/S - 0.08) * (abs(h[0])/S - 0.08) * 25 * SH;
|
||||||
|
hpcpush(ht[s] = zpush(zz) * 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;
|
||||||
|
shi[s] *= geom3::human_height;
|
||||||
|
}
|
||||||
|
if(ok && kind == 1) for(int i=0; i<3; i++) {
|
||||||
|
int j = (i+1) % 3;
|
||||||
|
hpcpush(ht[i]);
|
||||||
|
hpcpush(ht[j]);
|
||||||
|
hpcpush(zpush(shi[i]) * ht[i]);
|
||||||
|
hpcpush(ht[j]);
|
||||||
|
hpcpush(zpush(shi[i]) * ht[i]);
|
||||||
|
hpcpush(zpush(shi[i]) * ht[j]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
for(int s=0; s<3; s++) {
|
||||||
|
hyperpoint h = hs[s];
|
||||||
|
ld zz = zc(0.925);
|
||||||
|
if(h[0] < -0.05*S) zz += (h[0]/S + 0.05) * SH;
|
||||||
|
if(hdist0(h) <= 0.0501*S) {
|
||||||
|
zz += sqrt(0.0026 - pow(hdist0(h)/S, 2)) * SH;
|
||||||
|
}
|
||||||
|
hpcpush(zpush(zz) * h);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void make_armor_3d(hpcshape& sh, int kind = 1) {
|
||||||
|
|
||||||
|
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[3];
|
||||||
|
p[1] = h[1] / h[3];
|
||||||
|
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(-geom3::HEAD);
|
||||||
|
else
|
||||||
|
shift_last(-geom3::BODY);
|
||||||
|
}
|
||||||
|
|
||||||
|
void 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(-geom3::LEG0);
|
||||||
|
for(int i=last->s; i<isize(hpc); i++) hpc[i] = cpush(0, -0.0125*S) * hpc[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
void make_head_only() {
|
||||||
|
|
||||||
|
auto addpt = [] (int d, int u) {
|
||||||
|
hpcpush(zpush(zc(0.925) + 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+=30)
|
||||||
|
for(int u=-90; u<=90; u+=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(-geom3::HEAD - 0.01 * SH);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void 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[3];
|
||||||
|
p[1] = h[1] / h[3];
|
||||||
|
pts[0].emplace_back(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
array<ld, 2> zero = {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(-geom3::HEAD);
|
||||||
|
}
|
||||||
|
|
||||||
|
void 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);
|
||||||
|
shift_last(-geom3::LEG0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
void 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 make_skeletal(hpcshape& sh, ld push = 0) {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
void make_revolution(hpcshape& sh, int mx = 180, ld push = 0) {
|
||||||
|
auto body = get_shape(sh);
|
||||||
|
bshape(sh, PPR::MONSTER_BODY);
|
||||||
|
int step = (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(cspin(1, 2, s * degree) * h0);
|
||||||
|
hpcpush(cspin(1, 2, s * degree) * h1);
|
||||||
|
hpcpush(cspin(1, 2, (s+step) * degree) * h0);
|
||||||
|
hpcpush(cspin(1, 2, s * degree) * h1);
|
||||||
|
hpcpush(cspin(1, 2, (s+step) * degree) * h0);
|
||||||
|
hpcpush(cspin(1, 2, (s+step) * degree) * h1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
last->flags |= POLY_TRIANGLES;
|
||||||
|
add_texture(sh);
|
||||||
|
shift_last(-push);
|
||||||
|
}
|
||||||
|
|
||||||
|
void make_revolution_cut(hpcshape &sh, int each = 180, ld push = 0, ld width = 99) {
|
||||||
|
auto body = get_shape(sh);
|
||||||
|
int n = isize(body) / 2;
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
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; 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++) {
|
||||||
|
using namespace hyperpoint_vec;
|
||||||
|
if(!stillin[i]) gbody[i] = normalize(gbody[lastid[i]] * (i - lastid[i]) + gbody[nextid[i]] * (nextid[i] - i));
|
||||||
|
}
|
||||||
|
|
||||||
|
bshape(sh, PPR::MONSTER_BODY);
|
||||||
|
int step = 10;
|
||||||
|
for(int i=0; i<n; i++) {
|
||||||
|
for(int s=0; s<360; s+=step) {
|
||||||
|
auto& tbody = (s % each ? gbody : body);
|
||||||
|
auto& nbody = ((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(cspin(1, 2, s * degree) * h0);
|
||||||
|
hpcpush(cspin(1, 2, s * degree) * h1);
|
||||||
|
hpcpush(cspin(1, 2, (s+step) * degree) * hs0);
|
||||||
|
hpcpush(cspin(1, 2, s * degree) * h1);
|
||||||
|
hpcpush(cspin(1, 2, (s+step) * degree) * hs0);
|
||||||
|
hpcpush(cspin(1, 2, (s+step) * degree) * hs1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
last->flags |= POLY_TRIANGLES;
|
||||||
|
add_texture(sh);
|
||||||
|
shift_last(-push);
|
||||||
|
}
|
||||||
|
|
||||||
|
void disable(hpcshape& sh) {
|
||||||
|
sh.s = sh.e = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void make_3d_models() {
|
||||||
|
if(DIM == 2) return;
|
||||||
|
shcenter = C0;
|
||||||
|
|
||||||
|
if(floor_textures) {
|
||||||
|
auto& utt = models_texture;
|
||||||
|
utt.tvertices.clear();
|
||||||
|
utt.texture_id = floor_textures->renderedTexture;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
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(shRatHead);
|
||||||
|
make_head_3d(shDemon);
|
||||||
|
make_head_3d(shGoatHead);
|
||||||
|
make_head_3d(shRatCape1);
|
||||||
|
make_head_3d(shJiangShiCap1);
|
||||||
|
make_head_3d(shJiangShiCap2);
|
||||||
|
make_head_3d(shTerraHead);
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
make_foot_3d(shHumanFoot);
|
||||||
|
make_foot_3d(shYetiFoot);
|
||||||
|
make_skeletal(shSkeletalFoot);
|
||||||
|
|
||||||
|
make_paw_3d(shWolfFrontPaw, shWolfFrontLeg);
|
||||||
|
make_paw_3d(shWolfRearPaw, shWolfRearLeg);
|
||||||
|
make_paw_3d(shDogFrontPaw, shDogFrontLeg);
|
||||||
|
make_paw_3d(shDogRearPaw, shDogRearLeg);
|
||||||
|
|
||||||
|
// make_abody_3d(shWolfBody, 0.01);
|
||||||
|
// make_ahead_3d(shWolfHead);
|
||||||
|
// make_ahead_3d(shFamiliarHead);
|
||||||
|
make_revolution_cut(shWolfBody, 30, 0, 0.01*S);
|
||||||
|
make_revolution_cut(shWolfHead, 180, geom3::AHEAD - geom3::ABODY);
|
||||||
|
make_revolution_cut(shFamiliarHead, 30, geom3::AHEAD - geom3::ABODY);
|
||||||
|
|
||||||
|
// make_abody_3d(shDogTorso, 0.01);
|
||||||
|
make_revolution_cut(shDogTorso, 30);
|
||||||
|
make_revolution_cut(shDogHead, 180, geom3::AHEAD - geom3::ABODY);
|
||||||
|
// make_ahead_3d(shDogHead);
|
||||||
|
|
||||||
|
// make_abody_3d(shCatBody, 0.05);
|
||||||
|
// make_ahead_3d(shCatHead);
|
||||||
|
make_revolution_cut(shCatBody, 30);
|
||||||
|
make_revolution_cut(shCatHead, 180, geom3::AHEAD - geom3::ABODY);
|
||||||
|
|
||||||
|
make_paw_3d(shReptileFrontFoot, shReptileFrontLeg);
|
||||||
|
make_paw_3d(shReptileRearFoot, shReptileRearLeg);
|
||||||
|
make_abody_3d(shReptileBody, -1);
|
||||||
|
// make_ahead_3d(shReptileHead);
|
||||||
|
make_revolution_cut(shReptileHead, 180, geom3::AHEAD - geom3::ABODY);
|
||||||
|
|
||||||
|
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);
|
||||||
|
make_revolution_cut(shBullHead, 60, geom3::AHEAD - geom3::ABODY);
|
||||||
|
shift_shape(shBullHorn, -(geom3::AHEAD - geom3::ABODY));
|
||||||
|
// make_revolution_cut(shBullHorn, 180, geom3::AHEAD - geom3::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, geom3::AHEAD - geom3::ABODY);
|
||||||
|
|
||||||
|
make_revolution_cut(shShark, 180);
|
||||||
|
|
||||||
|
make_revolution_cut(shGhost, 60);
|
||||||
|
make_revolution_cut(shSlime, 60);
|
||||||
|
|
||||||
|
make_revolution_cut(shEagle, 180, 0, 0.05*S);
|
||||||
|
make_revolution_cut(shHawk, 180, 0, 0.05*S);
|
||||||
|
|
||||||
|
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(shJelly, 60);
|
||||||
|
make_revolution(shFoxTail1);
|
||||||
|
make_revolution(shFoxTail2);
|
||||||
|
make_revolution(shGadflyBody);
|
||||||
|
for(int i=0; i<8; i++)
|
||||||
|
make_revolution(shAsteroid[i], 360);
|
||||||
|
|
||||||
|
make_revolution_cut(shBugLeg, 60, geom3::ALEG0);
|
||||||
|
|
||||||
|
make_revolution(shBugArmor, 180, geom3::ABODY);
|
||||||
|
make_revolution_cut(shBugAntenna, 90, geom3::ABODY);
|
||||||
|
|
||||||
|
make_revolution_cut(shButterflyBody, 180);
|
||||||
|
|
||||||
|
shift_shape(shWolf1, -0.088 * S);
|
||||||
|
shift_shape(shWolf2, -0.088 * S);
|
||||||
|
shift_shape(shWolf3, -0.098 * S);
|
||||||
|
shift_shape(shFamiliarEye, -0.088 * S);
|
||||||
|
shift_shape(shWolfEyes, (-0.088 - 0.01 * 0.9) * S);
|
||||||
|
|
||||||
|
shift_shape(shEyes, (-3.3) * S / -20);
|
||||||
|
for(int i=shEyes.s; i<shEyes.e; i++) hpc[i] = cspin(0, 2, M_PI/2) * hpc[i];
|
||||||
|
|
||||||
|
disable(shWolfRearLeg);
|
||||||
|
disable(shWolfFrontLeg);
|
||||||
|
disable(shDogRearLeg);
|
||||||
|
disable(shDogFrontLeg);
|
||||||
|
disable(shReptileFrontLeg);
|
||||||
|
disable(shReptileRearLeg);
|
||||||
|
disable(shTrylobiteFrontLeg);
|
||||||
|
disable(shTrylobiteRearLeg);
|
||||||
|
disable(shPFace);
|
||||||
|
disable(shJiangShi);
|
||||||
|
|
||||||
|
make_head_only();
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef S
|
||||||
|
#undef SH
|
||||||
|
|
||||||
|
}
|
|
@ -65,6 +65,7 @@ namespace hr { namespace inv { bool on, activating; } }
|
||||||
#include "geometry.cpp"
|
#include "geometry.cpp"
|
||||||
#include "geometry2.cpp"
|
#include "geometry2.cpp"
|
||||||
#include "polygons.cpp"
|
#include "polygons.cpp"
|
||||||
|
#include "3d-models.cpp"
|
||||||
#include "floorshapes.cpp"
|
#include "floorshapes.cpp"
|
||||||
#include "mapeditor.cpp"
|
#include "mapeditor.cpp"
|
||||||
#if CAP_MODEL
|
#if CAP_MODEL
|
||||||
|
|
|
@ -0,0 +1,794 @@
|
||||||
|
/*
|
||||||
|
ISC License
|
||||||
|
|
||||||
|
Copyright (c) 2015, Mapbox
|
||||||
|
|
||||||
|
Permission to use, copy, modify, and/or distribute this software for any purpose
|
||||||
|
with or without fee is hereby granted, provided that the above copyright notice
|
||||||
|
and this permission notice appear in all copies.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||||
|
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||||
|
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||||
|
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
|
||||||
|
OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
|
||||||
|
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
|
||||||
|
THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cassert>
|
||||||
|
#include <cmath>
|
||||||
|
#include <memory>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace mapbox {
|
||||||
|
|
||||||
|
namespace util {
|
||||||
|
|
||||||
|
template <std::size_t I, typename T> struct nth {
|
||||||
|
inline static typename std::tuple_element<I, T>::type
|
||||||
|
get(const T& t) { return std::get<I>(t); };
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
template <typename N = uint32_t>
|
||||||
|
class Earcut {
|
||||||
|
public:
|
||||||
|
std::vector<N> indices;
|
||||||
|
std::size_t vertices = 0;
|
||||||
|
|
||||||
|
template <typename Polygon>
|
||||||
|
void operator()(const Polygon& points);
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct Node {
|
||||||
|
Node(N index, double x_, double y_) : i(index), x(x_), y(y_) {}
|
||||||
|
Node(const Node&) = delete;
|
||||||
|
Node& operator=(const Node&) = delete;
|
||||||
|
Node(Node&&) = delete;
|
||||||
|
Node& operator=(Node&&) = delete;
|
||||||
|
|
||||||
|
const N i;
|
||||||
|
const double x;
|
||||||
|
const double y;
|
||||||
|
|
||||||
|
// previous and next vertice nodes in a polygon ring
|
||||||
|
Node* prev = nullptr;
|
||||||
|
Node* next = nullptr;
|
||||||
|
|
||||||
|
// z-order curve value
|
||||||
|
int32_t z = 0;
|
||||||
|
|
||||||
|
// previous and next nodes in z-order
|
||||||
|
Node* prevZ = nullptr;
|
||||||
|
Node* nextZ = nullptr;
|
||||||
|
|
||||||
|
// indicates whether this is a steiner point
|
||||||
|
bool steiner = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Ring> Node* linkedList(const Ring& points, const bool clockwise);
|
||||||
|
Node* filterPoints(Node* start, Node* end = nullptr);
|
||||||
|
void earcutLinked(Node* ear, int pass = 0);
|
||||||
|
bool isEar(Node* ear);
|
||||||
|
bool isEarHashed(Node* ear);
|
||||||
|
Node* cureLocalIntersections(Node* start);
|
||||||
|
void splitEarcut(Node* start);
|
||||||
|
template <typename Polygon> Node* eliminateHoles(const Polygon& points, Node* outerNode);
|
||||||
|
void eliminateHole(Node* hole, Node* outerNode);
|
||||||
|
Node* findHoleBridge(Node* hole, Node* outerNode);
|
||||||
|
void indexCurve(Node* start);
|
||||||
|
Node* sortLinked(Node* list);
|
||||||
|
int32_t zOrder(const double x_, const double y_);
|
||||||
|
Node* getLeftmost(Node* start);
|
||||||
|
bool pointInTriangle(double ax, double ay, double bx, double by, double cx, double cy, double px, double py) const;
|
||||||
|
bool isValidDiagonal(Node* a, Node* b);
|
||||||
|
double area(const Node* p, const Node* q, const Node* r) const;
|
||||||
|
bool equals(const Node* p1, const Node* p2);
|
||||||
|
bool intersects(const Node* p1, const Node* q1, const Node* p2, const Node* q2);
|
||||||
|
bool intersectsPolygon(const Node* a, const Node* b);
|
||||||
|
bool locallyInside(const Node* a, const Node* b);
|
||||||
|
bool middleInside(const Node* a, const Node* b);
|
||||||
|
Node* splitPolygon(Node* a, Node* b);
|
||||||
|
template <typename Point> Node* insertNode(std::size_t i, const Point& p, Node* last);
|
||||||
|
void removeNode(Node* p);
|
||||||
|
|
||||||
|
bool hashing;
|
||||||
|
double minX, maxX;
|
||||||
|
double minY, maxY;
|
||||||
|
double inv_size = 0;
|
||||||
|
|
||||||
|
template <typename T, typename Alloc = std::allocator<T>>
|
||||||
|
class ObjectPool {
|
||||||
|
public:
|
||||||
|
ObjectPool() { }
|
||||||
|
ObjectPool(std::size_t blockSize_) {
|
||||||
|
reset(blockSize_);
|
||||||
|
}
|
||||||
|
~ObjectPool() {
|
||||||
|
clear();
|
||||||
|
}
|
||||||
|
template <typename... Args>
|
||||||
|
T* construct(Args&&... args) {
|
||||||
|
if (currentIndex >= blockSize) {
|
||||||
|
currentBlock = alloc_traits::allocate(alloc, blockSize);
|
||||||
|
allocations.emplace_back(currentBlock);
|
||||||
|
currentIndex = 0;
|
||||||
|
}
|
||||||
|
T* object = ¤tBlock[currentIndex++];
|
||||||
|
alloc_traits::construct(alloc, object, std::forward<Args>(args)...);
|
||||||
|
return object;
|
||||||
|
}
|
||||||
|
void reset(std::size_t newBlockSize) {
|
||||||
|
for (auto allocation : allocations) {
|
||||||
|
alloc_traits::deallocate(alloc, allocation, blockSize);
|
||||||
|
}
|
||||||
|
allocations.clear();
|
||||||
|
blockSize = std::max<std::size_t>(1, newBlockSize);
|
||||||
|
currentBlock = nullptr;
|
||||||
|
currentIndex = blockSize;
|
||||||
|
}
|
||||||
|
void clear() { reset(blockSize); }
|
||||||
|
private:
|
||||||
|
T* currentBlock = nullptr;
|
||||||
|
std::size_t currentIndex = 1;
|
||||||
|
std::size_t blockSize = 1;
|
||||||
|
std::vector<T*> allocations;
|
||||||
|
Alloc alloc;
|
||||||
|
typedef typename std::allocator_traits<Alloc> alloc_traits;
|
||||||
|
};
|
||||||
|
ObjectPool<Node> nodes;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename N> template <typename Polygon>
|
||||||
|
void Earcut<N>::operator()(const Polygon& points) {
|
||||||
|
// reset
|
||||||
|
indices.clear();
|
||||||
|
vertices = 0;
|
||||||
|
|
||||||
|
if (points.empty()) return;
|
||||||
|
|
||||||
|
double x;
|
||||||
|
double y;
|
||||||
|
int threshold = 80;
|
||||||
|
std::size_t len = 0;
|
||||||
|
|
||||||
|
for (size_t i = 0; threshold >= 0 && i < points.size(); i++) {
|
||||||
|
threshold -= static_cast<int>(points[i].size());
|
||||||
|
len += points[i].size();
|
||||||
|
}
|
||||||
|
|
||||||
|
//estimate size of nodes and indices
|
||||||
|
nodes.reset(len * 3 / 2);
|
||||||
|
indices.reserve(len + points[0].size());
|
||||||
|
|
||||||
|
Node* outerNode = linkedList(points[0], true);
|
||||||
|
if (!outerNode || outerNode->prev == outerNode->next) return;
|
||||||
|
|
||||||
|
if (points.size() > 1) outerNode = eliminateHoles(points, outerNode);
|
||||||
|
|
||||||
|
// if the shape is not too simple, we'll use z-order curve hash later; calculate polygon bbox
|
||||||
|
hashing = threshold < 0;
|
||||||
|
if (hashing) {
|
||||||
|
Node* p = outerNode->next;
|
||||||
|
minX = maxX = outerNode->x;
|
||||||
|
minY = maxY = outerNode->y;
|
||||||
|
do {
|
||||||
|
x = p->x;
|
||||||
|
y = p->y;
|
||||||
|
minX = std::min<double>(minX, x);
|
||||||
|
minY = std::min<double>(minY, y);
|
||||||
|
maxX = std::max<double>(maxX, x);
|
||||||
|
maxY = std::max<double>(maxY, y);
|
||||||
|
p = p->next;
|
||||||
|
} while (p != outerNode);
|
||||||
|
|
||||||
|
// minX, minY and size are later used to transform coords into integers for z-order calculation
|
||||||
|
inv_size = std::max<double>(maxX - minX, maxY - minY);
|
||||||
|
inv_size = inv_size != .0 ? (1. / inv_size) : .0;
|
||||||
|
}
|
||||||
|
|
||||||
|
earcutLinked(outerNode);
|
||||||
|
|
||||||
|
nodes.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
// create a circular doubly linked list from polygon points in the specified winding order
|
||||||
|
template <typename N> template <typename Ring>
|
||||||
|
typename Earcut<N>::Node*
|
||||||
|
Earcut<N>::linkedList(const Ring& points, const bool clockwise) {
|
||||||
|
using Point = typename Ring::value_type;
|
||||||
|
double sum = 0;
|
||||||
|
const std::size_t len = points.size();
|
||||||
|
std::size_t i, j;
|
||||||
|
Node* last = nullptr;
|
||||||
|
|
||||||
|
// calculate original winding order of a polygon ring
|
||||||
|
for (i = 0, j = len > 0 ? len - 1 : 0; i < len; j = i++) {
|
||||||
|
const auto& p1 = points[i];
|
||||||
|
const auto& p2 = points[j];
|
||||||
|
const double p20 = util::nth<0, Point>::get(p2);
|
||||||
|
const double p10 = util::nth<0, Point>::get(p1);
|
||||||
|
const double p11 = util::nth<1, Point>::get(p1);
|
||||||
|
const double p21 = util::nth<1, Point>::get(p2);
|
||||||
|
sum += (p20 - p10) * (p11 + p21);
|
||||||
|
}
|
||||||
|
|
||||||
|
// link points into circular doubly-linked list in the specified winding order
|
||||||
|
if (clockwise == (sum > 0)) {
|
||||||
|
for (i = 0; i < len; i++) last = insertNode(vertices + i, points[i], last);
|
||||||
|
} else {
|
||||||
|
for (i = len; i-- > 0;) last = insertNode(vertices + i, points[i], last);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (last && equals(last, last->next)) {
|
||||||
|
removeNode(last);
|
||||||
|
last = last->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
vertices += len;
|
||||||
|
|
||||||
|
return last;
|
||||||
|
}
|
||||||
|
|
||||||
|
// eliminate colinear or duplicate points
|
||||||
|
template <typename N>
|
||||||
|
typename Earcut<N>::Node*
|
||||||
|
Earcut<N>::filterPoints(Node* start, Node* end) {
|
||||||
|
if (!end) end = start;
|
||||||
|
|
||||||
|
Node* p = start;
|
||||||
|
bool again;
|
||||||
|
do {
|
||||||
|
again = false;
|
||||||
|
|
||||||
|
if (!p->steiner && (equals(p, p->next) || area(p->prev, p, p->next) == 0)) {
|
||||||
|
removeNode(p);
|
||||||
|
p = end = p->prev;
|
||||||
|
|
||||||
|
if (p == p->next) break;
|
||||||
|
again = true;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
p = p->next;
|
||||||
|
}
|
||||||
|
} while (again || p != end);
|
||||||
|
|
||||||
|
return end;
|
||||||
|
}
|
||||||
|
|
||||||
|
// main ear slicing loop which triangulates a polygon (given as a linked list)
|
||||||
|
template <typename N>
|
||||||
|
void Earcut<N>::earcutLinked(Node* ear, int pass) {
|
||||||
|
if (!ear) return;
|
||||||
|
|
||||||
|
// interlink polygon nodes in z-order
|
||||||
|
if (!pass && hashing) indexCurve(ear);
|
||||||
|
|
||||||
|
Node* stop = ear;
|
||||||
|
Node* prev;
|
||||||
|
Node* next;
|
||||||
|
|
||||||
|
int iterations = 0;
|
||||||
|
|
||||||
|
// iterate through ears, slicing them one by one
|
||||||
|
while (ear->prev != ear->next) {
|
||||||
|
iterations++;
|
||||||
|
prev = ear->prev;
|
||||||
|
next = ear->next;
|
||||||
|
|
||||||
|
if (hashing ? isEarHashed(ear) : isEar(ear)) {
|
||||||
|
// cut off the triangle
|
||||||
|
indices.emplace_back(prev->i);
|
||||||
|
indices.emplace_back(ear->i);
|
||||||
|
indices.emplace_back(next->i);
|
||||||
|
|
||||||
|
removeNode(ear);
|
||||||
|
|
||||||
|
// skipping the next vertice leads to less sliver triangles
|
||||||
|
ear = next->next;
|
||||||
|
stop = next->next;
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
ear = next;
|
||||||
|
|
||||||
|
// if we looped through the whole remaining polygon and can't find any more ears
|
||||||
|
if (ear == stop) {
|
||||||
|
// try filtering points and slicing again
|
||||||
|
if (!pass) earcutLinked(filterPoints(ear), 1);
|
||||||
|
|
||||||
|
// if this didn't work, try curing all small self-intersections locally
|
||||||
|
else if (pass == 1) {
|
||||||
|
ear = cureLocalIntersections(ear);
|
||||||
|
earcutLinked(ear, 2);
|
||||||
|
|
||||||
|
// as a last resort, try splitting the remaining polygon into two
|
||||||
|
} else if (pass == 2) splitEarcut(ear);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// check whether a polygon node forms a valid ear with adjacent nodes
|
||||||
|
template <typename N>
|
||||||
|
bool Earcut<N>::isEar(Node* ear) {
|
||||||
|
const Node* a = ear->prev;
|
||||||
|
const Node* b = ear;
|
||||||
|
const Node* c = ear->next;
|
||||||
|
|
||||||
|
if (area(a, b, c) >= 0) return false; // reflex, can't be an ear
|
||||||
|
|
||||||
|
// now make sure we don't have other points inside the potential ear
|
||||||
|
Node* p = ear->next->next;
|
||||||
|
|
||||||
|
while (p != ear->prev) {
|
||||||
|
if (pointInTriangle(a->x, a->y, b->x, b->y, c->x, c->y, p->x, p->y) &&
|
||||||
|
area(p->prev, p, p->next) >= 0) return false;
|
||||||
|
p = p->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename N>
|
||||||
|
bool Earcut<N>::isEarHashed(Node* ear) {
|
||||||
|
const Node* a = ear->prev;
|
||||||
|
const Node* b = ear;
|
||||||
|
const Node* c = ear->next;
|
||||||
|
|
||||||
|
if (area(a, b, c) >= 0) return false; // reflex, can't be an ear
|
||||||
|
|
||||||
|
// triangle bbox; min & max are calculated like this for speed
|
||||||
|
const double minTX = std::min<double>(a->x, std::min<double>(b->x, c->x));
|
||||||
|
const double minTY = std::min<double>(a->y, std::min<double>(b->y, c->y));
|
||||||
|
const double maxTX = std::max<double>(a->x, std::max<double>(b->x, c->x));
|
||||||
|
const double maxTY = std::max<double>(a->y, std::max<double>(b->y, c->y));
|
||||||
|
|
||||||
|
// z-order range for the current triangle bbox;
|
||||||
|
const int32_t minZ = zOrder(minTX, minTY);
|
||||||
|
const int32_t maxZ = zOrder(maxTX, maxTY);
|
||||||
|
|
||||||
|
// first look for points inside the triangle in increasing z-order
|
||||||
|
Node* p = ear->nextZ;
|
||||||
|
|
||||||
|
while (p && p->z <= maxZ) {
|
||||||
|
if (p != ear->prev && p != ear->next &&
|
||||||
|
pointInTriangle(a->x, a->y, b->x, b->y, c->x, c->y, p->x, p->y) &&
|
||||||
|
area(p->prev, p, p->next) >= 0) return false;
|
||||||
|
p = p->nextZ;
|
||||||
|
}
|
||||||
|
|
||||||
|
// then look for points in decreasing z-order
|
||||||
|
p = ear->prevZ;
|
||||||
|
|
||||||
|
while (p && p->z >= minZ) {
|
||||||
|
if (p != ear->prev && p != ear->next &&
|
||||||
|
pointInTriangle(a->x, a->y, b->x, b->y, c->x, c->y, p->x, p->y) &&
|
||||||
|
area(p->prev, p, p->next) >= 0) return false;
|
||||||
|
p = p->prevZ;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// go through all polygon nodes and cure small local self-intersections
|
||||||
|
template <typename N>
|
||||||
|
typename Earcut<N>::Node*
|
||||||
|
Earcut<N>::cureLocalIntersections(Node* start) {
|
||||||
|
Node* p = start;
|
||||||
|
do {
|
||||||
|
Node* a = p->prev;
|
||||||
|
Node* b = p->next->next;
|
||||||
|
|
||||||
|
// a self-intersection where edge (v[i-1],v[i]) intersects (v[i+1],v[i+2])
|
||||||
|
if (!equals(a, b) && intersects(a, p, p->next, b) && locallyInside(a, b) && locallyInside(b, a)) {
|
||||||
|
indices.emplace_back(a->i);
|
||||||
|
indices.emplace_back(p->i);
|
||||||
|
indices.emplace_back(b->i);
|
||||||
|
|
||||||
|
// remove two nodes involved
|
||||||
|
removeNode(p);
|
||||||
|
removeNode(p->next);
|
||||||
|
|
||||||
|
p = start = b;
|
||||||
|
}
|
||||||
|
p = p->next;
|
||||||
|
} while (p != start);
|
||||||
|
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
// try splitting polygon into two and triangulate them independently
|
||||||
|
template <typename N>
|
||||||
|
void Earcut<N>::splitEarcut(Node* start) {
|
||||||
|
// look for a valid diagonal that divides the polygon into two
|
||||||
|
Node* a = start;
|
||||||
|
do {
|
||||||
|
Node* b = a->next->next;
|
||||||
|
while (b != a->prev) {
|
||||||
|
if (a->i != b->i && isValidDiagonal(a, b)) {
|
||||||
|
// split the polygon in two by the diagonal
|
||||||
|
Node* c = splitPolygon(a, b);
|
||||||
|
|
||||||
|
// filter colinear points around the cuts
|
||||||
|
a = filterPoints(a, a->next);
|
||||||
|
c = filterPoints(c, c->next);
|
||||||
|
|
||||||
|
// run earcut on each half
|
||||||
|
earcutLinked(a);
|
||||||
|
earcutLinked(c);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
b = b->next;
|
||||||
|
}
|
||||||
|
a = a->next;
|
||||||
|
} while (a != start);
|
||||||
|
}
|
||||||
|
|
||||||
|
// link every hole into the outer loop, producing a single-ring polygon without holes
|
||||||
|
template <typename N> template <typename Polygon>
|
||||||
|
typename Earcut<N>::Node*
|
||||||
|
Earcut<N>::eliminateHoles(const Polygon& points, Node* outerNode) {
|
||||||
|
const size_t len = points.size();
|
||||||
|
|
||||||
|
std::vector<Node*> queue;
|
||||||
|
for (size_t i = 1; i < len; i++) {
|
||||||
|
Node* list = linkedList(points[i], false);
|
||||||
|
if (list) {
|
||||||
|
if (list == list->next) list->steiner = true;
|
||||||
|
queue.push_back(getLeftmost(list));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
std::sort(queue.begin(), queue.end(), [](const Node* a, const Node* b) {
|
||||||
|
return a->x < b->x;
|
||||||
|
});
|
||||||
|
|
||||||
|
// process holes from left to right
|
||||||
|
for (size_t i = 0; i < queue.size(); i++) {
|
||||||
|
eliminateHole(queue[i], outerNode);
|
||||||
|
outerNode = filterPoints(outerNode, outerNode->next);
|
||||||
|
}
|
||||||
|
|
||||||
|
return outerNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
// find a bridge between vertices that connects hole with an outer ring and and link it
|
||||||
|
template <typename N>
|
||||||
|
void Earcut<N>::eliminateHole(Node* hole, Node* outerNode) {
|
||||||
|
outerNode = findHoleBridge(hole, outerNode);
|
||||||
|
if (outerNode) {
|
||||||
|
Node* b = splitPolygon(outerNode, hole);
|
||||||
|
filterPoints(b, b->next);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// David Eberly's algorithm for finding a bridge between hole and outer polygon
|
||||||
|
template <typename N>
|
||||||
|
typename Earcut<N>::Node*
|
||||||
|
Earcut<N>::findHoleBridge(Node* hole, Node* outerNode) {
|
||||||
|
Node* p = outerNode;
|
||||||
|
double hx = hole->x;
|
||||||
|
double hy = hole->y;
|
||||||
|
double qx = -std::numeric_limits<double>::infinity();
|
||||||
|
Node* m = nullptr;
|
||||||
|
|
||||||
|
// find a segment intersected by a ray from the hole's leftmost Vertex to the left;
|
||||||
|
// segment's endpoint with lesser x will be potential connection Vertex
|
||||||
|
do {
|
||||||
|
if (hy <= p->y && hy >= p->next->y && p->next->y != p->y) {
|
||||||
|
double x = p->x + (hy - p->y) * (p->next->x - p->x) / (p->next->y - p->y);
|
||||||
|
if (x <= hx && x > qx) {
|
||||||
|
qx = x;
|
||||||
|
if (x == hx) {
|
||||||
|
if (hy == p->y) return p;
|
||||||
|
if (hy == p->next->y) return p->next;
|
||||||
|
}
|
||||||
|
m = p->x < p->next->x ? p : p->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
p = p->next;
|
||||||
|
} while (p != outerNode);
|
||||||
|
|
||||||
|
if (!m) return 0;
|
||||||
|
|
||||||
|
if (hx == qx) return m->prev;
|
||||||
|
|
||||||
|
// look for points inside the triangle of hole Vertex, segment intersection and endpoint;
|
||||||
|
// if there are no points found, we have a valid connection;
|
||||||
|
// otherwise choose the Vertex of the minimum angle with the ray as connection Vertex
|
||||||
|
|
||||||
|
const Node* stop = m;
|
||||||
|
double tanMin = std::numeric_limits<double>::infinity();
|
||||||
|
double tanCur = 0;
|
||||||
|
|
||||||
|
p = m->next;
|
||||||
|
double mx = m->x;
|
||||||
|
double my = m->y;
|
||||||
|
|
||||||
|
while (p != stop) {
|
||||||
|
if (hx >= p->x && p->x >= mx && hx != p->x &&
|
||||||
|
pointInTriangle(hy < my ? hx : qx, hy, mx, my, hy < my ? qx : hx, hy, p->x, p->y)) {
|
||||||
|
|
||||||
|
tanCur = std::abs(hy - p->y) / (hx - p->x); // tangential
|
||||||
|
|
||||||
|
if ((tanCur < tanMin || (tanCur == tanMin && p->x > m->x)) && locallyInside(p, hole)) {
|
||||||
|
m = p;
|
||||||
|
tanMin = tanCur;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
p = p->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
|
||||||
|
// interlink polygon nodes in z-order
|
||||||
|
template <typename N>
|
||||||
|
void Earcut<N>::indexCurve(Node* start) {
|
||||||
|
assert(start);
|
||||||
|
Node* p = start;
|
||||||
|
|
||||||
|
do {
|
||||||
|
p->z = p->z ? p->z : zOrder(p->x, p->y);
|
||||||
|
p->prevZ = p->prev;
|
||||||
|
p->nextZ = p->next;
|
||||||
|
p = p->next;
|
||||||
|
} while (p != start);
|
||||||
|
|
||||||
|
p->prevZ->nextZ = nullptr;
|
||||||
|
p->prevZ = nullptr;
|
||||||
|
|
||||||
|
sortLinked(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Simon Tatham's linked list merge sort algorithm
|
||||||
|
// http://www.chiark.greenend.org.uk/~sgtatham/algorithms/listsort.html
|
||||||
|
template <typename N>
|
||||||
|
typename Earcut<N>::Node*
|
||||||
|
Earcut<N>::sortLinked(Node* list) {
|
||||||
|
assert(list);
|
||||||
|
Node* p;
|
||||||
|
Node* q;
|
||||||
|
Node* e;
|
||||||
|
Node* tail;
|
||||||
|
int i, numMerges, pSize, qSize;
|
||||||
|
int inSize = 1;
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
p = list;
|
||||||
|
list = nullptr;
|
||||||
|
tail = nullptr;
|
||||||
|
numMerges = 0;
|
||||||
|
|
||||||
|
while (p) {
|
||||||
|
numMerges++;
|
||||||
|
q = p;
|
||||||
|
pSize = 0;
|
||||||
|
for (i = 0; i < inSize; i++) {
|
||||||
|
pSize++;
|
||||||
|
q = q->nextZ;
|
||||||
|
if (!q) break;
|
||||||
|
}
|
||||||
|
|
||||||
|
qSize = inSize;
|
||||||
|
|
||||||
|
while (pSize > 0 || (qSize > 0 && q)) {
|
||||||
|
|
||||||
|
if (pSize == 0) {
|
||||||
|
e = q;
|
||||||
|
q = q->nextZ;
|
||||||
|
qSize--;
|
||||||
|
} else if (qSize == 0 || !q) {
|
||||||
|
e = p;
|
||||||
|
p = p->nextZ;
|
||||||
|
pSize--;
|
||||||
|
} else if (p->z <= q->z) {
|
||||||
|
e = p;
|
||||||
|
p = p->nextZ;
|
||||||
|
pSize--;
|
||||||
|
} else {
|
||||||
|
e = q;
|
||||||
|
q = q->nextZ;
|
||||||
|
qSize--;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tail) tail->nextZ = e;
|
||||||
|
else list = e;
|
||||||
|
|
||||||
|
e->prevZ = tail;
|
||||||
|
tail = e;
|
||||||
|
}
|
||||||
|
|
||||||
|
p = q;
|
||||||
|
}
|
||||||
|
|
||||||
|
tail->nextZ = nullptr;
|
||||||
|
|
||||||
|
if (numMerges <= 1) return list;
|
||||||
|
|
||||||
|
inSize *= 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// z-order of a Vertex given coords and size of the data bounding box
|
||||||
|
template <typename N>
|
||||||
|
int32_t Earcut<N>::zOrder(const double x_, const double y_) {
|
||||||
|
// coords are transformed into non-negative 15-bit integer range
|
||||||
|
int32_t x = static_cast<int32_t>(32767.0 * (x_ - minX) * inv_size);
|
||||||
|
int32_t y = static_cast<int32_t>(32767.0 * (y_ - minY) * inv_size);
|
||||||
|
|
||||||
|
x = (x | (x << 8)) & 0x00FF00FF;
|
||||||
|
x = (x | (x << 4)) & 0x0F0F0F0F;
|
||||||
|
x = (x | (x << 2)) & 0x33333333;
|
||||||
|
x = (x | (x << 1)) & 0x55555555;
|
||||||
|
|
||||||
|
y = (y | (y << 8)) & 0x00FF00FF;
|
||||||
|
y = (y | (y << 4)) & 0x0F0F0F0F;
|
||||||
|
y = (y | (y << 2)) & 0x33333333;
|
||||||
|
y = (y | (y << 1)) & 0x55555555;
|
||||||
|
|
||||||
|
return x | (y << 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// find the leftmost node of a polygon ring
|
||||||
|
template <typename N>
|
||||||
|
typename Earcut<N>::Node*
|
||||||
|
Earcut<N>::getLeftmost(Node* start) {
|
||||||
|
Node* p = start;
|
||||||
|
Node* leftmost = start;
|
||||||
|
do {
|
||||||
|
if (p->x < leftmost->x || (p->x == leftmost->x && p->y < leftmost->y))
|
||||||
|
leftmost = p;
|
||||||
|
p = p->next;
|
||||||
|
} while (p != start);
|
||||||
|
|
||||||
|
return leftmost;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if a point lies within a convex triangle
|
||||||
|
template <typename N>
|
||||||
|
bool Earcut<N>::pointInTriangle(double ax, double ay, double bx, double by, double cx, double cy, double px, double py) const {
|
||||||
|
return (cx - px) * (ay - py) - (ax - px) * (cy - py) >= 0 &&
|
||||||
|
(ax - px) * (by - py) - (bx - px) * (ay - py) >= 0 &&
|
||||||
|
(bx - px) * (cy - py) - (cx - px) * (by - py) >= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if a diagonal between two polygon nodes is valid (lies in polygon interior)
|
||||||
|
template <typename N>
|
||||||
|
bool Earcut<N>::isValidDiagonal(Node* a, Node* b) {
|
||||||
|
return a->next->i != b->i && a->prev->i != b->i && !intersectsPolygon(a, b) &&
|
||||||
|
locallyInside(a, b) && locallyInside(b, a) && middleInside(a, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
// signed area of a triangle
|
||||||
|
template <typename N>
|
||||||
|
double Earcut<N>::area(const Node* p, const Node* q, const Node* r) const {
|
||||||
|
return (q->y - p->y) * (r->x - q->x) - (q->x - p->x) * (r->y - q->y);
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if two points are equal
|
||||||
|
template <typename N>
|
||||||
|
bool Earcut<N>::equals(const Node* p1, const Node* p2) {
|
||||||
|
return p1->x == p2->x && p1->y == p2->y;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if two segments intersect
|
||||||
|
template <typename N>
|
||||||
|
bool Earcut<N>::intersects(const Node* p1, const Node* q1, const Node* p2, const Node* q2) {
|
||||||
|
if ((equals(p1, q1) && equals(p2, q2)) ||
|
||||||
|
(equals(p1, q2) && equals(p2, q1))) return true;
|
||||||
|
return (area(p1, q1, p2) > 0) != (area(p1, q1, q2) > 0) &&
|
||||||
|
(area(p2, q2, p1) > 0) != (area(p2, q2, q1) > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if a polygon diagonal intersects any polygon segments
|
||||||
|
template <typename N>
|
||||||
|
bool Earcut<N>::intersectsPolygon(const Node* a, const Node* b) {
|
||||||
|
const Node* p = a;
|
||||||
|
do {
|
||||||
|
if (p->i != a->i && p->next->i != a->i && p->i != b->i && p->next->i != b->i &&
|
||||||
|
intersects(p, p->next, a, b)) return true;
|
||||||
|
p = p->next;
|
||||||
|
} while (p != a);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if a polygon diagonal is locally inside the polygon
|
||||||
|
template <typename N>
|
||||||
|
bool Earcut<N>::locallyInside(const Node* a, const Node* b) {
|
||||||
|
return area(a->prev, a, a->next) < 0 ?
|
||||||
|
area(a, b, a->next) >= 0 && area(a, a->prev, b) >= 0 :
|
||||||
|
area(a, b, a->prev) < 0 || area(a, a->next, b) < 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if the middle Vertex of a polygon diagonal is inside the polygon
|
||||||
|
template <typename N>
|
||||||
|
bool Earcut<N>::middleInside(const Node* a, const Node* b) {
|
||||||
|
const Node* p = a;
|
||||||
|
bool inside = false;
|
||||||
|
double px = (a->x + b->x) / 2;
|
||||||
|
double py = (a->y + b->y) / 2;
|
||||||
|
do {
|
||||||
|
if (((p->y > py) != (p->next->y > py)) && p->next->y != p->y &&
|
||||||
|
(px < (p->next->x - p->x) * (py - p->y) / (p->next->y - p->y) + p->x))
|
||||||
|
inside = !inside;
|
||||||
|
p = p->next;
|
||||||
|
} while (p != a);
|
||||||
|
|
||||||
|
return inside;
|
||||||
|
}
|
||||||
|
|
||||||
|
// link two polygon vertices with a bridge; if the vertices belong to the same ring, it splits
|
||||||
|
// polygon into two; if one belongs to the outer ring and another to a hole, it merges it into a
|
||||||
|
// single ring
|
||||||
|
template <typename N>
|
||||||
|
typename Earcut<N>::Node*
|
||||||
|
Earcut<N>::splitPolygon(Node* a, Node* b) {
|
||||||
|
Node* a2 = nodes.construct(a->i, a->x, a->y);
|
||||||
|
Node* b2 = nodes.construct(b->i, b->x, b->y);
|
||||||
|
Node* an = a->next;
|
||||||
|
Node* bp = b->prev;
|
||||||
|
|
||||||
|
a->next = b;
|
||||||
|
b->prev = a;
|
||||||
|
|
||||||
|
a2->next = an;
|
||||||
|
an->prev = a2;
|
||||||
|
|
||||||
|
b2->next = a2;
|
||||||
|
a2->prev = b2;
|
||||||
|
|
||||||
|
bp->next = b2;
|
||||||
|
b2->prev = bp;
|
||||||
|
|
||||||
|
return b2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// create a node and util::optionally link it with previous one (in a circular doubly linked list)
|
||||||
|
template <typename N> template <typename Point>
|
||||||
|
typename Earcut<N>::Node*
|
||||||
|
Earcut<N>::insertNode(std::size_t i, const Point& pt, Node* last) {
|
||||||
|
Node* p = nodes.construct(static_cast<N>(i), util::nth<0, Point>::get(pt), util::nth<1, Point>::get(pt));
|
||||||
|
|
||||||
|
if (!last) {
|
||||||
|
p->prev = p;
|
||||||
|
p->next = p;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
assert(last);
|
||||||
|
p->next = last->next;
|
||||||
|
p->prev = last;
|
||||||
|
last->next->prev = p;
|
||||||
|
last->next = p;
|
||||||
|
}
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename N>
|
||||||
|
void Earcut<N>::removeNode(Node* p) {
|
||||||
|
p->next->prev = p->prev;
|
||||||
|
p->prev->next = p->next;
|
||||||
|
|
||||||
|
if (p->prevZ) p->prevZ->nextZ = p->nextZ;
|
||||||
|
if (p->nextZ) p->nextZ->prevZ = p->prevZ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename N = uint32_t, typename Polygon>
|
||||||
|
std::vector<N> earcut(const Polygon& poly) {
|
||||||
|
mapbox::detail::Earcut<N> earcut;
|
||||||
|
earcut(poly);
|
||||||
|
return std::move(earcut.indices);
|
||||||
|
}
|
||||||
|
}
|
37
graph.cpp
37
graph.cpp
|
@ -580,43 +580,56 @@ transmatrix otherbodyparts(const transmatrix& V, color_t col, eMonster who, doub
|
||||||
|
|
||||||
// todo
|
// todo
|
||||||
|
|
||||||
if(detaillevel >= 2) {
|
if(detaillevel >= 2 && DIM == 2) {
|
||||||
transmatrix VL = mmscale(V, geom3::LEG1);
|
transmatrix VL = mmscale(V, geom3::LEG1);
|
||||||
queuepoly(VL * xpush(rightfoot*3/4), shHumanLeg, col);
|
queuepoly(VL * xpush(rightfoot*3/4), shHumanLeg, col);
|
||||||
queuepoly(VL * Mirror * xpush(-rightfoot*3/4), shHumanLeg, col);
|
queuepoly(VL * Mirror * xpush(-rightfoot*3/4), shHumanLeg, col);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(true) {
|
if(DIM == 2) {
|
||||||
transmatrix VL = mmscale(V, geom3::LEG);
|
transmatrix VL = mmscale(V, geom3::LEG);
|
||||||
queuepoly(VL * xpush(rightfoot/2), shHumanLeg, col);
|
queuepoly(VL * xpush(rightfoot/2), shHumanLeg, col);
|
||||||
queuepoly(VL * Mirror * xpush(-rightfoot/2), shHumanLeg, col);
|
queuepoly(VL * Mirror * xpush(-rightfoot/2), shHumanLeg, col);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(detaillevel >= 2) {
|
if(detaillevel >= 2 && DIM == 2) {
|
||||||
transmatrix VL = mmscale(V, geom3::LEG3);
|
transmatrix VL = mmscale(V, geom3::LEG3);
|
||||||
queuepoly(VL * xpush(rightfoot/4), shHumanLeg, col);
|
queuepoly(VL * xpush(rightfoot/4), shHumanLeg, col);
|
||||||
queuepoly(VL * Mirror * xpush(-rightfoot/4), shHumanLeg, col);
|
queuepoly(VL * Mirror * xpush(-rightfoot/4), shHumanLeg, col);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(who == moWaterElemental) {
|
transmatrix Tright, Tleft;
|
||||||
|
|
||||||
|
if(DIM == 2) {
|
||||||
|
Tright = VFOOT * xpush(rightfoot);
|
||||||
|
Tleft = VFOOT * Mirror * xpush(-rightfoot);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Tright = V * cspin(0, 2, rightfoot/SCALE * 3);
|
||||||
|
Tleft = V * Mirror * cspin(2, 0, rightfoot/SCALE * 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(who == moWaterElemental && DIM == 2) {
|
||||||
double fishtail = footfun(footphase / .4) / 4 * 1.5;
|
double fishtail = footfun(footphase / .4) / 4 * 1.5;
|
||||||
queuepoly(VFOOT * xpush(fishtail), shFishTail, watercolor(100));
|
queuepoly(VFOOT * xpush(fishtail), shFishTail, watercolor(100));
|
||||||
}
|
}
|
||||||
else if(who == moSkeleton) {
|
else if(who == moSkeleton) {
|
||||||
queuepoly(VFOOT * xpush(rightfoot), shSkeletalFoot, col);
|
queuepoly(Tright, shSkeletalFoot, col);
|
||||||
queuepoly(VFOOT * Mirror * xpush(-rightfoot), shSkeletalFoot, col);
|
queuepoly(Tleft, shSkeletalFoot, col);
|
||||||
return spin(rightfoot * wobble);
|
return spin(rightfoot * wobble);
|
||||||
}
|
}
|
||||||
else if(isTroll(who) || who == moMonkey || who == moYeti || who == moRatling || who == moRatlingAvenger || who == moGoblin) {
|
else if(isTroll(who) || who == moMonkey || who == moYeti || who == moRatling || who == moRatlingAvenger || who == moGoblin) {
|
||||||
queuepoly(VFOOT * xpush(rightfoot), shYetiFoot, col);
|
queuepoly(Tright, shYetiFoot, col);
|
||||||
queuepoly(VFOOT * Mirror * xpush(-rightfoot), shYetiFoot, col);
|
queuepoly(Tleft, shYetiFoot, col);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
queuepoly(VFOOT * xpush(rightfoot), shHumanFoot, col);
|
queuepoly(Tright, shHumanFoot, col);
|
||||||
queuepoly(VFOOT * Mirror * xpush(-rightfoot), shHumanFoot, col);
|
queuepoly(Tleft, shHumanFoot, col);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!mmspatial) return spin(rightfoot * wobble);
|
if(DIM == 3) queuepoly(VHEAD, shPHeadOnly, col);
|
||||||
|
|
||||||
|
if(DIM == 3 || !mmspatial) return spin(rightfoot * wobble);
|
||||||
|
|
||||||
if(detaillevel >= 2 && who != moZombie)
|
if(detaillevel >= 2 && who != moZombie)
|
||||||
queuepoly(mmscale(V, geom3::NECK1), shHumanNeck, col);
|
queuepoly(mmscale(V, geom3::NECK1), shHumanNeck, col);
|
||||||
|
@ -630,8 +643,6 @@ transmatrix otherbodyparts(const transmatrix& V, color_t col, eMonster who, doub
|
||||||
}
|
}
|
||||||
|
|
||||||
return spin(rightfoot * wobble);
|
return spin(rightfoot * wobble);
|
||||||
|
|
||||||
return Id;
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
16
polygons.cpp
16
polygons.cpp
|
@ -1714,6 +1714,8 @@ hpcshape
|
||||||
|
|
||||||
shAsymmetric,
|
shAsymmetric,
|
||||||
|
|
||||||
|
shPBodyOnly, shPBodyArm, shPBodyHand, shPHeadOnly,
|
||||||
|
|
||||||
shDodeca;
|
shDodeca;
|
||||||
|
|
||||||
vector<hpcshape> shPlainWall3D, shWireframe3D, shWall3D, shMiniWall3D;
|
vector<hpcshape> shPlainWall3D, shWireframe3D, shWall3D, shMiniWall3D;
|
||||||
|
@ -2683,6 +2685,8 @@ void configure_floorshapes() {
|
||||||
generate_floorshapes();
|
generate_floorshapes();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extern void make_3d_models();
|
||||||
|
|
||||||
void buildpolys() {
|
void buildpolys() {
|
||||||
|
|
||||||
symmetriesAt.clear();
|
symmetriesAt.clear();
|
||||||
|
@ -2923,6 +2927,11 @@ void buildpolys() {
|
||||||
bshape(shFemaleDress, PPR::MONSTER_ARMOR0, scalefactor, 97);
|
bshape(shFemaleDress, PPR::MONSTER_ARMOR0, scalefactor, 97);
|
||||||
bshape(shDemon, PPR::MONSTER_HAIR, scalefactor, 98);
|
bshape(shDemon, PPR::MONSTER_HAIR, scalefactor, 98);
|
||||||
|
|
||||||
|
bshape(shPBodyOnly, PPR::MONSTER_BODY, scalefactor, 389);
|
||||||
|
bshape(shPBodyArm, PPR::MONSTER_BODY, scalefactor, 390);
|
||||||
|
bshape(shPBodyHand, PPR::MONSTER_BODY, scalefactor, 391);
|
||||||
|
bshape(shPHeadOnly, PPR::MONSTER_HEAD, scalefactor, 392);
|
||||||
|
|
||||||
bshape(shTrylobite, PPR::MONSTER_BODY, scalefactor, 99);
|
bshape(shTrylobite, PPR::MONSTER_BODY, scalefactor, 99);
|
||||||
bshape(shTrylobiteHead, PPR::MONSTER_HEAD, scalefactor, 100);
|
bshape(shTrylobiteHead, PPR::MONSTER_HEAD, scalefactor, 100);
|
||||||
bshape(shTrylobiteBody, PPR::MONSTER_BODY, scalefactor, 308);
|
bshape(shTrylobiteBody, PPR::MONSTER_BODY, scalefactor, 308);
|
||||||
|
@ -3093,6 +3102,8 @@ void buildpolys() {
|
||||||
bshape(shBead1, PPR(20), 1, 251);
|
bshape(shBead1, PPR(20), 1, 251);
|
||||||
bshape(shArrow, PPR::ARROW, 1, 252);
|
bshape(shArrow, PPR::ARROW, 1, 252);
|
||||||
|
|
||||||
|
make_3d_models();
|
||||||
|
|
||||||
bshapeend();
|
bshapeend();
|
||||||
|
|
||||||
prehpc = isize(hpc);
|
prehpc = isize(hpc);
|
||||||
|
@ -4094,6 +4105,11 @@ NEWSHAPE, 386, 3, 1, 0.173768,0.275379, 0.340287,0.116342, 0.229291,-0.115277,
|
||||||
NEWSHAPE, 387, 7, 1, 0.315263,-0.310217, 0.085056,-0.287538,
|
NEWSHAPE, 387, 7, 1, 0.315263,-0.310217, 0.085056,-0.287538,
|
||||||
NEWSHAPE, 388, 1, 1, 0.046590,0.284199, 0.028110,0.325611, 0.098711,0.333738, 0.088761,0.294314, 0.090351,0.227036, 0.092387,0.196322, 0.129546,0.192006, 0.168982,0.166667, 0.173088,0.117700, 0.022882,0.091527, 0.004586,0.133004, 0.022981,0.160866, 0.052990,0.184313, 0.085413,0.193910, 0.055297,0.184324,
|
NEWSHAPE, 388, 1, 1, 0.046590,0.284199, 0.028110,0.325611, 0.098711,0.333738, 0.088761,0.294314, 0.090351,0.227036, 0.092387,0.196322, 0.129546,0.192006, 0.168982,0.166667, 0.173088,0.117700, 0.022882,0.091527, 0.004586,0.133004, 0.022981,0.160866, 0.052990,0.184313, 0.085413,0.193910, 0.055297,0.184324,
|
||||||
|
|
||||||
|
NEWSHAPE, 389, 1, 2, -0.127943,0.000000, -0.121732,0.008437, -0.120752,0.047093, -0.114785,0.065246, -0.096531,0.082051, -0.079664,0.100183, -0.087015,0.156872, -0.056388,0.171466, -0.021870,0.150662, -0.022997,0.136774, -0.004819,0.120485, 0.007204,0.104455, 0.016748,0.083741, 0.026225,0.054833, 0.033323,0.030943, 0.034483,0.001189, 0.034483,-0.001189,
|
||||||
|
NEWSHAPE, 390, 1, 1, -0.079664,0.100183, -0.087015,0.156872, -0.090442,0.188317, -0.085023,0.215058, -0.078296,0.241201, -0.070101,0.263835, -0.062700,0.273833, -0.053763,0.276497, -0.037212,0.273273, -0.026261,0.230095, -0.024880,0.217700, -0.022225,0.198787, -0.020850,0.180288, -0.021870,0.150662, -0.022997,0.136774, -0.036634,0.100744,
|
||||||
|
NEWSHAPE, 391, 1, 1, -0.063645,0.226806, -0.078296,0.241201, -0.070101,0.263835, -0.062700,0.273833, -0.053763,0.276497, -0.030638,0.274461, -0.015319,0.275737, 0.001277,0.277150, 0.020384,0.271369, 0.038101,0.262896, 0.045596,0.255842, 0.062388,0.263558, 0.085371,0.258660, 0.084235,0.228817, 0.071073,0.213220, 0.048603,0.218088, 0.042541,0.228972, 0.028749,0.228742, 0.011222,0.224439, -0.012498,0.229969, -0.026261,0.230095,
|
||||||
|
NEWSHAPE, 392, 1, 2, 0.060794,0.001192, 0.058426,0.023847, 0.050054,0.030986, 0.042896,0.038130, 0.044109,0.042917, 0.032180,0.050058, 0.017884,0.059612, 0.005963,0.064401, -0.009546,0.068015, -0.022689,0.070455, -0.053753,0.053753, -0.065710,0.040621, -0.074098,0.028683, -0.088611,0.020357, -0.087387,0.017956,
|
||||||
|
|
||||||
NEWSHAPE
|
NEWSHAPE
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
Loading…
Reference in New Issue