mirror of
				https://github.com/zenorogue/hyperrogue.git
				synced 2025-10-31 14:02:59 +00:00 
			
		
		
		
	3d:: converting the old vector graphics into 3D models
This commit is contained in:
		
							
								
								
									
										793
									
								
								3d-models.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										793
									
								
								3d-models.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -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 | ||||||
|   | |||||||
							
								
								
									
										794
									
								
								earcut.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										794
									
								
								earcut.hpp
									
									
									
									
									
										Normal file
									
								
							| @@ -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 | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Zeno Rogue
					Zeno Rogue